• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.applications.manageapplications;
18 
19 import static com.android.settings.applications.manageapplications.ManageApplications.ApplicationsAdapter;
20 import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_CLONED_APPS;
21 import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_NONE;
22 
23 import android.app.settings.SettingsEnums;
24 import android.content.Context;
25 import android.content.pm.ApplicationInfo;
26 import android.content.pm.PackageManager;
27 import android.graphics.drawable.Drawable;
28 import android.os.AsyncTask;
29 import android.text.TextUtils;
30 import android.util.Log;
31 import android.view.LayoutInflater;
32 import android.view.View;
33 import android.view.ViewGroup;
34 import android.widget.ImageView;
35 import android.widget.ProgressBar;
36 import android.widget.Switch;
37 import android.widget.TextView;
38 
39 import androidx.annotation.StringRes;
40 import androidx.annotation.VisibleForTesting;
41 import androidx.fragment.app.FragmentActivity;
42 import androidx.recyclerview.widget.RecyclerView;
43 
44 import com.android.settings.R;
45 import com.android.settings.overlay.FeatureFactory;
46 import com.android.settingslib.applications.ApplicationsState;
47 import com.android.settingslib.applications.ApplicationsState.AppEntry;
48 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
49 import com.android.settingslib.widget.LottieColorUtils;
50 
51 import com.airbnb.lottie.LottieAnimationView;
52 
53 
54 public class ApplicationViewHolder extends RecyclerView.ViewHolder {
55 
56     @VisibleForTesting
57     final TextView mAppName;
58     @VisibleForTesting
59     final TextView mSummary;
60     @VisibleForTesting
61     final TextView mDisabled;
62     @VisibleForTesting
63     final ViewGroup mWidgetContainer;
64     @VisibleForTesting
65     final Switch mSwitch;
66     final ImageView mAddIcon;
67     final ProgressBar mProgressBar;
68 
69     private final ImageView mAppIcon;
70 
ApplicationViewHolder(View itemView)71     ApplicationViewHolder(View itemView) {
72         super(itemView);
73         mAppName = itemView.findViewById(android.R.id.title);
74         mAppIcon = itemView.findViewById(android.R.id.icon);
75         mSummary = itemView.findViewById(android.R.id.summary);
76         mDisabled = itemView.findViewById(R.id.appendix);
77         mSwitch = itemView.findViewById(R.id.switchWidget);
78         mWidgetContainer = itemView.findViewById(android.R.id.widget_frame);
79         mAddIcon = itemView.findViewById(R.id.add_preference_widget);
80         mProgressBar = itemView.findViewById(R.id.progressBar_cyclic);
81     }
82 
newView(ViewGroup parent)83     static View newView(ViewGroup parent) {
84         return newView(parent, false /* twoTarget */, LIST_TYPE_NONE /* listType */);
85     }
86 
newView(ViewGroup parent, boolean twoTarget, int listType)87     static View newView(ViewGroup parent, boolean twoTarget, int listType) {
88         ViewGroup view = (ViewGroup) LayoutInflater.from(parent.getContext())
89                 .inflate(R.layout.preference_app, parent, false);
90         ViewGroup widgetFrame = view.findViewById(android.R.id.widget_frame);
91         if (twoTarget) {
92             if (widgetFrame != null) {
93                 if (listType == LIST_TYPE_CLONED_APPS) {
94                     LayoutInflater.from(parent.getContext())
95                             .inflate(R.layout.preference_widget_add_progressbar, widgetFrame, true);
96                 } else {
97                     LayoutInflater.from(parent.getContext())
98                             .inflate(R.layout.preference_widget_primary_switch, widgetFrame, true);
99                 }
100                 View divider = LayoutInflater.from(parent.getContext()).inflate(
101                         R.layout.preference_two_target_divider, view, false);
102                 // second to last, before widget frame
103                 view.addView(divider, view.getChildCount() - 1);
104             }
105         } else if (widgetFrame != null) {
106             widgetFrame.setVisibility(View.GONE);
107         }
108         return view;
109     }
110 
newHeader(ViewGroup parent, int resText)111     static View newHeader(ViewGroup parent, int resText) {
112         ViewGroup view = (ViewGroup) LayoutInflater.from(parent.getContext())
113                 .inflate(R.layout.preference_app_header, parent, false);
114         TextView textView = view.findViewById(R.id.apps_top_intro_text);
115         textView.setText(resText);
116         return view;
117     }
118 
newHeaderWithAnimation(Context context, ViewGroup parent, int resIntroText, int resAnimation, int resAppListText)119     static View newHeaderWithAnimation(Context context, ViewGroup parent, int resIntroText,
120             int resAnimation, int resAppListText) {
121         ViewGroup view = (ViewGroup) LayoutInflater.from(parent.getContext())
122                 .inflate(R.layout.preference_app_header_animation, parent, false);
123         TextView introText = view.findViewById(R.id.apps_top_intro_text);
124         introText.setText(resIntroText);
125 
126         LottieAnimationView illustrationLottie = view.findViewById(R.id.illustration_lottie);
127         illustrationLottie.setAnimation(resAnimation);
128         illustrationLottie.setVisibility(View.VISIBLE);
129         LottieColorUtils.applyDynamicColors(context, illustrationLottie);
130 
131         TextView appListText = view.findViewById(R.id.app_list_text);
132         appListText.setText(resAppListText);
133 
134         return view;
135     }
136 
setSummary(CharSequence summary)137     void setSummary(CharSequence summary) {
138         mSummary.setText(summary);
139         updateSummaryVisibility();
140     }
141 
setSummary(@tringRes int summary)142     void setSummary(@StringRes int summary) {
143         mSummary.setText(summary);
144         updateSummaryVisibility();
145     }
146 
updateSummaryVisibility()147     private void updateSummaryVisibility() {
148         // Hide an empty summary and then title will be vertically centered.
149         mSummary.setVisibility(TextUtils.isEmpty(mSummary.getText()) ? View.GONE : View.VISIBLE);
150     }
151 
setEnabled(boolean isEnabled)152     void setEnabled(boolean isEnabled) {
153         itemView.setEnabled(isEnabled);
154     }
155 
setTitle(CharSequence title, CharSequence contentDescription)156     void setTitle(CharSequence title, CharSequence contentDescription) {
157         if (title == null) {
158             return;
159         }
160         mAppName.setText(title);
161 
162         if (TextUtils.isEmpty(contentDescription)) {
163             return;
164         }
165         mAppName.setContentDescription(contentDescription);
166     }
167 
setIcon(int drawableRes)168     void setIcon(int drawableRes) {
169         mAppIcon.setImageResource(drawableRes);
170     }
171 
setIcon(Drawable icon)172     void setIcon(Drawable icon) {
173         if (icon == null) {
174             return;
175         }
176         mAppIcon.setImageDrawable(icon);
177     }
178 
updateDisableView(ApplicationInfo info)179     void updateDisableView(ApplicationInfo info) {
180         if ((info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
181             mDisabled.setVisibility(View.VISIBLE);
182             mDisabled.setText(R.string.not_installed);
183         } else if (!info.enabled || info.enabledSetting
184                 == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
185             mDisabled.setVisibility(View.VISIBLE);
186             mDisabled.setText(R.string.disabled);
187         } else {
188             mDisabled.setVisibility(View.GONE);
189         }
190     }
191 
updateSizeText(AppEntry entry, CharSequence invalidSizeStr, int whichSize)192     void updateSizeText(AppEntry entry, CharSequence invalidSizeStr, int whichSize) {
193         if (ManageApplications.DEBUG) {
194             Log.d(ManageApplications.TAG, "updateSizeText of "
195                     + entry.label + " " + entry + ": " + entry.sizeStr);
196         }
197         if (entry.sizeStr != null) {
198             switch (whichSize) {
199                 case ManageApplications.SIZE_INTERNAL:
200                     setSummary(entry.internalSizeStr);
201                     break;
202                 case ManageApplications.SIZE_EXTERNAL:
203                     setSummary(entry.externalSizeStr);
204                     break;
205                 default:
206                     setSummary(entry.sizeStr);
207                     break;
208             }
209         } else if (entry.size == ApplicationsState.SIZE_INVALID) {
210             setSummary(invalidSizeStr);
211         }
212     }
213 
updateSwitch(Switch.OnCheckedChangeListener listener, boolean enabled, boolean checked)214     void updateSwitch(Switch.OnCheckedChangeListener listener, boolean enabled, boolean checked) {
215         if (mSwitch != null && mWidgetContainer != null) {
216             mWidgetContainer.setFocusable(false);
217             mWidgetContainer.setClickable(false);
218             mSwitch.setFocusable(true);
219             mSwitch.setClickable(true);
220             mSwitch.setOnCheckedChangeListener(listener);
221             mSwitch.setChecked(checked);
222             mSwitch.setEnabled(enabled);
223         }
224     }
225 
updateAppCloneWidget(Context context, View.OnClickListener onClickListener, AppEntry entry)226     void updateAppCloneWidget(Context context, View.OnClickListener onClickListener,
227             AppEntry entry) {
228         if (mAddIcon != null) {
229             if (!entry.isCloned) {
230                 mAddIcon.setBackground(context.getDrawable(R.drawable.ic_add_24dp));
231             } else {
232                 mAddIcon.setBackground(context.getDrawable(R.drawable.ic_trash_can));
233                 setSummary(R.string.cloned_app_created_summary);
234             }
235             mAddIcon.setOnClickListener(onClickListener);
236         }
237     }
238 
appCloneOnClickListener(AppEntry entry, ApplicationsAdapter adapter, FragmentActivity manageApplicationsActivity)239     View.OnClickListener appCloneOnClickListener(AppEntry entry,
240             ApplicationsAdapter adapter, FragmentActivity manageApplicationsActivity) {
241         Context context = manageApplicationsActivity.getApplicationContext();
242         return new View.OnClickListener() {
243             @Override
244             public void onClick(View v) {
245                 CloneBackend cloneBackend = CloneBackend.getInstance(context);
246                 final MetricsFeatureProvider metricsFeatureProvider =
247                         FeatureFactory.getFactory(context).getMetricsFeatureProvider();
248 
249                 String packageName = entry.info.packageName;
250 
251                 if (mWidgetContainer != null) {
252                     if (!entry.isCloned) {
253                         metricsFeatureProvider.action(context,
254                                 SettingsEnums.ACTION_CREATE_CLONE_APP);
255                         mAddIcon.setVisibility(View.INVISIBLE);
256                         mProgressBar.setVisibility(View.VISIBLE);
257                         setSummary(R.string.cloned_app_creation_summary);
258 
259                         // todo(b/262352524): To figure out a way to prevent memory leak
260                         //  without making this static.
261                         new AsyncTask<Void, Void, Integer>(){
262 
263                             @Override
264                             protected Integer doInBackground(Void... unused) {
265                                 return cloneBackend.installCloneApp(packageName);
266                             }
267 
268                             @Override
269                             protected void onPostExecute(Integer res) {
270                                 mProgressBar.setVisibility(View.INVISIBLE);
271                                 mAddIcon.setVisibility(View.VISIBLE);
272 
273                                 if (res != CloneBackend.SUCCESS) {
274                                     setSummary(null);
275                                     return;
276                                 }
277 
278                                 // Refresh the page to reflect newly created cloned app.
279                                 adapter.rebuild();
280                             }
281                         }.execute();
282 
283                     } else if (entry.isCloned) {
284                         metricsFeatureProvider.action(context,
285                                 SettingsEnums.ACTION_DELETE_CLONE_APP);
286                         cloneBackend.uninstallClonedApp(packageName, /*allUsers*/ false,
287                                 manageApplicationsActivity);
288                     }
289                 }
290             }
291         };
292     }
293 }
294