Home | History | Annotate | Download | only in storage
      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 
     17 package com.android.tv.settings.device.storage;
     18 
     19 import android.content.pm.ApplicationInfo;
     20 import android.content.pm.PackageManager;
     21 import android.graphics.drawable.Drawable;
     22 import android.os.AsyncTask;
     23 import android.os.Bundle;
     24 import android.os.storage.StorageManager;
     25 import android.os.storage.VolumeInfo;
     26 import android.support.annotation.NonNull;
     27 import android.support.v17.leanback.app.GuidedStepFragment;
     28 import android.support.v17.leanback.widget.GuidanceStylist;
     29 import android.support.v17.leanback.widget.GuidedAction;
     30 import android.text.TextUtils;
     31 import android.util.ArrayMap;
     32 
     33 import com.android.settingslib.applications.ApplicationsState;
     34 import com.android.tv.settings.R;
     35 import com.android.tv.settings.device.apps.AppInfo;
     36 import com.android.tv.settings.device.apps.MoveAppActivity;
     37 
     38 import java.util.ArrayList;
     39 import java.util.List;
     40 import java.util.Map;
     41 
     42 public class BackupAppsStepFragment extends GuidedStepFragment implements
     43         ApplicationsState.Callbacks {
     44 
     45     private static final int ACTION_NO_APPS = 0;
     46     private static final int ACTION_MIGRATE_DATA = 1;
     47     private static final int ACTION_BACKUP_APP_BASE = 100;
     48 
     49     private ApplicationsState mApplicationsState;
     50     private ApplicationsState.Session mSession;
     51 
     52     private PackageManager mPackageManager;
     53     private StorageManager mStorageManager;
     54 
     55     private String mVolumeId;
     56     private ApplicationsState.AppFilter mAppFilter;
     57 
     58     private IconLoaderTask mIconLoaderTask;
     59     private final Map<String, Drawable> mIconMap = new ArrayMap<>();
     60 
     61     private final List<ApplicationsState.AppEntry> mEntries = new ArrayList<>();
     62 
     63     public static BackupAppsStepFragment newInstance(String volumeId) {
     64         final BackupAppsStepFragment fragment = new BackupAppsStepFragment();
     65         final Bundle b = new Bundle(1);
     66         b.putString(VolumeInfo.EXTRA_VOLUME_ID, volumeId);
     67         fragment.setArguments(b);
     68         return fragment;
     69     }
     70 
     71     @Override
     72     public void onCreate(Bundle savedInstanceState) {
     73         // Need mPackageManager before onCreateActions, which is called from super.onCreate
     74         mPackageManager = getActivity().getPackageManager();
     75         mStorageManager = getActivity().getSystemService(StorageManager.class);
     76 
     77         mVolumeId = getArguments().getString(VolumeInfo.EXTRA_VOLUME_ID);
     78         final VolumeInfo info = mStorageManager.findVolumeById(mVolumeId);
     79         if (info != null) {
     80             mAppFilter = new ApplicationsState.VolumeFilter(info.getFsUuid());
     81         } else {
     82             if (!getFragmentManager().popBackStackImmediate()) {
     83                 getActivity().finish();
     84             }
     85             mAppFilter = new ApplicationsState.AppFilter() {
     86                 @Override
     87                 public void init() {}
     88 
     89                 @Override
     90                 public boolean filterApp(ApplicationsState.AppEntry info) {
     91                     return false;
     92                 }
     93             };
     94         }
     95 
     96         mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
     97         mSession = mApplicationsState.newSession(this);
     98 
     99         super.onCreate(savedInstanceState);
    100     }
    101 
    102     @Override
    103     public void onResume() {
    104         super.onResume();
    105         mSession.resume();
    106         updateActions();
    107     }
    108 
    109     @Override
    110     public void onPause() {
    111         super.onPause();
    112         mSession.pause();
    113     }
    114 
    115     @Override
    116     public @NonNull GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
    117         final String title;
    118         final VolumeInfo volumeInfo = mStorageManager.findVolumeById(mVolumeId);
    119         final String volumeDesc = mStorageManager.getBestVolumeDescription(volumeInfo);
    120         final String primaryStorageVolumeId =
    121                 mPackageManager.getPrimaryStorageCurrentVolume().getId();
    122         if (TextUtils.equals(primaryStorageVolumeId, volumeInfo.getId())) {
    123             title = getString(R.string.storage_wizard_back_up_apps_and_data_title, volumeDesc);
    124         } else {
    125             title = getString(R.string.storage_wizard_back_up_apps_title, volumeDesc);
    126         }
    127         return new GuidanceStylist.Guidance(
    128                 title,
    129                 "",
    130                 "",
    131                 getActivity().getDrawable(R.drawable.ic_storage_132dp));
    132     }
    133 
    134     @Override
    135     public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
    136         final List<ApplicationsState.AppEntry> entries = mSession.rebuild(mAppFilter,
    137                 ApplicationsState.ALPHA_COMPARATOR);
    138         if (entries != null) {
    139             actions.addAll(getAppActions(true, entries));
    140         }
    141     }
    142 
    143     private List<GuidedAction> getAppActions(boolean refreshIcons,
    144             List<ApplicationsState.AppEntry> entries) {
    145 
    146         final List<GuidedAction> actions = new ArrayList<>(entries.size() + 1);
    147 
    148         boolean showMigrate = false;
    149         final VolumeInfo currentExternal = mPackageManager.getPrimaryStorageCurrentVolume();
    150         // currentExternal will be null if the drive is not mounted. Don't offer the option to
    151         // migrate if so.
    152         if (currentExternal != null
    153                 && TextUtils.equals(currentExternal.getId(), mVolumeId)) {
    154             final List<VolumeInfo> candidates =
    155                     mPackageManager.getPrimaryStorageCandidateVolumes();
    156             for (final VolumeInfo candidate : candidates) {
    157                 if (!TextUtils.equals(candidate.getId(), mVolumeId)) {
    158                     showMigrate = true;
    159                     break;
    160                 }
    161             }
    162         }
    163 
    164         if (showMigrate) {
    165             actions.add(new GuidedAction.Builder(getContext())
    166                     .id(ACTION_MIGRATE_DATA)
    167                     .title(R.string.storage_migrate_away)
    168                     .build());
    169         }
    170 
    171         int index = ACTION_BACKUP_APP_BASE;
    172         for (final ApplicationsState.AppEntry entry : entries) {
    173             final ApplicationInfo info = entry.info;
    174             final AppInfo appInfo = new AppInfo(getActivity(), entry);
    175             actions.add(new GuidedAction.Builder(getContext())
    176                     .title(appInfo.getName())
    177                     .description(appInfo.getSize())
    178                     .icon(mIconMap.get(info.packageName))
    179                     .id(index++)
    180                     .build());
    181         }
    182         mEntries.clear();
    183         mEntries.addAll(entries);
    184 
    185         if (refreshIcons) {
    186             if (mIconLoaderTask != null) {
    187                 mIconLoaderTask.cancel(true);
    188             }
    189             mIconLoaderTask = new IconLoaderTask(entries);
    190             mIconLoaderTask.execute();
    191         }
    192 
    193         if (actions.size() == 0) {
    194             actions.add(new GuidedAction.Builder(getContext())
    195                     .id(ACTION_NO_APPS)
    196                     .title(R.string.storage_no_apps)
    197                     .build());
    198         }
    199         return actions;
    200     }
    201 
    202     private void updateActions() {
    203         final List<ApplicationsState.AppEntry> entries = mSession.rebuild(mAppFilter,
    204                 ApplicationsState.ALPHA_COMPARATOR);
    205         if (entries != null) {
    206             setActions(getAppActions(true, entries));
    207         } else {
    208             setActions(getAppActions(true, mEntries));
    209         }
    210     }
    211 
    212     @Override
    213     public void onGuidedActionClicked(GuidedAction action) {
    214         final int actionId = (int) action.getId();
    215         if (actionId == ACTION_MIGRATE_DATA) {
    216             startActivity(MigrateStorageActivity.getLaunchIntent(getActivity(), mVolumeId, false));
    217         } else if (actionId == ACTION_NO_APPS) {
    218             if (!getFragmentManager().popBackStackImmediate()) {
    219                 getActivity().finish();
    220             }
    221         } else if (actionId >= ACTION_BACKUP_APP_BASE
    222                 && actionId < mEntries.size() + ACTION_BACKUP_APP_BASE) {
    223             final ApplicationsState.AppEntry entry =
    224                     mEntries.get(actionId - ACTION_BACKUP_APP_BASE);
    225             final AppInfo appInfo = new AppInfo(getActivity(), entry);
    226 
    227             startActivity(MoveAppActivity.getLaunchIntent(getActivity(), entry.info.packageName,
    228                     appInfo.getName()));
    229         } else {
    230             throw new IllegalArgumentException("Unknown action " + action);
    231         }
    232     }
    233 
    234     @Override
    235     public void onRunningStateChanged(boolean running) {
    236         updateActions();
    237     }
    238 
    239     @Override
    240     public void onPackageListChanged() {
    241         updateActions();
    242     }
    243 
    244     @Override
    245     public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) {
    246         setActions(getAppActions(true, apps));
    247     }
    248 
    249     @Override
    250     public void onLauncherInfoChanged() {
    251         updateActions();
    252     }
    253 
    254     @Override
    255     public void onLoadEntriesCompleted() {
    256         updateActions();
    257     }
    258 
    259     @Override
    260     public void onPackageIconChanged() {
    261         updateActions();
    262     }
    263 
    264     @Override
    265     public void onPackageSizeChanged(String packageName) {
    266         updateActions();
    267     }
    268 
    269     @Override
    270     public void onAllSizesComputed() {
    271         updateActions();
    272     }
    273 
    274     private class IconLoaderTask extends AsyncTask<Void, Void, Map<String, Drawable>> {
    275         private final List<ApplicationsState.AppEntry> mEntries;
    276 
    277         public IconLoaderTask(List<ApplicationsState.AppEntry> entries) {
    278             mEntries = entries;
    279         }
    280 
    281         @Override
    282         protected Map<String, Drawable> doInBackground(Void... params) {
    283             // NB: Java doesn't like parameterized generics in varargs
    284             final Map<String, Drawable> result = new ArrayMap<>(mEntries.size());
    285             for (final ApplicationsState.AppEntry entry : mEntries) {
    286                 result.put(entry.info.packageName, mPackageManager.getApplicationIcon(entry.info));
    287             }
    288             return result;
    289         }
    290 
    291         @Override
    292         protected void onPostExecute(Map<String, Drawable> stringDrawableMap) {
    293             mIconLoaderTask = null;
    294             if (!isAdded()) {
    295                 return;
    296             }
    297             mIconMap.putAll(stringDrawableMap);
    298             setActions(getAppActions(false, mEntries));
    299         }
    300     }
    301 
    302 }
    303