Adding shortcuts corresponding to ManagedUsers automatically.

Bug: 16188104
Change-Id: Ic07578dd187263f59f3c431cbb78dea90d0c24f4
This commit is contained in:
Sunny Goyal
2014-11-10 18:05:31 -08:00
parent b155bbb375
commit e0f58d7364
4 changed files with 332 additions and 217 deletions
+12 -7
View File
@@ -84,16 +84,11 @@ public class AppInfo extends ItemInfo {
flags = initFlags(info);
firstInstallTime = info.getFirstInstallTime();
iconCache.getTitleAndIcon(this, info, labelCache);
intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setComponent(info.getComponentName());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user);
intent.putExtra(EXTRA_PROFILE, serialNumber);
intent = makeLaunchIntent(context, info, user);
this.user = user;
}
private static int initFlags(LauncherActivityInfoCompat info) {
public static int initFlags(LauncherActivityInfoCompat info) {
int appFlags = info.getApplicationInfo().flags;
int flags = 0;
if ((appFlags & android.content.pm.ApplicationInfo.FLAG_SYSTEM) == 0) {
@@ -137,4 +132,14 @@ public class AppInfo extends ItemInfo {
public ShortcutInfo makeShortcut() {
return new ShortcutInfo(this);
}
public static Intent makeLaunchIntent(Context context, LauncherActivityInfoCompat info,
UserHandleCompat user) {
long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user);
return new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER)
.setComponent(info.getComponentName())
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
.putExtra(EXTRA_PROFILE, serialNumber);
}
}
@@ -27,14 +27,18 @@ import android.graphics.BitmapFactory;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import android.widget.Toast;
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 org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONStringer;
import org.json.JSONTokener;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
@@ -47,73 +51,49 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
private static final String ACTION_INSTALL_SHORTCUT =
"com.android.launcher.action.INSTALL_SHORTCUT";
private static final String DATA_INTENT_KEY = "intent.data";
private static final String LAUNCH_INTENT_KEY = "intent.launch";
private static final String NAME_KEY = "name";
private static final String ICON_KEY = "icon";
private static final String ICON_RESOURCE_NAME_KEY = "iconResource";
private static final String ICON_RESOURCE_PACKAGE_NAME_KEY = "iconResourcePackage";
private static final String APP_SHORTCUT_TYPE_KEY = "isAppShortcut";
private static final String USER_HANDLE_KEY = "userHandle";
// The set of shortcuts that are pending install
private static final String APPS_PENDING_INSTALL = "apps_to_install";
public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450;
public static final int NEW_SHORTCUT_STAGGER_DELAY = 85;
private static final int INSTALL_SHORTCUT_SUCCESSFUL = 0;
private static final int INSTALL_SHORTCUT_IS_DUPLICATE = -1;
private static Object sLock = new Object();
private static void addToStringSet(SharedPreferences sharedPrefs,
SharedPreferences.Editor editor, String key, String value) {
Set<String> strings = sharedPrefs.getStringSet(key, null);
if (strings == null) {
strings = new HashSet<String>(1);
} else {
strings = new HashSet<String>(strings);
}
strings.add(value);
editor.putStringSet(key, strings);
}
private static final Object sLock = new Object();
private static void addToInstallQueue(
SharedPreferences sharedPrefs, PendingInstallShortcutInfo info) {
synchronized(sLock) {
try {
JSONStringer json = new JSONStringer()
.object()
.key(DATA_INTENT_KEY).value(info.data.toUri(0))
.key(LAUNCH_INTENT_KEY).value(info.launchIntent.toUri(0))
.key(NAME_KEY).value(info.name);
if (info.icon != null) {
byte[] iconByteArray = ItemInfo.flattenBitmap(info.icon);
json = json.key(ICON_KEY).value(
Base64.encodeToString(
iconByteArray, 0, iconByteArray.length, Base64.DEFAULT));
String encoded = info.encodeToString();
if (encoded != null) {
Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null);
if (strings == null) {
strings = new HashSet<String>(1);
} else {
strings = new HashSet<String>(strings);
}
if (info.iconResource != null) {
json = json.key(ICON_RESOURCE_NAME_KEY).value(info.iconResource.resourceName);
json = json.key(ICON_RESOURCE_PACKAGE_NAME_KEY)
.value(info.iconResource.packageName);
}
json = json.endObject();
SharedPreferences.Editor editor = sharedPrefs.edit();
if (DBG) Log.d(TAG, "Adding to APPS_PENDING_INSTALL: " + json);
addToStringSet(sharedPrefs, editor, APPS_PENDING_INSTALL, json.toString());
editor.commit();
} catch (org.json.JSONException e) {
Log.d(TAG, "Exception when adding shortcut: " + e);
strings.add(encoded);
sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).commit();
}
}
}
public static void removeFromInstallQueue(SharedPreferences sharedPrefs,
ArrayList<String> packageNames) {
public static void removeFromInstallQueue(Context context, ArrayList<String> packageNames,
UserHandleCompat user) {
if (packageNames.isEmpty()) {
return;
}
String spKey = LauncherAppState.getSharedPreferencesKey();
SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
synchronized(sLock) {
Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null);
Set<String> strings = sp.getStringSet(APPS_PENDING_INSTALL, null);
if (DBG) {
Log.d(TAG, "APPS_PENDING_INSTALL: " + strings
+ ", removing packages: " + packageNames);
@@ -122,34 +102,20 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
Set<String> newStrings = new HashSet<String>(strings);
Iterator<String> newStringsIter = newStrings.iterator();
while (newStringsIter.hasNext()) {
String json = newStringsIter.next();
try {
JSONObject object = (JSONObject) new JSONTokener(json).nextValue();
Intent launchIntent = Intent.parseUri(object.getString(LAUNCH_INTENT_KEY), 0);
String pn = launchIntent.getPackage();
if (pn == null) {
if (launchIntent.getComponent() == null) {
continue;
}
pn = launchIntent.getComponent().getPackageName();
}
if (packageNames.contains(pn)) {
newStringsIter.remove();
}
} catch (org.json.JSONException e) {
Log.d(TAG, "Exception reading shortcut to remove: " + e);
} catch (java.net.URISyntaxException e) {
Log.d(TAG, "Exception reading shortcut to remove: " + e);
String encoded = newStringsIter.next();
PendingInstallShortcutInfo info = decode(encoded, context);
if (info == null || (packageNames.contains(info.getTargetPackage())
&& user.equals(info.user))) {
newStringsIter.remove();
}
}
sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL,
new HashSet<String>(newStrings)).commit();
sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).commit();
}
}
}
private static ArrayList<PendingInstallShortcutInfo> getAndClearInstallQueue(
SharedPreferences sharedPrefs) {
SharedPreferences sharedPrefs, Context context) {
synchronized(sLock) {
Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null);
if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings);
@@ -158,36 +124,10 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
}
ArrayList<PendingInstallShortcutInfo> infos =
new ArrayList<PendingInstallShortcutInfo>();
for (String json : strings) {
try {
JSONObject object = (JSONObject) new JSONTokener(json).nextValue();
Intent data = Intent.parseUri(object.getString(DATA_INTENT_KEY), 0);
Intent launchIntent =
Intent.parseUri(object.getString(LAUNCH_INTENT_KEY), 0);
String name = object.getString(NAME_KEY);
String iconBase64 = object.optString(ICON_KEY);
String iconResourceName = object.optString(ICON_RESOURCE_NAME_KEY);
String iconResourcePackageName =
object.optString(ICON_RESOURCE_PACKAGE_NAME_KEY);
if (iconBase64 != null && !iconBase64.isEmpty()) {
byte[] iconArray = Base64.decode(iconBase64, Base64.DEFAULT);
Bitmap b = BitmapFactory.decodeByteArray(iconArray, 0, iconArray.length);
data.putExtra(Intent.EXTRA_SHORTCUT_ICON, b);
} else if (iconResourceName != null && !iconResourceName.isEmpty()) {
Intent.ShortcutIconResource iconResource =
new Intent.ShortcutIconResource();
iconResource.resourceName = iconResourceName;
iconResource.packageName = iconResourcePackageName;
data.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
}
data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, launchIntent);
PendingInstallShortcutInfo info =
new PendingInstallShortcutInfo(data, name, launchIntent);
for (String encoded : strings) {
PendingInstallShortcutInfo info = decode(encoded, context);
if (info != null) {
infos.add(info);
} catch (org.json.JSONException e) {
Log.d(TAG, "Exception reading shortcut to add: " + e);
} catch (java.net.URISyntaxException e) {
Log.d(TAG, "Exception reading shortcut to add: " + e);
}
}
sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, new HashSet<String>()).commit();
@@ -199,49 +139,26 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
// processAllPendingInstalls() is called.
private static boolean mUseInstallQueue = false;
private static class PendingInstallShortcutInfo {
Intent data;
Intent launchIntent;
String name;
Bitmap icon;
Intent.ShortcutIconResource iconResource;
public PendingInstallShortcutInfo(Intent rawData, String shortcutName,
Intent shortcutIntent) {
data = rawData;
name = shortcutName;
launchIntent = shortcutIntent;
}
}
public void onReceive(Context context, Intent data) {
if (!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) {
return;
}
if (DBG) Log.d(TAG, "Got INSTALL_SHORTCUT: " + data.toUri(0));
PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, context);
Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
if (intent == null) {
return;
}
queuePendingShortcutInfo(info, context);
}
// This name is only used for comparisons and notifications, so fall back to activity name
// if not supplied
String name = ensureValidName(context, intent,
data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME)).toString();
Bitmap icon = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
Intent.ShortcutIconResource iconResource =
data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
static void queueInstallShortcut(LauncherActivityInfoCompat info, Context context) {
queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, context), context);
}
private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) {
// Queue the item up for adding if launcher has not loaded properly yet
LauncherAppState.setApplicationContext(context.getApplicationContext());
LauncherAppState app = LauncherAppState.getInstance();
boolean launcherNotLoaded = (app.getDynamicGrid() == null);
PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, name, intent);
info.icon = icon;
info.iconResource = iconResource;
boolean launcherNotLoaded = app.getModel().getCallback() == null;
String spKey = LauncherAppState.getSharedPreferencesKey();
SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
@@ -261,33 +178,22 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
static void flushInstallQueue(Context context) {
String spKey = LauncherAppState.getSharedPreferencesKey();
SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
ArrayList<PendingInstallShortcutInfo> installQueue = getAndClearInstallQueue(sp);
ArrayList<PendingInstallShortcutInfo> installQueue = getAndClearInstallQueue(sp, context);
if (!installQueue.isEmpty()) {
Iterator<PendingInstallShortcutInfo> iter = installQueue.iterator();
ArrayList<ItemInfo> addShortcuts = new ArrayList<ItemInfo>();
int result = INSTALL_SHORTCUT_SUCCESSFUL;
String duplicateName = "";
while (iter.hasNext()) {
final PendingInstallShortcutInfo pendingInfo = iter.next();
//final Intent data = pendingInfo.data;
final Intent intent = pendingInfo.launchIntent;
final String name = pendingInfo.name;
if (LauncherAppState.isDisableAllApps() && !isValidShortcutLaunchIntent(intent)) {
if (DBG) Log.d(TAG, "Ignoring shortcut with launchIntent:" + intent);
continue;
}
final boolean exists = LauncherModel.shortcutExists(context, name, intent);
//final boolean allowDuplicate = data.getBooleanExtra(Launcher.EXTRA_SHORTCUT_DUPLICATE, true);
// If the intent specifies a package, make sure the package exists
String packageName = intent.getPackage();
if (packageName == null) {
packageName = intent.getComponent() == null ? null :
intent.getComponent().getPackageName();
}
if (packageName != null && !packageName.isEmpty()) {
String packageName = pendingInfo.getTargetPackage();
if (TextUtils.isEmpty(packageName)) {
UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle();
if (!LauncherModel.isValidPackage(context, packageName, myUserHandle)) {
if (DBG) Log.d(TAG, "Ignoring shortcut for absent package:" + intent);
@@ -295,19 +201,12 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
}
}
final boolean exists = LauncherModel.shortcutExists(context, pendingInfo.label,
intent, pendingInfo.user);
if (!exists) {
// Generate a shortcut info to add into the model
ShortcutInfo info = getShortcutInfo(context, pendingInfo.data,
pendingInfo.launchIntent);
addShortcuts.add(info);
addShortcuts.add(pendingInfo.getShortcutInfo());
}
}
// Notify the user once if we weren't able to place any duplicates
if (result == INSTALL_SHORTCUT_IS_DUPLICATE) {
Toast.makeText(context, context.getString(R.string.shortcut_duplicate,
duplicateName), Toast.LENGTH_SHORT).show();
}
// Add the new apps to the model and bind them
@@ -342,22 +241,6 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
return true;
}
private static ShortcutInfo getShortcutInfo(Context context, Intent data,
Intent launchIntent) {
if (launchIntent.getAction() == null) {
launchIntent.setAction(Intent.ACTION_VIEW);
} else if (launchIntent.getAction().equals(Intent.ACTION_MAIN) &&
launchIntent.getCategories() != null &&
launchIntent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
launchIntent.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
}
LauncherAppState app = LauncherAppState.getInstance();
ShortcutInfo info = app.getModel().infoFromShortcutIntent(context, data);
info.title = ensureValidName(context, launchIntent, info.title);
return info;
}
/**
* Ensures that we have a valid, non-null name. If the provided name is null, we will return
* the application name instead.
@@ -374,4 +257,168 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
}
return name;
}
private static class PendingInstallShortcutInfo {
final LauncherActivityInfoCompat activityInfo;
final Intent data;
final Context mContext;
final Intent launchIntent;
final String label;
final UserHandleCompat user;
/**
* Initializes a PendingInstallShortcutInfo received from a different app.
*/
public PendingInstallShortcutInfo(Intent data, Context context) {
this.data = data;
mContext = context;
launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
user = UserHandleCompat.myUserHandle();
activityInfo = null;
}
/**
* Initializes a PendingInstallShortcutInfo to represent a launcher target.
*/
public PendingInstallShortcutInfo(LauncherActivityInfoCompat info, Context context) {
this.data = null;
mContext = context;
activityInfo = info;
user = info.getUser();
launchIntent = AppInfo.makeLaunchIntent(context, info, user);
label = info.getLabel().toString();
}
public String encodeToString() {
if (activityInfo != null) {
try {
// If it a launcher target, we only need component name, and user to
// recreate this.
return new JSONStringer()
.object()
.key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
.key(APP_SHORTCUT_TYPE_KEY).value(true)
.key(USER_HANDLE_KEY).value(UserManagerCompat.getInstance(mContext)
.getSerialNumberForUser(user))
.endObject().toString();
} catch (JSONException e) {
Log.d(TAG, "Exception when adding shortcut: " + e);
return null;
}
}
if (launchIntent.getAction() == null) {
launchIntent.setAction(Intent.ACTION_VIEW);
} else if (launchIntent.getAction().equals(Intent.ACTION_MAIN) &&
launchIntent.getCategories() != null &&
launchIntent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
launchIntent.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
}
// This name is only used for comparisons and notifications, so fall back to activity
// name if not supplied
String name = ensureValidName(mContext, launchIntent, label).toString();
Bitmap icon = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
Intent.ShortcutIconResource iconResource =
data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
// Only encode the parameters which are supported by the API.
try {
JSONStringer json = new JSONStringer()
.object()
.key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
.key(NAME_KEY).value(name);
if (icon != null) {
byte[] iconByteArray = ItemInfo.flattenBitmap(icon);
json = json.key(ICON_KEY).value(
Base64.encodeToString(
iconByteArray, 0, iconByteArray.length, Base64.DEFAULT));
}
if (iconResource != null) {
json = json.key(ICON_RESOURCE_NAME_KEY).value(iconResource.resourceName);
json = json.key(ICON_RESOURCE_PACKAGE_NAME_KEY)
.value(iconResource.packageName);
}
return json.endObject().toString();
} catch (JSONException e) {
Log.d(TAG, "Exception when adding shortcut: " + e);
}
return null;
}
public ShortcutInfo getShortcutInfo() {
if (activityInfo != null) {
final ShortcutInfo info = new ShortcutInfo();
info.user = user;
info.title = label;
info.contentDescription = label;
info.customIcon = false;
info.intent = launchIntent;
info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
info.flags = AppInfo.initFlags(activityInfo);
info.firstInstallTime = activityInfo.getFirstInstallTime();
return info;
} else {
return LauncherAppState.getInstance().getModel().infoFromShortcutIntent(mContext, data);
}
}
public String getTargetPackage() {
String packageName = launchIntent.getPackage();
if (packageName == null) {
packageName = launchIntent.getComponent() == null ? null :
launchIntent.getComponent().getPackageName();
}
return packageName;
}
}
private static PendingInstallShortcutInfo decode(String encoded, Context context) {
try {
JSONObject object = (JSONObject) new JSONTokener(encoded).nextValue();
Intent launcherIntent = Intent.parseUri(object.getString(LAUNCH_INTENT_KEY), 0);
if (object.optBoolean(APP_SHORTCUT_TYPE_KEY)) {
// The is an internal launcher target shortcut.
UserHandleCompat user = UserManagerCompat.getInstance(context)
.getUserForSerialNumber(object.getLong(USER_HANDLE_KEY));
LauncherActivityInfoCompat info = LauncherAppsCompat.getInstance(context)
.resolveActivity(launcherIntent, user);
return info == null ? null : new PendingInstallShortcutInfo(info, context);
}
Intent data = new Intent();
data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, launcherIntent);
data.putExtra(Intent.EXTRA_SHORTCUT_NAME, object.getString(NAME_KEY));
String iconBase64 = object.optString(ICON_KEY);
String iconResourceName = object.optString(ICON_RESOURCE_NAME_KEY);
String iconResourcePackageName = object.optString(ICON_RESOURCE_PACKAGE_NAME_KEY);
if (iconBase64 != null && !iconBase64.isEmpty()) {
byte[] iconArray = Base64.decode(iconBase64, Base64.DEFAULT);
Bitmap b = BitmapFactory.decodeByteArray(iconArray, 0, iconArray.length);
data.putExtra(Intent.EXTRA_SHORTCUT_ICON, b);
} else if (iconResourceName != null && !iconResourceName.isEmpty()) {
Intent.ShortcutIconResource iconResource =
new Intent.ShortcutIconResource();
iconResource.resourceName = iconResourceName;
iconResource.packageName = iconResourcePackageName;
data.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
}
return new PendingInstallShortcutInfo(data, context);
} catch (JSONException e) {
Log.d(TAG, "Exception reading shortcut to add: " + e);
} catch (URISyntaxException e) {
Log.d(TAG, "Exception reading shortcut to add: " + e);
}
return null;
}
}
@@ -46,12 +46,14 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
private final AppFilter mAppFilter;
private final BuildInfo mBuildInfo;
private LauncherModel mModel;
private IconCache mIconCache;
private final LauncherModel mModel;
private final IconCache mIconCache;
private final boolean mIsScreenLarge;
private final float mScreenDensity;
private final int mLongPressTimeout = 300;
private WidgetPreviewLoader.CacheDb mWidgetPreviewCacheDb;
private boolean mIsScreenLarge;
private float mScreenDensity;
private int mLongPressTimeout = 300;
private boolean mWallpaperChangedSinceLastCheck;
private static WeakReference<LauncherProvider> sLauncherProvider;
@@ -159,9 +161,6 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
};
LauncherModel setLauncher(Launcher launcher) {
if (mModel == null) {
throw new IllegalStateException("setLauncher() called before init()");
}
mModel.initialize(launcher);
return mModel;
}
+101 -37
View File
@@ -30,6 +30,7 @@ import android.content.Intent;
import android.content.Intent.ShortcutIconResource;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.LauncherApps.Callback;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
@@ -75,7 +76,6 @@ import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Maintains in-memory state of the Launcher. It is expected that there should be only one
@@ -110,6 +110,11 @@ public class LauncherModel extends BroadcastReceiver
private boolean mIsLoaderTaskRunning;
private volatile boolean mFlushingWorkerThread;
/**
* Maintain a set of packages per user, for which we added a shortcut on the workspace.
*/
private static final String INSTALLED_SHORTCUTS_SET_PREFIX = "installed_shortcuts_set_for_user_";
// Specific runnable types that are run on the main thread deferred handler, this allows us to
// clear all queued binding runnables when the Launcher activity is destroyed.
private static final int MAIN_THREAD_NORMAL_RUNNABLE = 0;
@@ -343,7 +348,7 @@ public class LauncherModel extends BroadcastReceiver
// Process the updated package state
Runnable r = new Runnable() {
public void run() {
Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
Callbacks callbacks = getCallback();
if (callbacks != null) {
callbacks.updatePackageState(installInfo);
}
@@ -356,7 +361,7 @@ public class LauncherModel extends BroadcastReceiver
// Process the updated package badge
Runnable r = new Runnable() {
public void run() {
Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
Callbacks callbacks = getCallback();
if (callbacks != null) {
callbacks.updatePackageBadge(packageName);
}
@@ -366,7 +371,7 @@ public class LauncherModel extends BroadcastReceiver
}
public void addAppsToAllApps(final Context ctx, final ArrayList<AppInfo> allAppsApps) {
final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
final Callbacks callbacks = getCallback();
if (allAppsApps == null) {
throw new RuntimeException("allAppsApps must not be null");
@@ -380,7 +385,7 @@ public class LauncherModel extends BroadcastReceiver
public void run() {
runOnMainThread(new Runnable() {
public void run() {
Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
Callbacks cb = getCallback();
if (callbacks == cb && cb != null) {
callbacks.bindAppsAdded(null, null, null, allAppsApps);
}
@@ -393,7 +398,7 @@ public class LauncherModel extends BroadcastReceiver
public void addAndBindAddedWorkspaceApps(final Context context,
final ArrayList<ItemInfo> workspaceApps) {
final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
final Callbacks callbacks = getCallback();
if (workspaceApps == null) {
throw new RuntimeException("workspaceApps and allAppsApps must not be null");
@@ -425,7 +430,7 @@ public class LauncherModel extends BroadcastReceiver
final Intent launchIntent = a.getIntent();
// Short-circuit this logic if the icon exists somewhere on the workspace
if (shortcutExists(context, name, launchIntent)) {
if (shortcutExists(context, name, launchIntent, a.user)) {
continue;
}
@@ -484,7 +489,7 @@ public class LauncherModel extends BroadcastReceiver
if (!addedShortcutsFinal.isEmpty()) {
runOnMainThread(new Runnable() {
public void run() {
Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
Callbacks cb = getCallback();
if (callbacks == cb && cb != null) {
final ArrayList<ItemInfo> addAnimated = new ArrayList<ItemInfo>();
final ArrayList<ItemInfo> addNotAnimated = new ArrayList<ItemInfo>();
@@ -844,7 +849,8 @@ public class LauncherModel extends BroadcastReceiver
* Returns true if the shortcuts already exists in the database.
* we identify a shortcut by its title and intent.
*/
static boolean shortcutExists(Context context, String title, Intent intent) {
static boolean shortcutExists(Context context, String title, Intent intent,
UserHandleCompat user) {
final ContentResolver cr = context.getContentResolver();
final Intent intentWithPkg, intentWithoutPkg;
@@ -863,16 +869,18 @@ public class LauncherModel extends BroadcastReceiver
intentWithPkg = intent;
intentWithoutPkg = intent;
}
String userSerial = Long.toString(UserManagerCompat.getInstance(context)
.getSerialNumberForUser(user));
Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
new String[] { "title", "intent" }, "title=? and (intent=? or intent=?)",
new String[] { title, intentWithPkg.toUri(0), intentWithoutPkg.toUri(0) }, null);
boolean result = false;
new String[] { "title", "intent", "profileId" },
"title=? and (intent=? or intent=?) and profileId=?",
new String[] { title, intentWithPkg.toUri(0), intentWithoutPkg.toUri(0), userSerial },
null);
try {
result = c.moveToFirst();
return c.moveToFirst();
} finally {
c.close();
}
return result;
}
/**
@@ -1285,11 +1293,9 @@ public class LauncherModel extends BroadcastReceiver
mPreviousConfigMcc = currentConfig.mcc;
} else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action) ||
SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED.equals(action)) {
if (mCallbacks != null) {
Callbacks callbacks = mCallbacks.get();
if (callbacks != null) {
callbacks.bindSearchablesChanged();
}
Callbacks callbacks = getCallback();
if (callbacks != null) {
callbacks.bindSearchablesChanged();
}
}
}
@@ -1321,13 +1327,11 @@ public class LauncherModel extends BroadcastReceiver
*/
public void startLoaderFromBackground() {
boolean runLoader = false;
if (mCallbacks != null) {
Callbacks callbacks = mCallbacks.get();
if (callbacks != null) {
// Only actually run the loader if they're not paused.
if (!callbacks.setLoadOnResume()) {
runLoader = true;
}
Callbacks callbacks = getCallback();
if (callbacks != null) {
// Only actually run the loader if they're not paused.
if (!callbacks.setLoadOnResume()) {
runLoader = true;
}
}
if (runLoader) {
@@ -2811,6 +2815,8 @@ public class LauncherModel extends BroadcastReceiver
// Clear the list of apps
mBgAllAppsList.clear();
SharedPreferences prefs = mContext.getSharedPreferences(
LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
for (UserHandleCompat user : profiles) {
// Query for the set of apps
final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
@@ -2821,6 +2827,7 @@ public class LauncherModel extends BroadcastReceiver
Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
}
// Fail if we don't have any apps
// TODO: Fix this. Only fail for the current user.
if (apps == null || apps.isEmpty()) {
return;
}
@@ -2839,6 +2846,25 @@ public class LauncherModel extends BroadcastReceiver
// This builds the icon bitmaps.
mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, mLabelCache));
}
if (!user.equals(UserHandleCompat.myUserHandle())) {
// Add shortcuts for packages which were installed while launcher was dead.
String shortcutsSetKey = INSTALLED_SHORTCUTS_SET_PREFIX
+ mUserManager.getSerialNumberForUser(user);
Set<String> packagesAdded = prefs.getStringSet(shortcutsSetKey, Collections.EMPTY_SET);
HashSet<String> newPackageSet = new HashSet<String>();
for (LauncherActivityInfoCompat info : apps) {
String packageName = info.getComponentName().getPackageName();
if (!packagesAdded.contains(packageName)
&& !newPackageSet.contains(packageName)) {
InstallShortcutReceiver.queueInstallShortcut(info, mContext);
}
newPackageSet.add(packageName);
}
prefs.edit().putStringSet(shortcutsSetKey, newPackageSet).commit();
}
}
// Huh? Shouldn't this be inside the Runnable below?
final ArrayList<AppInfo> added = mBgAllAppsList.added;
@@ -2953,6 +2979,30 @@ public class LauncherModel extends BroadcastReceiver
mIconCache.remove(packages[i], mUser);
mBgAllAppsList.addPackage(context, packages[i], mUser);
}
// Auto add shortcuts for added packages.
if (!UserHandleCompat.myUserHandle().equals(mUser)) {
SharedPreferences prefs = context.getSharedPreferences(
LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
String shortcutsSetKey = INSTALLED_SHORTCUTS_SET_PREFIX
+ mUserManager.getSerialNumberForUser(mUser);
Set<String> shortcutSet = new HashSet<String>(
prefs.getStringSet(shortcutsSetKey,Collections.EMPTY_SET));
for (int i=0; i<N; i++) {
if (!shortcutSet.contains(packages[i])) {
shortcutSet.add(packages[i]);
List<LauncherActivityInfoCompat> activities =
mLauncherApps.getActivityList(packages[i], mUser);
if (activities != null && !activities.isEmpty()) {
InstallShortcutReceiver.queueInstallShortcut(
activities.get(0), context);
}
}
}
prefs.edit().putStringSet(shortcutsSetKey, shortcutSet).commit();
}
break;
case OP_UPDATE:
for (int i=0; i<N; i++) {
@@ -2963,6 +3013,19 @@ public class LauncherModel extends BroadcastReceiver
}
break;
case OP_REMOVE:
// Remove the packageName for the set of auto-installed shortcuts. This
// will ensure that the shortcut when the app is installed again.
if (!UserHandleCompat.myUserHandle().equals(mUser)) {
SharedPreferences prefs = context.getSharedPreferences(
LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
String shortcutsSetKey = INSTALLED_SHORTCUTS_SET_PREFIX
+ mUserManager.getSerialNumberForUser(mUser);
HashSet<String> shortcutSet = new HashSet<String>(
prefs.getStringSet(shortcutsSetKey, Collections.EMPTY_SET));
shortcutSet.removeAll(Arrays.asList(mPackages));
prefs.edit().putStringSet(shortcutsSetKey, shortcutSet).commit();
}
// Fall through
case OP_UNAVAILABLE:
boolean clearCache = mOp == OP_REMOVE;
for (int i=0; i<N; i++) {
@@ -2991,7 +3054,7 @@ public class LauncherModel extends BroadcastReceiver
mBgAllAppsList.removed.clear();
}
final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
final Callbacks callbacks = getCallback();
if (callbacks == null) {
Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
return;
@@ -3021,7 +3084,7 @@ public class LauncherModel extends BroadcastReceiver
mHandler.post(new Runnable() {
public void run() {
Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
Callbacks cb = getCallback();
if (callbacks == cb && cb != null) {
callbacks.bindAppsUpdated(modifiedFinal);
}
@@ -3134,7 +3197,7 @@ public class LauncherModel extends BroadcastReceiver
mHandler.post(new Runnable() {
public void run() {
Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
Callbacks cb = getCallback();
if (callbacks == cb && cb != null) {
callbacks.bindShortcutsChanged(
updatedShortcuts, removedShortcuts, mUser);
@@ -3148,7 +3211,7 @@ public class LauncherModel extends BroadcastReceiver
if (!widgets.isEmpty()) {
mHandler.post(new Runnable() {
public void run() {
Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
Callbacks cb = getCallback();
if (callbacks == cb && cb != null) {
callbacks.bindWidgetsRestored(widgets);
}
@@ -3189,14 +3252,11 @@ public class LauncherModel extends BroadcastReceiver
}
// Remove any queued items from the install queue
String spKey = LauncherAppState.getSharedPreferencesKey();
SharedPreferences sp =
context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
InstallShortcutReceiver.removeFromInstallQueue(sp, removedPackageNames);
InstallShortcutReceiver.removeFromInstallQueue(context, removedPackageNames, mUser);
// Call the components-removed callback
mHandler.post(new Runnable() {
public void run() {
Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
Callbacks cb = getCallback();
if (callbacks == cb && cb != null) {
callbacks.bindComponentsRemoved(
removedPackageNames, removedApps, mUser, removeReason);
@@ -3210,7 +3270,7 @@ public class LauncherModel extends BroadcastReceiver
mHandler.post(new Runnable() {
@Override
public void run() {
Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
Callbacks cb = getCallback();
if (callbacks == cb && cb != null) {
callbacks.bindPackagesUpdated(widgetsAndShortcuts);
}
@@ -3220,7 +3280,7 @@ public class LauncherModel extends BroadcastReceiver
// Write all the logs to disk
mHandler.post(new Runnable() {
public void run() {
Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
Callbacks cb = getCallback();
if (callbacks == cb && cb != null) {
callbacks.dumpLogsToLocalData();
}
@@ -3754,4 +3814,8 @@ public class LauncherModel extends BroadcastReceiver
Log.d(TAG, "mLoaderTask=null");
}
}
public Callbacks getCallback() {
return mCallbacks != null ? mCallbacks.get() : null;
}
}