Adding shortcuts corresponding to ManagedUsers automatically.
Bug: 16188104 Change-Id: Ic07578dd187263f59f3c431cbb78dea90d0c24f4
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user