From 1f4efd7360dea3038fe2096b1d4bccaae8e2a09f Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 15 Apr 2015 14:32:25 -0700 Subject: [PATCH] Move packages between candidate volumes. We now allow moving packages between all possible candidate volumes, as determined by the framework. Moving now jumps through wizard to help user understand what's going on. Bug: 19993667 Change-Id: I5416ed2865f400b1d718c68a3f0e033080fefa0e --- res/layout/preference_list_fragment.xml | 2 +- .../applications/AppStorageSettings.java | 142 +++++------------- .../applications/ApplicationsState.java | 14 -- .../applications/ManageApplications.java | 46 ------ .../settings/deviceinfo/StorageSettings.java | 18 +-- .../deviceinfo/StorageWizardMoveConfirm.java | 68 +++++++++ .../deviceinfo/StorageWizardMoveProgress.java | 138 +++++++++++++++++ 7 files changed, 246 insertions(+), 182 deletions(-) create mode 100644 src/com/android/settings/deviceinfo/StorageWizardMoveConfirm.java create mode 100644 src/com/android/settings/deviceinfo/StorageWizardMoveProgress.java diff --git a/res/layout/preference_list_fragment.xml b/res/layout/preference_list_fragment.xml index 6e8ad911032..195f6eb8744 100644 --- a/res/layout/preference_list_fragment.xml +++ b/res/layout/preference_list_fragment.xml @@ -36,7 +36,7 @@ candidates = context.getPackageManager() + .getApplicationCandidateVolumes(mAppEntry.info); + Collections.sort(candidates, VolumeInfo.getDescriptionComparator()); + + mMoveDropDown.clearItems(); + for (VolumeInfo vol : candidates) { + final String volDescrip = storage.getBestVolumeDescription(vol); + mMoveDropDown.addItem(volDescrip, vol); } - boolean dataOnly = (mPackageInfo == null) && (mAppEntry != null); - boolean moveDisable = true; - if ((mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { - // Always let apps move to internal storage from sdcard. - moveDisable = false; - } else { - mCanBeOnSdCardChecker.init(); - moveDisable = !mCanBeOnSdCardChecker.check(mAppEntry.info); - } - updateMoveEnabled(!moveDisable); mMoveDropDown.setSelectable(!mAppControlRestricted); } @@ -351,21 +338,6 @@ public class AppStorageSettings extends AppInfoWithHeader } } - private void processMoveMsg(Message msg) { - int result = msg.arg1; - String packageName = mAppEntry.info.packageName; - // Refresh the button attributes. - mMoveInProgress = false; - if (result == PackageManager.MOVE_SUCCEEDED) { - Log.i(TAG, "Moved resources for " + packageName); - // Refresh size information again. - mState.requestSize(mPackageName, mUserId); - } else { - showDialogInner(DLG_MOVE_FAILED, result); - } - refreshUi(); - } - /* * Private method to handle clear message notification from observer when * the async operation from PackageManager is complete @@ -382,24 +354,6 @@ public class AppStorageSettings extends AppInfoWithHeader } } - private CharSequence getMoveErrMsg(int errCode) { - switch (errCode) { - case PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE: - return getActivity().getString(R.string.insufficient_storage); - case PackageManager.MOVE_FAILED_DOESNT_EXIST: - return getActivity().getString(R.string.does_not_exist); - case PackageManager.MOVE_FAILED_FORWARD_LOCKED: - return getActivity().getString(R.string.app_forward_locked); - case PackageManager.MOVE_FAILED_INVALID_LOCATION: - return getActivity().getString(R.string.invalid_location); - case PackageManager.MOVE_FAILED_SYSTEM_PACKAGE: - return getActivity().getString(R.string.system_package); - case PackageManager.MOVE_FAILED_INTERNAL_ERROR: - return ""; - } - return ""; - } - @Override protected AlertDialog createDialog(int id, int errorCode) { switch (id) { @@ -427,14 +381,6 @@ public class AppStorageSettings extends AppInfoWithHeader } }) .create(); - case DLG_MOVE_FAILED: - CharSequence msg = getActivity().getString(R.string.move_app_failed_dlg_text, - getMoveErrMsg(errorCode)); - return new AlertDialog.Builder(getActivity()) - .setTitle(getActivity().getText(R.string.move_app_failed_dlg_title)) - .setMessage(msg) - .setNeutralButton(R.string.dlg_ok, null) - .create(); } return null; } @@ -459,9 +405,6 @@ public class AppStorageSettings extends AppInfoWithHeader // Refresh size info mState.requestSize(mPackageName, mUserId); break; - case MSG_PACKAGE_MOVE: - processMoveMsg(msg); - break; } } }; @@ -508,13 +451,4 @@ public class AppStorageSettings extends AppInfoWithHeader mHandler.sendMessage(msg); } } - - class PackageMoveObserver extends IPackageMoveObserver.Stub { - public void packageMoved(String packageName, int returnCode) throws RemoteException { - final Message msg = mHandler.obtainMessage(MSG_PACKAGE_MOVE); - msg.arg1 = returnCode; - mHandler.sendMessage(msg); - } - } - } diff --git a/src/com/android/settings/applications/ApplicationsState.java b/src/com/android/settings/applications/ApplicationsState.java index 162ca7715a5..ddfc207650f 100644 --- a/src/com/android/settings/applications/ApplicationsState.java +++ b/src/com/android/settings/applications/ApplicationsState.java @@ -295,20 +295,6 @@ public class ApplicationsState { } }; - public static final AppFilter FILTER_ON_SD_CARD = new AppFilter() { - final CanBeOnSdCardChecker mCanBeOnSdCardChecker - = new CanBeOnSdCardChecker(); - - public void init() { - mCanBeOnSdCardChecker.init(); - } - - @Override - public boolean filterApp(AppEntry entry) { - return mCanBeOnSdCardChecker.check(entry.info); - } - }; - public static final AppFilter FILTER_DISABLED = new AppFilter() { public void init() { } diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java index b6b2b828944..362903e53af 100644 --- a/src/com/android/settings/applications/ManageApplications.java +++ b/src/com/android/settings/applications/ManageApplications.java @@ -20,15 +20,11 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageManager; import android.content.pm.IntentFilterVerificationInfo; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.os.Environment; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.preference.PreferenceFrameLayout; @@ -53,7 +49,6 @@ import android.widget.Filterable; import android.widget.ListView; import android.widget.Spinner; -import com.android.internal.content.PackageHelper; import com.android.internal.logging.MetricsLogger; import com.android.settings.HelpUtils; import com.android.settings.InstrumentedFragment; @@ -74,47 +69,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; -final class CanBeOnSdCardChecker { - final IPackageManager mPm; - int mInstallLocation; - - CanBeOnSdCardChecker() { - mPm = IPackageManager.Stub.asInterface( - ServiceManager.getService("package")); - } - - void init() { - try { - mInstallLocation = mPm.getInstallLocation(); - } catch (RemoteException e) { - Log.e("CanBeOnSdCardChecker", "Is Package Manager running?"); - return; - } - } - - boolean check(ApplicationInfo info) { - boolean canBe = false; - if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { - canBe = true; - } else { - if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { - if (info.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL || - info.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) { - canBe = true; - } else if (info.installLocation - == PackageInfo.INSTALL_LOCATION_UNSPECIFIED) { - if (mInstallLocation == PackageHelper.APP_INSTALL_EXTERNAL) { - // For apps with no preference and the default value set - // to install on sdcard. - canBe = true; - } - } - } - } - return canBe; - } -} - /** * Activity to pick an application that will be used to display installation information and * options to uninstall/delete user data for system applications. This activity diff --git a/src/com/android/settings/deviceinfo/StorageSettings.java b/src/com/android/settings/deviceinfo/StorageSettings.java index 1e33230df88..74040ef20c8 100644 --- a/src/com/android/settings/deviceinfo/StorageSettings.java +++ b/src/com/android/settings/deviceinfo/StorageSettings.java @@ -41,7 +41,6 @@ import com.android.settings.search.SearchIndexableRaw; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.List; /** @@ -91,21 +90,6 @@ public class StorageSettings extends SettingsPreferenceFragment implements Index setHasOptionsMenu(true); } - private static final Comparator sVolumeComparator = new Comparator() { - @Override - public int compare(VolumeInfo lhs, VolumeInfo rhs) { - if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(lhs.getId())) { - return -1; - } else if (lhs.getDescription() == null) { - return 1; - } else if (rhs.getDescription() == null) { - return -1; - } else { - return lhs.getDescription().compareTo(rhs.getDescription()); - } - } - }; - private final StorageEventListener mStorageListener = new StorageEventListener() { @Override public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { @@ -133,7 +117,7 @@ public class StorageSettings extends SettingsPreferenceFragment implements Index mExternalCategory.removeAll(); final List volumes = mStorageManager.getVolumes(); - Collections.sort(volumes, sVolumeComparator); + Collections.sort(volumes, VolumeInfo.getDescriptionComparator()); for (VolumeInfo vol : volumes) { if (vol.getType() == VolumeInfo.TYPE_PRIVATE) { diff --git a/src/com/android/settings/deviceinfo/StorageWizardMoveConfirm.java b/src/com/android/settings/deviceinfo/StorageWizardMoveConfirm.java new file mode 100644 index 00000000000..36750c078db --- /dev/null +++ b/src/com/android/settings/deviceinfo/StorageWizardMoveConfirm.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.deviceinfo; + +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Bundle; +import android.os.storage.VolumeInfo; + +import com.android.internal.util.Preconditions; +import com.android.settings.R; + +public class StorageWizardMoveConfirm extends StorageWizardBase { + private String mPackageName; + private ApplicationInfo mApp; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.storage_wizard_generic); + + try { + mPackageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME); + mApp = getPackageManager().getApplicationInfo(mPackageName, 0); + } catch (NameNotFoundException e) { + throw new RuntimeException(e); + } + + Preconditions.checkNotNull(mVolume); + Preconditions.checkNotNull(mApp); + + // Sanity check that target volume is candidate + Preconditions.checkState( + getPackageManager().getApplicationCandidateVolumes(mApp).contains(mVolume)); + + final String appName = getPackageManager().getApplicationLabel(mApp).toString(); + final String volumeName = mStorage.getBestVolumeDescription(mVolume); + + setHeaderText(R.string.storage_wizard_move_confirm_title, appName); + setBodyText(R.string.storage_wizard_move_confirm_body, appName, volumeName); + + getNextButton().setText(R.string.move_app); + } + + @Override + public void onNavigateNext() { + final Intent intent = new Intent(this, StorageWizardMoveProgress.class); + intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, mVolume.getId()); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mPackageName); + startActivity(intent); + finishAffinity(); + } +} diff --git a/src/com/android/settings/deviceinfo/StorageWizardMoveProgress.java b/src/com/android/settings/deviceinfo/StorageWizardMoveProgress.java new file mode 100644 index 00000000000..20233357c2a --- /dev/null +++ b/src/com/android/settings/deviceinfo/StorageWizardMoveProgress.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.deviceinfo; + +import static com.android.settings.deviceinfo.StorageSettings.TAG; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageMoveObserver; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.storage.VolumeInfo; +import android.util.Log; +import android.view.View; +import android.widget.Toast; + +import com.android.internal.util.Preconditions; +import com.android.settings.R; + +import java.util.concurrent.CountDownLatch; + +public class StorageWizardMoveProgress extends StorageWizardBase { + private String mPackageName; + private ApplicationInfo mApp; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.storage_wizard_progress); + + try { + mPackageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME); + mApp = getPackageManager().getApplicationInfo(mPackageName, 0); + } catch (NameNotFoundException e) { + throw new RuntimeException(e); + } + + Preconditions.checkNotNull(mVolume); + Preconditions.checkNotNull(mApp); + + final String appName = getPackageManager().getApplicationLabel(mApp).toString(); + final String volumeName = mStorage.getBestVolumeDescription(mVolume); + + setHeaderText(R.string.storage_wizard_move_progress_title, appName); + setBodyText(R.string.storage_wizard_move_progress_body, volumeName, appName); + + setCurrentProgress(20); + + getNextButton().setVisibility(View.GONE); + + new MoveTask().execute(); + } + + private CharSequence moveStatusToMessage(int returnCode) { + switch (returnCode) { + case PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE: + return getString(R.string.insufficient_storage); + case PackageManager.MOVE_FAILED_DOESNT_EXIST: + return getString(R.string.does_not_exist); + case PackageManager.MOVE_FAILED_FORWARD_LOCKED: + return getString(R.string.app_forward_locked); + case PackageManager.MOVE_FAILED_INVALID_LOCATION: + return getString(R.string.invalid_location); + case PackageManager.MOVE_FAILED_SYSTEM_PACKAGE: + return getString(R.string.system_package); + case PackageManager.MOVE_FAILED_INTERNAL_ERROR: + default: + return getString(R.string.insufficient_storage); + } + } + + private class LocalPackageMoveObserver extends IPackageMoveObserver.Stub { + public int returnCode; + public CountDownLatch finished = new CountDownLatch(1); + + @Override + public void packageMoved(String packageName, int returnCode) throws RemoteException { + this.returnCode = returnCode; + this.finished.countDown(); + } + } + + public class MoveTask extends AsyncTask { + @Override + protected Integer doInBackground(Void... params) { + try { + final LocalPackageMoveObserver observer = new LocalPackageMoveObserver(); + + if (mApp.isExternalAsec()) { + getPackageManager().movePackage(mPackageName, observer, + PackageManager.MOVE_INTERNAL); + } else if (mVolume.getType() == VolumeInfo.TYPE_PUBLIC) { + getPackageManager().movePackage(mPackageName, observer, + PackageManager.MOVE_EXTERNAL_MEDIA); + } else { + getPackageManager().movePackageAndData(mPackageName, mVolume.fsUuid, observer); + } + + observer.finished.await(); + return observer.returnCode; + } catch (Exception e) { + Log.e(TAG, "Failed to move", e); + return PackageManager.MOVE_FAILED_INTERNAL_ERROR; + } + } + + @Override + protected void onPostExecute(Integer returnCode) { + final Context context = StorageWizardMoveProgress.this; + if (returnCode == PackageManager.MOVE_SUCCEEDED) { + finishAffinity(); + + } else { + Log.w(TAG, "Move failed with status " + returnCode); + Toast.makeText(context, moveStatusToMessage(returnCode), Toast.LENGTH_LONG).show(); + finishAffinity(); + } + } + } +}