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.CompoundButton; 35 import android.widget.ImageView; 36 import android.widget.ProgressBar; 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.spaprivileged.template.app.AppListItemModelKt; 50 import com.android.settingslib.spaprivileged.template.app.AppListPageKt; 51 import com.android.settingslib.widget.LottieColorUtils; 52 53 import com.airbnb.lottie.LottieAnimationView; 54 55 /** 56 * @deprecated Will be removed, use {@link AppListItemModelKt} {@link AppListPageKt} instead. 57 */ 58 @Deprecated(forRemoval = true) 59 public class ApplicationViewHolder extends RecyclerView.ViewHolder { 60 61 @VisibleForTesting 62 final TextView mAppName; 63 @VisibleForTesting 64 final TextView mSummary; 65 @VisibleForTesting 66 final TextView mDisabled; 67 @VisibleForTesting 68 final ViewGroup mWidgetContainer; 69 @VisibleForTesting 70 final CompoundButton mSwitch; 71 final ImageView mAddIcon; 72 final ProgressBar mProgressBar; 73 74 private final ImageView mAppIcon; 75 ApplicationViewHolder(View itemView)76 ApplicationViewHolder(View itemView) { 77 super(itemView); 78 mAppName = itemView.findViewById(android.R.id.title); 79 mAppIcon = itemView.findViewById(android.R.id.icon); 80 mSummary = itemView.findViewById(android.R.id.summary); 81 mDisabled = itemView.findViewById(com.android.settingslib.widget.preference.app.R.id.appendix); 82 mSwitch = itemView.findViewById(com.android.settingslib.R.id.switchWidget); 83 mWidgetContainer = itemView.findViewById(android.R.id.widget_frame); 84 mAddIcon = itemView.findViewById(R.id.add_preference_widget); 85 mProgressBar = itemView.findViewById(R.id.progressBar_cyclic); 86 } 87 newView(ViewGroup parent)88 static View newView(ViewGroup parent) { 89 return newView(parent, false /* twoTarget */, LIST_TYPE_NONE /* listType */); 90 } 91 newView(ViewGroup parent, boolean twoTarget, int listType)92 static View newView(ViewGroup parent, boolean twoTarget, int listType) { 93 ViewGroup view = (ViewGroup) LayoutInflater.from(parent.getContext()) 94 .inflate(com.android.settingslib.widget.preference.app.R.layout.preference_app, parent, false); 95 ViewGroup widgetFrame = view.findViewById(android.R.id.widget_frame); 96 if (twoTarget) { 97 if (widgetFrame != null) { 98 if (listType == LIST_TYPE_CLONED_APPS) { 99 LayoutInflater.from(parent.getContext()) 100 .inflate(R.layout.preference_widget_add_progressbar, widgetFrame, true); 101 } else { 102 LayoutInflater.from(parent.getContext()).inflate( 103 com.android.settingslib.R.layout.preference_widget_primary_switch, 104 widgetFrame, true); 105 } 106 View divider = LayoutInflater.from(parent.getContext()).inflate( 107 com.android.settingslib.widget.preference.twotarget.R.layout.preference_two_target_divider, 108 view, false); 109 // second to last, before widget frame 110 view.addView(divider, view.getChildCount() - 1); 111 } 112 } else if (widgetFrame != null) { 113 widgetFrame.setVisibility(View.GONE); 114 } 115 return view; 116 } 117 newHeader(ViewGroup parent, int resText)118 static View newHeader(ViewGroup parent, int resText) { 119 ViewGroup view = (ViewGroup) LayoutInflater.from(parent.getContext()) 120 .inflate(com.android.settingslib.widget.preference.app.R.layout.preference_app_header, 121 parent, false); 122 TextView textView = view.findViewById(R.id.apps_top_intro_text); 123 textView.setText(resText); 124 return view; 125 } 126 newHeaderWithAnimation(Context context, ViewGroup parent, int resIntroText, int resAnimation, int resAppListText)127 static View newHeaderWithAnimation(Context context, ViewGroup parent, int resIntroText, 128 int resAnimation, int resAppListText) { 129 ViewGroup view = (ViewGroup) LayoutInflater.from(parent.getContext()) 130 .inflate(R.layout.preference_app_header_animation, parent, false); 131 TextView introText = view.findViewById(R.id.apps_top_intro_text); 132 introText.setText(resIntroText); 133 134 LottieAnimationView illustrationLottie = view.findViewById(R.id.illustration_lottie); 135 illustrationLottie.setAnimation(resAnimation); 136 illustrationLottie.setVisibility(View.VISIBLE); 137 LottieColorUtils.applyDynamicColors(context, illustrationLottie); 138 139 TextView appListText = view.findViewById(R.id.app_list_text); 140 appListText.setText(resAppListText); 141 142 return view; 143 } 144 setSummary(CharSequence summary)145 void setSummary(CharSequence summary) { 146 mSummary.setText(summary); 147 updateSummaryVisibility(); 148 } 149 setSummary(@tringRes int summary)150 void setSummary(@StringRes int summary) { 151 mSummary.setText(summary); 152 updateSummaryVisibility(); 153 } 154 updateSummaryVisibility()155 private void updateSummaryVisibility() { 156 // Hide an empty summary and then title will be vertically centered. 157 mSummary.setVisibility(TextUtils.isEmpty(mSummary.getText()) ? View.GONE : View.VISIBLE); 158 } 159 setEnabled(boolean isEnabled)160 void setEnabled(boolean isEnabled) { 161 itemView.setEnabled(isEnabled); 162 } 163 setTitle(CharSequence title, CharSequence contentDescription)164 void setTitle(CharSequence title, CharSequence contentDescription) { 165 if (title == null) { 166 return; 167 } 168 mAppName.setText(title); 169 170 if (TextUtils.isEmpty(contentDescription)) { 171 return; 172 } 173 mAppName.setContentDescription(contentDescription); 174 } 175 setIcon(Drawable icon)176 void setIcon(Drawable icon) { 177 if (icon == null) { 178 return; 179 } 180 mAppIcon.setImageDrawable(icon); 181 } 182 updateDisableView(ApplicationInfo info)183 void updateDisableView(ApplicationInfo info) { 184 if ((info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { 185 mDisabled.setVisibility(View.VISIBLE); 186 mDisabled.setText(R.string.not_installed); 187 } else if (!info.enabled || info.enabledSetting 188 == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { 189 mDisabled.setVisibility(View.VISIBLE); 190 mDisabled.setText(com.android.settingslib.R.string.disabled); 191 } else { 192 mDisabled.setVisibility(View.GONE); 193 } 194 } 195 updateSizeText(AppEntry entry, CharSequence invalidSizeStr, int whichSize)196 void updateSizeText(AppEntry entry, CharSequence invalidSizeStr, int whichSize) { 197 if (ManageApplications.DEBUG) { 198 Log.d(ManageApplications.TAG, "updateSizeText of " 199 + entry.label + " " + entry + ": " + entry.sizeStr); 200 } 201 if (entry.sizeStr != null) { 202 switch (whichSize) { 203 case ManageApplications.SIZE_INTERNAL: 204 setSummary(entry.internalSizeStr); 205 break; 206 case ManageApplications.SIZE_EXTERNAL: 207 setSummary(entry.externalSizeStr); 208 break; 209 default: 210 setSummary(entry.sizeStr); 211 break; 212 } 213 } else if (entry.size == ApplicationsState.SIZE_INVALID) { 214 setSummary(invalidSizeStr); 215 } 216 } 217 updateSwitch(CompoundButton.OnCheckedChangeListener listener, boolean enabled, boolean checked)218 void updateSwitch(CompoundButton.OnCheckedChangeListener listener, boolean enabled, 219 boolean checked) { 220 if (mSwitch != null && mWidgetContainer != null) { 221 mWidgetContainer.setFocusable(false); 222 mWidgetContainer.setClickable(false); 223 mSwitch.setFocusable(true); 224 mSwitch.setClickable(true); 225 mSwitch.setOnCheckedChangeListener(listener); 226 mSwitch.setChecked(checked); 227 mSwitch.setEnabled(enabled); 228 } 229 } 230 updateAppCloneWidget(Context context, View.OnClickListener onClickListener, AppEntry entry)231 void updateAppCloneWidget(Context context, View.OnClickListener onClickListener, 232 AppEntry entry) { 233 if (mAddIcon != null) { 234 if (!entry.isClonedProfile()) { 235 mAddIcon.setBackground(context.getDrawable(R.drawable.ic_add_24dp)); 236 } else { 237 mAddIcon.setBackground(context.getDrawable(R.drawable.ic_trash_can)); 238 setSummary(R.string.cloned_app_created_summary); 239 } 240 mAddIcon.setOnClickListener(onClickListener); 241 } 242 } 243 appCloneOnClickListener(AppEntry entry, ApplicationsAdapter adapter, FragmentActivity manageApplicationsActivity)244 View.OnClickListener appCloneOnClickListener(AppEntry entry, 245 ApplicationsAdapter adapter, FragmentActivity manageApplicationsActivity) { 246 Context context = manageApplicationsActivity.getApplicationContext(); 247 return new View.OnClickListener() { 248 @Override 249 public void onClick(View v) { 250 CloneBackend cloneBackend = CloneBackend.getInstance(context); 251 final MetricsFeatureProvider metricsFeatureProvider = 252 FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); 253 254 String packageName = entry.info.packageName; 255 256 if (mWidgetContainer != null) { 257 if (!entry.isClonedProfile()) { 258 metricsFeatureProvider.action(context, 259 SettingsEnums.ACTION_CREATE_CLONE_APP); 260 mAddIcon.setVisibility(View.INVISIBLE); 261 mProgressBar.setVisibility(View.VISIBLE); 262 setSummary(R.string.cloned_app_creation_summary); 263 264 // todo(b/262352524): To figure out a way to prevent memory leak 265 // without making this static. 266 new AsyncTask<Void, Void, Integer>(){ 267 268 @Override 269 protected Integer doInBackground(Void... unused) { 270 return cloneBackend.installCloneApp(packageName); 271 } 272 273 @Override 274 protected void onPostExecute(Integer res) { 275 mProgressBar.setVisibility(View.INVISIBLE); 276 mAddIcon.setVisibility(View.VISIBLE); 277 278 if (res != CloneBackend.SUCCESS) { 279 setSummary(null); 280 return; 281 } 282 283 // Refresh the page to reflect newly created cloned app. 284 adapter.rebuild(); 285 } 286 }.execute(); 287 288 } else if (entry.isClonedProfile()) { 289 metricsFeatureProvider.action(context, 290 SettingsEnums.ACTION_DELETE_CLONE_APP); 291 cloneBackend.uninstallClonedApp(packageName, /*allUsers*/ false, 292 manageApplicationsActivity); 293 } 294 } 295 } 296 }; 297 } 298 } 299