I want to know how to check whether the app is movable to sd or not through code. I know how to get the installed applications list and is it on sd card or not.
If ApplicationInfo flags contains ApplicationInfo.FLAG_EXTERNAL_STORAGE then it is on sd card but how can I check whether the app is movable to sd or not.
My first guess is too look into Android Source code for InstalledAppDetails
activity. This is activity which shows "Move to phone" and "Move to SD card" buttons. It has interesting function called initMoveButton
:
private void initMoveButton() {
String pkgName = mAppInfo.packageName;
boolean dataOnly = false;
ApplicationInfo info1 = null;
PackageInfo pkgInfo = null;
try {
info1 = mPm.getApplicationInfo(pkgName, 0);
pkgInfo = mPm.getPackageInfo(mAppInfo.packageName,
PackageManager.GET_UNINSTALLED_PACKAGES);
} catch (NameNotFoundException e) {
}
dataOnly = (info1 == null) && (mAppInfo != null);
boolean moveDisable = true;
if (dataOnly) {
mMoveAppButton.setText(R.string.move_app);
} else if ((mAppInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
mMoveAppButton.setText(R.string.move_app_to_internal);
// Always let apps move to internal storage from sdcard.
moveDisable = false;
} else {
mMoveAppButton.setText(R.string.move_app_to_sdcard);
if ((mAppInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0 &&
(mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0 &&
pkgInfo != null) {
if (pkgInfo.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL ||
pkgInfo.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
moveDisable = false;
} else if (pkgInfo.installLocation == PackageInfo.INSTALL_LOCATION_UNSPECIFIED) {
IPackageManager ipm = IPackageManager.Stub.asInterface(
ServiceManager.getService("package"));
int loc;
try {
loc = ipm.getInstallLocation();
} catch (RemoteException e) {
Log.e(TAG, "Is Pakage Manager running?");
return;
}
if (loc == PackageHelper.APP_INSTALL_EXTERNAL) {
// For apps with no preference and the default value set
// to install on sdcard.
moveDisable = false;
}
}
}
}
if (moveDisable) {
mMoveAppButton.setEnabled(false);
} else {
mMoveAppButton.setOnClickListener(this);
mMoveAppButton.setEnabled(true);
}
}
Though not all classes used in this functions are available through public API. Specifically PackageHelper
is internal class.
You can get all aplications filter by user and try if packetInfo set filter 'INSTALL_LOCATION_INTERNAL_ONLY'
List<ApplicationInfo> packagesAll = pm.getInstalledApplications(PackageManager.GET_META_DATA);
List<ApplicationInfo> packagesByUser = Stream.of(packagesAll)
.filter((p -> (p.flags & ApplicationInfo.FLAG_SYSTEM) == 0))
.filter((this::tryMoveSDPackage))
.map(ApplicationInfo::new)
.collect(Collectors.toList());
private boolean tryMoveSDPackage(ApplicationInfo applicationInfo) {
try {
PackageInfo packageInfo = getPackageManager().getPackageInfo(applicationInfo.packageName, 0);
if (packageInfo.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
//Log.i(TAG, "INSTALL_LOCATION_AUTO");
return true;
} else if (packageInfo.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
//Log.d(TAG, "INSTALL_LOCATION_PREFER_EXTERNAL");
return true;
} else {
//Log.w(TAG, "INSTALL_LOCATION_INTERNAL_ONLY");
return false;
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return false;
}
}
来源:https://stackoverflow.com/questions/6141592/how-can-i-check-the-app-is-movable-to-sd-or-not