1/* 2 * Copyright (C) 2015 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 17package com.android.tv.settings.device.apps; 18 19import android.app.Activity; 20import android.app.ActivityManager; 21import android.content.ActivityNotFoundException; 22import android.content.Context; 23import android.content.Intent; 24import android.content.pm.IPackageDataObserver; 25import android.content.pm.PackageManager; 26import android.graphics.drawable.Drawable; 27import android.os.Bundle; 28import android.os.Handler; 29import android.os.UserHandle; 30import android.support.annotation.NonNull; 31import android.support.v17.preference.LeanbackPreferenceFragment; 32import android.support.v7.preference.Preference; 33import android.support.v7.preference.PreferenceScreen; 34import android.text.TextUtils; 35import android.util.Log; 36import android.widget.Toast; 37 38import com.android.settingslib.applications.ApplicationsState; 39import com.android.tv.settings.R; 40 41import java.util.ArrayList; 42 43public class AppManagementFragment extends LeanbackPreferenceFragment { 44 private static final String TAG = "AppManagementFragment"; 45 46 private static final String ARG_PACKAGE_NAME = "packageName"; 47 48 private static final String KEY_VERSION = "version"; 49 private static final String KEY_OPEN = "open"; 50 private static final String KEY_FORCE_STOP = "forceStop"; 51 private static final String KEY_UNINSTALL = "uninstall"; 52 private static final String KEY_ENABLE_DISABLE = "enableDisable"; 53 private static final String KEY_APP_STORAGE = "appStorage"; 54 private static final String KEY_CLEAR_DATA = "clearData"; 55 private static final String KEY_CLEAR_CACHE = "clearCache"; 56 private static final String KEY_CLEAR_DEFAULTS = "clearDefaults"; 57 private static final String KEY_NOTIFICATIONS = "notifications"; 58 private static final String KEY_PERMISSIONS = "permissions"; 59 60 // Result code identifiers 61 private static final int REQUEST_UNINSTALL = 1; 62 private static final int REQUEST_MANAGE_SPACE = 2; 63 private static final int REQUEST_UNINSTALL_UPDATES = 3; 64 65 private PackageManager mPackageManager; 66 private String mPackageName; 67 private ApplicationsState mApplicationsState; 68 private ApplicationsState.Session mSession; 69 private ApplicationsState.AppEntry mEntry; 70 private final ApplicationsState.Callbacks mCallbacks = new ApplicationsStateCallbacks(); 71 72 private ForceStopPreference mForceStopPreference; 73 private UninstallPreference mUninstallPreference; 74 private EnableDisablePreference mEnableDisablePreference; 75 private AppStoragePreference mAppStoragePreference; 76 private ClearDataPreference mClearDataPreference; 77 private ClearCachePreference mClearCachePreference; 78 private ClearDefaultsPreference mClearDefaultsPreference; 79 private NotificationsPreference mNotificationsPreference; 80 81 private final Handler mHandler = new Handler(); 82 private Runnable mBailoutRunnable = new Runnable() { 83 @Override 84 public void run() { 85 if (isResumed() && !getFragmentManager().popBackStackImmediate()) { 86 getActivity().onBackPressed(); 87 } 88 } 89 }; 90 91 public static void prepareArgs(@NonNull Bundle args, String packageName) { 92 args.putString(ARG_PACKAGE_NAME, packageName); 93 } 94 95 @Override 96 public void onCreate(Bundle savedInstanceState) { 97 mPackageName = getArguments().getString(ARG_PACKAGE_NAME); 98 99 final Activity activity = getActivity(); 100 mPackageManager = activity.getPackageManager(); 101 mApplicationsState = ApplicationsState.getInstance(activity.getApplication()); 102 mSession = mApplicationsState.newSession(mCallbacks); 103 mEntry = mApplicationsState.getEntry(mPackageName, UserHandle.myUserId()); 104 105 super.onCreate(savedInstanceState); 106 } 107 108 @Override 109 public void onResume() { 110 super.onResume(); 111 mSession.resume(); 112 113 if (mEntry == null) { 114 Log.w(TAG, "App not found, trying to bail out"); 115 navigateBack(); 116 } 117 118 if (mClearDefaultsPreference != null) { 119 mClearDefaultsPreference.refresh(); 120 } 121 if (mEnableDisablePreference != null) { 122 mEnableDisablePreference.refresh(); 123 } 124 } 125 126 @Override 127 public void onPause() { 128 super.onPause(); 129 mSession.pause(); 130 mHandler.removeCallbacks(mBailoutRunnable); 131 } 132 133 @Override 134 public void onActivityResult(int requestCode, int resultCode, Intent data) { 135 super.onActivityResult(requestCode, resultCode, data); 136 if (mEntry == null) { 137 return; 138 } 139 switch (requestCode) { 140 case REQUEST_UNINSTALL: 141 final int deleteResult = data != null 142 ? data.getIntExtra(Intent.EXTRA_INSTALL_RESULT, 0) : 0; 143 if (deleteResult == PackageManager.DELETE_SUCCEEDED) { 144 final int userId = UserHandle.getUserId(mEntry.info.uid); 145 mApplicationsState.removePackage(mPackageName, userId); 146 navigateBack(); 147 } else { 148 Log.e(TAG, "Uninstall failed with result " + deleteResult); 149 } 150 break; 151 case REQUEST_MANAGE_SPACE: 152 mClearDataPreference.setClearingData(false); 153 if(resultCode == Activity.RESULT_OK) { 154 final int userId = UserHandle.getUserId(mEntry.info.uid); 155 mApplicationsState.requestSize(mPackageName, userId); 156 } else { 157 Log.w(TAG, "Failed to clear data!"); 158 } 159 break; 160 case REQUEST_UNINSTALL_UPDATES: 161 mUninstallPreference.refresh(); 162 break; 163 } 164 } 165 166 private void navigateBack() { 167 // need to post this to avoid recursing in the fragment manager. 168 mHandler.removeCallbacks(mBailoutRunnable); 169 mHandler.post(mBailoutRunnable); 170 } 171 172 @Override 173 public boolean onPreferenceTreeClick(Preference preference) { 174 final Intent intent = preference.getIntent(); 175 if (intent != null) { 176 try { 177 if (preference.equals(mUninstallPreference)) { 178 startActivityForResult(intent, mUninstallPreference.canUninstall() 179 ? REQUEST_UNINSTALL : REQUEST_UNINSTALL_UPDATES); 180 } else { 181 startActivity(intent); 182 } 183 } catch (ActivityNotFoundException e) { 184 Log.e(TAG, "Could not find activity to launch", e); 185 Toast.makeText(getContext(), R.string.device_apps_app_management_not_available, 186 Toast.LENGTH_SHORT).show(); 187 } 188 return true; 189 } else { 190 return super.onPreferenceTreeClick(preference); 191 } 192 } 193 194 @Override 195 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 196 final Context themedContext = getPreferenceManager().getContext(); 197 final PreferenceScreen screen = 198 getPreferenceManager().createPreferenceScreen(themedContext); 199 screen.setTitle(getAppName()); 200 setPreferenceScreen(screen); 201 202 updatePrefs(); 203 } 204 205 private void updatePrefs() { 206 if (mEntry == null) { 207 final PreferenceScreen screen = getPreferenceScreen(); 208 screen.removeAll(); 209 return; 210 } 211 final Context themedContext = getPreferenceManager().getContext(); 212 213 // Version 214 Preference versionPreference = findPreference(KEY_VERSION); 215 if (versionPreference == null) { 216 versionPreference = new Preference(themedContext); 217 versionPreference.setKey(KEY_VERSION); 218 replacePreference(versionPreference); 219 versionPreference.setSelectable(false); 220 } 221 versionPreference.setTitle(getString(R.string.device_apps_app_management_version, 222 mEntry.getVersion(getActivity()))); 223 versionPreference.setSummary(mPackageName); 224 225 // Open 226 Preference openPreference = findPreference(KEY_OPEN); 227 if (openPreference == null) { 228 openPreference = new Preference(themedContext); 229 openPreference.setKey(KEY_OPEN); 230 replacePreference(openPreference); 231 } 232 Intent appLaunchIntent = 233 mPackageManager.getLeanbackLaunchIntentForPackage(mEntry.info.packageName); 234 if (appLaunchIntent == null) { 235 appLaunchIntent = mPackageManager.getLaunchIntentForPackage(mEntry.info.packageName); 236 } 237 if (appLaunchIntent != null) { 238 openPreference.setIntent(appLaunchIntent); 239 openPreference.setTitle(R.string.device_apps_app_management_open); 240 openPreference.setVisible(true); 241 } else { 242 openPreference.setVisible(false); 243 } 244 245 // Force stop 246 if (mForceStopPreference == null) { 247 mForceStopPreference = new ForceStopPreference(themedContext, mEntry); 248 mForceStopPreference.setKey(KEY_FORCE_STOP); 249 replacePreference(mForceStopPreference); 250 } else { 251 mForceStopPreference.setEntry(mEntry); 252 } 253 254 // Uninstall 255 if (mUninstallPreference == null) { 256 mUninstallPreference = new UninstallPreference(themedContext, mEntry); 257 mUninstallPreference.setKey(KEY_UNINSTALL); 258 replacePreference(mUninstallPreference); 259 } else { 260 mUninstallPreference.setEntry(mEntry); 261 } 262 263 // Disable/Enable 264 if (mEnableDisablePreference == null) { 265 mEnableDisablePreference = new EnableDisablePreference(themedContext, mEntry); 266 mEnableDisablePreference.setKey(KEY_ENABLE_DISABLE); 267 replacePreference(mEnableDisablePreference); 268 } else { 269 mEnableDisablePreference.setEntry(mEntry); 270 } 271 272 // Storage used 273 if (mAppStoragePreference == null) { 274 mAppStoragePreference = new AppStoragePreference(themedContext, mEntry); 275 mAppStoragePreference.setKey(KEY_APP_STORAGE); 276 replacePreference(mAppStoragePreference); 277 } else { 278 mAppStoragePreference.setEntry(mEntry); 279 } 280 281 // Clear data 282 if (mClearDataPreference == null) { 283 mClearDataPreference = new ClearDataPreference(themedContext, mEntry); 284 mClearDataPreference.setKey(KEY_CLEAR_DATA); 285 replacePreference(mClearDataPreference); 286 } else { 287 mClearDataPreference.setEntry(mEntry); 288 } 289 290 // Clear cache 291 if (mClearCachePreference == null) { 292 mClearCachePreference = new ClearCachePreference(themedContext, mEntry); 293 mClearCachePreference.setKey(KEY_CLEAR_CACHE); 294 replacePreference(mClearCachePreference); 295 } else { 296 mClearCachePreference.setEntry(mEntry); 297 } 298 299 // Clear defaults 300 if (mClearDefaultsPreference == null) { 301 mClearDefaultsPreference = new ClearDefaultsPreference(themedContext, mEntry); 302 mClearDefaultsPreference.setKey(KEY_CLEAR_DEFAULTS); 303 replacePreference(mClearDefaultsPreference); 304 } else { 305 mClearDefaultsPreference.setEntry(mEntry); 306 } 307 308 // Notifications 309 if (mNotificationsPreference == null) { 310 mNotificationsPreference = new NotificationsPreference(themedContext, mEntry); 311 mNotificationsPreference.setKey(KEY_NOTIFICATIONS); 312 replacePreference(mNotificationsPreference); 313 } else { 314 mNotificationsPreference.setEntry(mEntry); 315 } 316 317 // Permissions 318 Preference permissionsPreference = findPreference(KEY_PERMISSIONS); 319 if (permissionsPreference == null) { 320 permissionsPreference = new Preference(themedContext); 321 permissionsPreference.setKey(KEY_PERMISSIONS); 322 permissionsPreference.setTitle(R.string.device_apps_app_management_permissions); 323 replacePreference(permissionsPreference); 324 } 325 permissionsPreference.setIntent(new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS) 326 .putExtra(Intent.EXTRA_PACKAGE_NAME, mPackageName)); 327 } 328 329 private void replacePreference(Preference preference) { 330 final String key = preference.getKey(); 331 if (TextUtils.isEmpty(key)) { 332 throw new IllegalArgumentException("Can't replace a preference without a key"); 333 } 334 final Preference old = findPreference(key); 335 if (old != null) { 336 getPreferenceScreen().removePreference(old); 337 } 338 getPreferenceScreen().addPreference(preference); 339 } 340 341 public String getAppName() { 342 if (mEntry == null) { 343 return null; 344 } 345 mEntry.ensureLabel(getActivity()); 346 return mEntry.label; 347 } 348 349 public Drawable getAppIcon() { 350 if (mEntry == null) { 351 return null; 352 } 353 mApplicationsState.ensureIcon(mEntry); 354 return mEntry.icon; 355 } 356 357 public void clearData() { 358 mClearDataPreference.setClearingData(true); 359 String spaceManagementActivityName = mEntry.info.manageSpaceActivityName; 360 if (spaceManagementActivityName != null) { 361 if (!ActivityManager.isUserAMonkey()) { 362 Intent intent = new Intent(Intent.ACTION_DEFAULT); 363 intent.setClassName(mEntry.info.packageName, spaceManagementActivityName); 364 startActivityForResult(intent, REQUEST_MANAGE_SPACE); 365 } 366 } else { 367 ActivityManager am = (ActivityManager) getActivity().getSystemService( 368 Context.ACTIVITY_SERVICE); 369 boolean success = am.clearApplicationUserData( 370 mEntry.info.packageName, new IPackageDataObserver.Stub() { 371 public void onRemoveCompleted( 372 final String packageName, final boolean succeeded) { 373 mHandler.post(new Runnable() { 374 @Override 375 public void run() { 376 mClearDataPreference.setClearingData(false); 377 if (succeeded) { 378 dataCleared(true); 379 } else { 380 dataCleared(false); 381 } 382 } 383 }); 384 } 385 }); 386 if (!success) { 387 mClearDataPreference.setClearingData(false); 388 dataCleared(false); 389 } 390 } 391 mClearDataPreference.refresh(); 392 } 393 394 private void dataCleared(boolean succeeded) { 395 if (succeeded) { 396 final int userId = UserHandle.getUserId(mEntry.info.uid); 397 mApplicationsState.requestSize(mPackageName, userId); 398 } else { 399 Log.w(TAG, "Failed to clear data!"); 400 mClearDataPreference.refresh(); 401 } 402 } 403 404 public void clearCache() { 405 mClearCachePreference.setClearingCache(true); 406 mPackageManager.deleteApplicationCacheFiles(mEntry.info.packageName, 407 new IPackageDataObserver.Stub() { 408 public void onRemoveCompleted(final String packageName, 409 final boolean succeeded) { 410 mHandler.post(new Runnable() { 411 @Override 412 public void run() { 413 mClearCachePreference.setClearingCache(false); 414 cacheCleared(succeeded); 415 } 416 }); 417 } 418 }); 419 mClearCachePreference.refresh(); 420 } 421 422 private void cacheCleared(boolean succeeded) { 423 if (succeeded) { 424 final int userId = UserHandle.getUserId(mEntry.info.uid); 425 mApplicationsState.requestSize(mPackageName, userId); 426 } else { 427 Log.w(TAG, "Failed to clear cache!"); 428 mClearCachePreference.refresh(); 429 } 430 } 431 432 private class ApplicationsStateCallbacks implements ApplicationsState.Callbacks { 433 434 @Override 435 public void onRunningStateChanged(boolean running) { 436 if (mForceStopPreference != null) { 437 mForceStopPreference.refresh(); 438 } 439 } 440 441 @Override 442 public void onPackageListChanged() { 443 if (mEntry == null) { 444 return; 445 } 446 final int userId = UserHandle.getUserId(mEntry.info.uid); 447 mEntry = mApplicationsState.getEntry(mPackageName, userId); 448 if (mEntry == null) { 449 navigateBack(); 450 } 451 updatePrefs(); 452 } 453 454 @Override 455 public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) {} 456 457 @Override 458 public void onPackageIconChanged() {} 459 460 @Override 461 public void onPackageSizeChanged(String packageName) { 462 if (mAppStoragePreference == null) { 463 // Nothing to do here. 464 return; 465 } 466 mAppStoragePreference.refresh(); 467 mClearDataPreference.refresh(); 468 mClearCachePreference.refresh(); 469 } 470 471 @Override 472 public void onAllSizesComputed() { 473 if (mAppStoragePreference == null) { 474 // Nothing to do here. 475 return; 476 } 477 mAppStoragePreference.refresh(); 478 mClearDataPreference.refresh(); 479 mClearCachePreference.refresh(); 480 } 481 482 @Override 483 public void onLauncherInfoChanged() { 484 updatePrefs(); 485 } 486 487 @Override 488 public void onLoadEntriesCompleted() { 489 if (mAppStoragePreference == null) { 490 // Nothing to do here. 491 return; 492 } 493 mAppStoragePreference.refresh(); 494 mClearDataPreference.refresh(); 495 mClearCachePreference.refresh(); 496 } 497 } 498} 499