Showing icons for apps on sdcard which are not available during system boot.

issue 15852084

Change-Id: I1e6f59a413581ae3af4219ab32cb5af3d726d382
This commit is contained in:
Sunny Goyal 2014-07-08 13:01:29 -07:00
parent bb474f6df0
commit f599ccfe96
3 changed files with 121 additions and 29 deletions

View File

@ -63,6 +63,8 @@
<uses-permission android:name="android.permission.BIND_APPWIDGET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
<uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" />
@ -206,6 +208,12 @@
</intent-filter>
</receiver>
<receiver android:name="com.android.launcher3.StartupReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- The settings provider contains Home's data, like the workspace favorites -->
<provider
android:name="com.android.launcher3.LauncherProvider"

View File

@ -19,12 +19,19 @@ package com.android.launcher3;
import android.app.SearchManager;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.*;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProviderClient;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.Intent.ShortcutIconResource;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
@ -45,11 +52,11 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import com.android.launcher3.InstallWidgetReceiver.WidgetMimeTypeHandlerData;
import com.android.launcher3.compat.LauncherActivityInfoCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.InstallWidgetReceiver.WidgetMimeTypeHandlerData;
import java.lang.ref.WeakReference;
import java.net.URISyntaxException;
@ -63,6 +70,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicBoolean;
@ -157,6 +165,9 @@ public class LauncherModel extends BroadcastReceiver
// sBgWorkspaceScreens is the ordered set of workspace screens.
static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
// sPendingPackages is a set of packages which could be on sdcard and are not available yet
static final HashMap<UserHandleCompat, HashSet<String>> sPendingPackages = new HashMap<>();
// </ only access in worker thread >
private IconCache mIconCache;
@ -1826,6 +1837,9 @@ public class LauncherModel extends BroadcastReceiver
final PackageManager manager = context.getPackageManager();
final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
final boolean isSafeMode = manager.isSafeMode();
final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
final boolean isSdCardReady = context.registerReceiver(null,
new IntentFilter(StartupReceiver.SYESTEM_READY)) != null;
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
@ -1919,6 +1933,7 @@ public class LauncherModel extends BroadcastReceiver
try {
int itemType = c.getInt(itemTypeIndex);
boolean restored = 0 != c.getInt(restoredIndex);
boolean allowMissingTarget = false;
switch (itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
@ -1935,30 +1950,51 @@ public class LauncherModel extends BroadcastReceiver
try {
intent = Intent.parseUri(intentDescription, 0);
ComponentName cn = intent.getComponent();
if (cn != null && !isValidPackageActivity(context, cn, user)) {
if (restored) {
// might be installed later
if (cn != null && cn.getPackageName() != null) {
boolean validPkg = launcherApps.isPackageEnabledForProfile(
cn.getPackageName(), user);
boolean validComponent = validPkg &&
launcherApps.isActivityEnabledForProfile(cn, user);
if (validComponent) {
if (restored) {
// no special handling necessary for this item
restoredRows.add(id);
restored = false;
}
} else if (validPkg) {
// The app is installed but the component is no
// longer available.
Launcher.addDumpLog(TAG,
"Invalid component removed: " + cn, true);
itemsToRemove.add(id);
continue;
} else if (restored) {
// Package is not yet available but might be
// installed later.
Launcher.addDumpLog(TAG,
"package not yet restored: " + cn, true);
} else {
if (!mAppsCanBeOnRemoveableStorage) {
// Log the invalid package, and remove it
Launcher.addDumpLog(TAG,
"Invalid package removed: " + cn, true);
itemsToRemove.add(id);
} else {
// If apps can be on external storage, then we just
// leave them for the user to remove (maybe add
// visual treatment to it)
Launcher.addDumpLog(TAG,
"Invalid package found: " + cn, true);
}
} else if (isSdCardReady) {
// Do not wait for external media load anymore.
// Log the invalid package, and remove it
Launcher.addDumpLog(TAG,
"Invalid package removed: " + cn, true);
itemsToRemove.add(id);
continue;
} else {
// SdCard is not ready yet. Package might get available,
// once it is ready.
Launcher.addDumpLog(TAG, "Invalid package: " + cn
+ " (check again later)", true);
HashSet<String> pkgs = sPendingPackages.get(user);
if (pkgs == null) {
pkgs = new HashSet<>();
sPendingPackages.put(user, pkgs);
}
pkgs.add(cn.getPackageName());
allowMissingTarget = true;
// Add the icon on the workspace anyway.
}
} else if (restored) {
// no special handling necessary for this restored item
restoredRows.add(id);
restored = false;
}
} catch (URISyntaxException e) {
Launcher.addDumpLog(TAG,
@ -1980,8 +2016,8 @@ public class LauncherModel extends BroadcastReceiver
}
} else if (itemType ==
LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
info = getShortcutInfo(manager, intent, user, context, c, iconIndex,
titleIndex, mLabelCache);
info = getShortcutInfo(manager, intent, user, context, c,
iconIndex, titleIndex, mLabelCache, allowMissingTarget);
} else {
info = getShortcutInfo(c, context, iconTypeIndex,
iconPackageIndex, iconResourceIndex, iconIndex,
@ -2198,6 +2234,12 @@ public class LauncherModel extends BroadcastReceiver
}
}
if (!isSdCardReady && !sPendingPackages.isEmpty()) {
context.registerReceiver(new AppsAvailabilityCheck(),
new IntentFilter(StartupReceiver.SYESTEM_READY),
null, sWorker);
}
if (loadedOldDb) {
long maxScreenId = 0;
// If we're importing we use the old screen order.
@ -2743,6 +2785,33 @@ public class LauncherModel extends BroadcastReceiver
sWorker.post(task);
}
private class AppsAvailabilityCheck extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (sBgLock) {
final LauncherAppsCompat launcherApps = LauncherAppsCompat
.getInstance(mApp.getContext());
ArrayList<String> packagesRemoved;
for (Entry<UserHandleCompat, HashSet<String>> entry : sPendingPackages.entrySet()) {
UserHandleCompat user = entry.getKey();
packagesRemoved = new ArrayList<>();
for (String pkg : entry.getValue()) {
if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
Launcher.addDumpLog(TAG, "Package not found: " + pkg, true);
packagesRemoved.add(pkg);
}
}
if (!packagesRemoved.isEmpty()) {
enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE,
packagesRemoved.toArray(new String[packagesRemoved.size()]), user));
}
}
sPendingPackages.clear();
}
}
}
private class PackageUpdatedTask implements Runnable {
int mOp;
String[] mPackages;
@ -3006,7 +3075,7 @@ public class LauncherModel extends BroadcastReceiver
*/
public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
UserHandleCompat user, Context context) {
return getShortcutInfo(manager, intent, user, context, null, -1, -1, null);
return getShortcutInfo(manager, intent, user, context, null, -1, -1, null, false);
}
/**
@ -3016,7 +3085,7 @@ public class LauncherModel extends BroadcastReceiver
*/
public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex,
HashMap<Object, CharSequence> labelCache) {
HashMap<Object, CharSequence> labelCache, boolean allowMissingTarget) {
if (user == null) {
Log.d(TAG, "Null user found in getShortcutInfo");
return null;
@ -3032,7 +3101,7 @@ public class LauncherModel extends BroadcastReceiver
newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
newIntent.setComponent(componentName);
LauncherActivityInfoCompat lai = mLauncherApps.resolveActivity(newIntent, user);
if (lai == null) {
if ((lai == null) && !allowMissingTarget) {
Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
return null;
}

View File

@ -0,0 +1,15 @@
package com.android.launcher3;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class StartupReceiver extends BroadcastReceiver {
static final String SYESTEM_READY = "com.android.launcher3.SYESTEM_READY";
@Override
public void onReceive(Context context, Intent intent) {
context.sendStickyBroadcast(new Intent(SYESTEM_READY));
}
}