infoProvider,
+ UserHandle user, boolean usePackageIcon, boolean useLowResIcon) {
+ Preconditions.assertWorkerThread();
ComponentKey cacheKey = new ComponentKey(componentName, user);
CacheEntry entry = mCache.get(cacheKey);
if (entry == null || (entry.isLowResIcon && !useLowResIcon)) {
@@ -553,11 +529,17 @@ public class IconCache {
mCache.put(cacheKey, entry);
// Check the DB first.
+ LauncherActivityInfo info = null;
+ boolean providerFetchedOnce = false;
+
if (!getEntryFromDB(cacheKey, entry, useLowResIcon) || DEBUG_IGNORE_CACHE) {
+ info = infoProvider.get();
+ providerFetchedOnce = true;
+
if (info != null) {
- entry.icon = Utilities.createBadgedIconBitmap(
- mIconProvider.getIcon(info, mIconDpi), info.getUser(),
- mContext);
+ entry.icon = LauncherIcons.createBadgedIconBitmap(
+ getFullResIcon(info), info.getUser(), mContext,
+ infoProvider.get().getApplicationInfo().targetSdkVersion);
} else {
if (usePackageIcon) {
CacheEntry packageEntry = getEntryForPackageLocked(
@@ -578,19 +560,30 @@ public class IconCache {
}
}
- if (TextUtils.isEmpty(entry.title) && info != null) {
- entry.title = info.getLabel();
- entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
+ if (TextUtils.isEmpty(entry.title)) {
+ if (info == null && !providerFetchedOnce) {
+ info = infoProvider.get();
+ providerFetchedOnce = true;
+ }
+ if (info != null) {
+ entry.title = info.getLabel();
+ entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
+ }
}
}
return entry;
}
+ public synchronized void clear() {
+ Preconditions.assertWorkerThread();
+ mIconDb.clear();
+ }
+
/**
* Adds a default package entry in the cache. This entry is not persisted and will be removed
* when the cache is flushed.
*/
- public synchronized void cachePackageInstallInfo(String packageName, UserHandleCompat user,
+ public synchronized void cachePackageInstallInfo(String packageName, UserHandle user,
Bitmap icon, CharSequence title) {
removeFromMemCacheLocked(packageName, user);
@@ -606,11 +599,11 @@ public class IconCache {
entry.title = title;
}
if (icon != null) {
- entry.icon = Utilities.createIconBitmap(icon, mContext);
+ entry.icon = LauncherIcons.createIconBitmap(icon, mContext);
}
}
- private static ComponentKey getPackageKey(String packageName, UserHandleCompat user) {
+ private static ComponentKey getPackageKey(String packageName, UserHandle user) {
ComponentName cn = new ComponentName(packageName, packageName + EMPTY_CLASS_NAME);
return new ComponentKey(cn, user);
}
@@ -619,8 +612,9 @@ public class IconCache {
* Gets an entry for the package, which can be used as a fallback entry for various components.
* This method is not thread safe, it must be called from a synchronized method.
*/
- private CacheEntry getEntryForPackageLocked(String packageName, UserHandleCompat user,
+ private CacheEntry getEntryForPackageLocked(String packageName, UserHandle user,
boolean useLowResIcon) {
+ Preconditions.assertWorkerThread();
ComponentKey cacheKey = getPackageKey(packageName, user);
CacheEntry entry = mCache.get(cacheKey);
@@ -631,7 +625,7 @@ public class IconCache {
// Check the DB first.
if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) {
try {
- int flags = UserHandleCompat.myUserHandle().equals(user) ? 0 :
+ int flags = Process.myUserHandle().equals(user) ? 0 :
PackageManager.GET_UNINSTALLED_PACKAGES;
PackageInfo info = mPackageManager.getPackageInfo(packageName, flags);
ApplicationInfo appInfo = info.applicationInfo;
@@ -641,8 +635,8 @@ public class IconCache {
// Load the full res icon for the application, but if useLowResIcon is set, then
// only keep the low resolution icon instead of the larger full-sized icon
- Bitmap icon = Utilities.createBadgedIconBitmap(
- appInfo.loadIcon(mPackageManager), user, mContext);
+ Bitmap icon = LauncherIcons.createBadgedIconBitmap(
+ appInfo.loadIcon(mPackageManager), user, mContext, appInfo.targetSdkVersion);
Bitmap lowResIcon = generateLowResIcon(icon, mPackageBgColor);
entry.title = appInfo.loadLabel(mPackageManager);
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
@@ -670,37 +664,6 @@ public class IconCache {
return entry;
}
- /**
- * Pre-load an icon into the persistent cache.
- *
- * Queries for a component that does not exist in the package manager
- * will be answered by the persistent cache.
- *
- * @param componentName the icon should be returned for this component
- * @param icon the icon to be persisted
- * @param dpi the native density of the icon
- */
- public void preloadIcon(ComponentName componentName, Bitmap icon, int dpi, String label,
- long userSerial, InvariantDeviceProfile idp) {
- // TODO rescale to the correct native DPI
- try {
- PackageManager packageManager = mContext.getPackageManager();
- packageManager.getActivityIcon(componentName);
- // component is present on the system already, do nothing
- return;
- } catch (PackageManager.NameNotFoundException e) {
- // pass
- }
-
- icon = Bitmap.createScaledBitmap(icon, idp.iconBitmapSize, idp.iconBitmapSize, true);
- Bitmap lowResIcon = generateLowResIcon(icon, Color.TRANSPARENT);
- ContentValues values = newContentValues(icon, lowResIcon, label,
- componentName.getPackageName());
- values.put(IconDB.COLUMN_COMPONENT, componentName.flattenToString());
- values.put(IconDB.COLUMN_USER, userSerial);
- mIconDb.insertOrReplace(values);
- }
-
private boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) {
Cursor c = null;
try {
@@ -749,19 +712,19 @@ public class IconCache {
/**
* A runnable that updates invalid icons and adds missing icons in the DB for the provided
- * LauncherActivityInfoCompat list. Items are updated/added one at a time, so that the
+ * LauncherActivityInfo list. Items are updated/added one at a time, so that the
* worker thread doesn't get blocked.
*/
@Thunk class SerializedIconUpdateTask implements Runnable {
private final long mUserSerial;
private final HashMap mPkgInfoMap;
- private final Stack mAppsToAdd;
- private final Stack mAppsToUpdate;
+ private final Stack mAppsToAdd;
+ private final Stack mAppsToUpdate;
private final HashSet mUpdatedPackages = new HashSet();
@Thunk SerializedIconUpdateTask(long userSerial, HashMap pkgInfoMap,
- Stack appsToAdd,
- Stack appsToUpdate) {
+ Stack appsToAdd,
+ Stack appsToUpdate) {
mUserSerial = userSerial;
mPkgInfoMap = pkgInfoMap;
mAppsToAdd = appsToAdd;
@@ -771,31 +734,27 @@ public class IconCache {
@Override
public void run() {
if (!mAppsToUpdate.isEmpty()) {
- LauncherActivityInfoCompat app = mAppsToUpdate.pop();
+ LauncherActivityInfo app = mAppsToUpdate.pop();
String pkg = app.getComponentName().getPackageName();
PackageInfo info = mPkgInfoMap.get(pkg);
- if (info != null) {
- synchronized (IconCache.this) {
- ContentValues values = updateCacheAndGetContentValues(app, true);
- addIconToDB(values, app.getComponentName(), info, mUserSerial);
- }
- mUpdatedPackages.add(pkg);
- }
+ addIconToDBAndMemCache(app, info, mUserSerial, true /*replace existing*/);
+ mUpdatedPackages.add(pkg);
+
if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) {
// No more app to update. Notify model.
- LauncherAppState.getInstance().getModel().onPackageIconsUpdated(
+ LauncherAppState.getInstance(mContext).getModel().onPackageIconsUpdated(
mUpdatedPackages, mUserManager.getUserForSerialNumber(mUserSerial));
}
// Let it run one more time.
scheduleNext();
} else if (!mAppsToAdd.isEmpty()) {
- LauncherActivityInfoCompat app = mAppsToAdd.pop();
+ LauncherActivityInfo app = mAppsToAdd.pop();
PackageInfo info = mPkgInfoMap.get(app.getComponentName().getPackageName());
+ // We do not check the mPkgInfoMap when generating the mAppsToAdd. Although every
+ // app should have package info, this is not guaranteed by the api
if (info != null) {
- synchronized (IconCache.this) {
- addIconToDBAndMemCache(app, info, mUserSerial);
- }
+ addIconToDBAndMemCache(app, info, mUserSerial, false /*replace existing*/);
}
if (!mAppsToAdd.isEmpty()) {
@@ -810,7 +769,7 @@ public class IconCache {
}
private static final class IconDB extends SQLiteCacheHelper {
- private final static int DB_VERSION = 10;
+ private final static int DB_VERSION = 13;
private final static int RELEASE_VERSION = DB_VERSION +
(FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1);
@@ -891,4 +850,28 @@ public class IconCache {
return null;
}
}
+
+ private class ActivityInfoProvider extends Provider {
+
+ private final Intent mIntent;
+ private final UserHandle mUser;
+
+ public ActivityInfoProvider(Intent intent, UserHandle user) {
+ mIntent = intent;
+ mUser = user;
+ }
+
+ @Override
+ public LauncherActivityInfo get() {
+ return mLauncherApps.resolveActivity(mIntent, mUser);
+ }
+ }
+
+ /**
+ * Interface for receiving itemInfo with high-res icon.
+ */
+ public interface ItemInfoUpdateReceiver {
+
+ void reapplyItemInfo(ItemInfoWithIcon info);
+ }
}
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
index 0a273bbc3b..a5d399013e 100644
--- a/src/com/android/launcher3/IconProvider.java
+++ b/src/com/android/launcher3/IconProvider.java
@@ -1,13 +1,8 @@
package com.android.launcher3;
-import android.content.Context;
+import android.content.pm.LauncherActivityInfo;
import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.util.Log;
-import com.android.launcher3.compat.LauncherActivityInfoCompat;
-
-import java.lang.reflect.InvocationTargetException;
import java.util.Locale;
public class IconProvider {
@@ -21,19 +16,6 @@ public class IconProvider {
updateSystemStateString();
}
- public static IconProvider loadByName(String className, Context context) {
- if (TextUtils.isEmpty(className)) return new IconProvider();
- if (DBG) Log.d(TAG, "Loading IconProvider: " + className);
- try {
- Class> cls = Class.forName(className);
- return (IconProvider) cls.getDeclaredConstructor(Context.class).newInstance(context);
- } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
- | ClassCastException | NoSuchMethodException | InvocationTargetException e) {
- Log.e(TAG, "Bad IconProvider class", e);
- return new IconProvider();
- }
- }
-
public void updateSystemStateString() {
mSystemState = Locale.getDefault().toString();
}
@@ -43,7 +25,7 @@ public class IconProvider {
}
- public Drawable getIcon(LauncherActivityInfoCompat info, int iconDpi) {
+ public Drawable getIcon(LauncherActivityInfo info, int iconDpi) {
return info.getIcon(iconDpi);
}
}
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index 398c9d2a29..0608fdd2e4 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -18,14 +18,16 @@ package com.android.launcher3;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.Toast;
import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.util.Themes;
public class InfoDropTarget extends UninstallDropTarget {
@@ -43,13 +45,13 @@ public class InfoDropTarget extends UninstallDropTarget {
protected void onFinishInflate() {
super.onFinishInflate();
// Get the hover color
- mHoverColor = Utilities.getColorAccent(getContext());
+ mHoverColor = Themes.getColorAccent(getContext());
setDrawable(R.drawable.ic_info_launcher);
}
@Override
- void completeDrop(DragObject d) {
+ public void completeDrop(DragObject d) {
DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback
? (DropTargetResultCallback) d.dragSource : null;
startDetailsActivityForInfo(d.dragInfo, mLauncher, callback);
@@ -60,6 +62,11 @@ public class InfoDropTarget extends UninstallDropTarget {
*/
public static boolean startDetailsActivityForInfo(
ItemInfo info, Launcher launcher, DropTargetResultCallback callback) {
+ return startDetailsActivityForInfo(info, launcher, callback, null, null);
+ }
+
+ public static boolean startDetailsActivityForInfo(ItemInfo info, Launcher launcher,
+ DropTargetResultCallback callback, Rect sourceBounds, Bundle opts) {
boolean result = false;
ComponentName componentName = null;
if (info instanceof AppInfo) {
@@ -74,7 +81,7 @@ public class InfoDropTarget extends UninstallDropTarget {
if (componentName != null) {
try {
LauncherAppsCompat.getInstance(launcher)
- .showAppDetailsForProfile(componentName, info.user);
+ .showAppDetailsForProfile(componentName, info.user, sourceBounds, opts);
result = true;
} catch (SecurityException | ActivityNotFoundException e) {
Toast.makeText(launcher, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
@@ -90,17 +97,21 @@ public class InfoDropTarget extends UninstallDropTarget {
@Override
protected boolean supportsDrop(DragSource source, ItemInfo info) {
- return source.supportsAppInfoDropTarget() && supportsDrop(info);
+ return source.supportsAppInfoDropTarget() && supportsDrop(getContext(), info);
}
- public static boolean supportsDrop(ItemInfo info) {
+ public static boolean supportsDrop(Context context, ItemInfo info) {
// Only show the App Info drop target if developer settings are enabled.
- ContentResolver resolver = LauncherAppState.getInstance().getContext().getContentResolver();
- boolean developmentSettingsEnabled = Settings.Global.getInt(resolver,
+ boolean developmentSettingsEnabled = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1;
- return developmentSettingsEnabled
- && (info instanceof AppInfo || info instanceof ShortcutInfo
- || info instanceof PendingAddItemInfo || info instanceof LauncherAppWidgetInfo)
- && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+ if (!developmentSettingsEnabled) {
+ return false;
+ }
+ return info.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT &&
+ (info instanceof AppInfo ||
+ (info instanceof ShortcutInfo && !((ShortcutInfo) info).isPromise()) ||
+ (info instanceof LauncherAppWidgetInfo &&
+ ((LauncherAppWidgetInfo) info).restoreStatus == 0) ||
+ info instanceof PendingAddItemInfo);
}
}
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index d8e58d8294..ce8557065e 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -16,35 +16,46 @@
package com.android.launcher3;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
+import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.os.Looper;
+import android.os.Parcelable;
+import android.os.Process;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
-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.graphics.LauncherIcons;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.Provider;
import com.android.launcher3.util.Thunk;
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.Arrays;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Set;
public class InstallShortcutReceiver extends BroadcastReceiver {
@@ -61,6 +72,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
private static final String ICON_RESOURCE_PACKAGE_NAME_KEY = "iconResourcePackage";
private static final String APP_SHORTCUT_TYPE_KEY = "isAppShortcut";
+ private static final String DEEPSHORTCUT_TYPE_KEY = "isDeepShortcut";
+ private static final String APP_WIDGET_TYPE_KEY = "isAppWidget";
private static final String USER_HANDLE_KEY = "userHandle";
// The set of shortcuts that are pending install
@@ -77,11 +90,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
String encoded = info.encodeToString();
if (encoded != null) {
Set strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null);
- if (strings == null) {
- strings = new HashSet(1);
- } else {
- strings = new HashSet(strings);
- }
+ strings = (strings != null) ? new HashSet<>(strings) : new HashSet(1);
strings.add(encoded);
sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply();
}
@@ -89,7 +98,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
}
public static void removeFromInstallQueue(Context context, HashSet packageNames,
- UserHandleCompat user) {
+ UserHandle user) {
if (packageNames.isEmpty()) {
return;
}
@@ -100,32 +109,37 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
Log.d(TAG, "APPS_PENDING_INSTALL: " + strings
+ ", removing packages: " + packageNames);
}
- if (strings != null) {
- Set newStrings = new HashSet(strings);
- Iterator newStringsIter = newStrings.iterator();
- while (newStringsIter.hasNext()) {
- String encoded = newStringsIter.next();
- PendingInstallShortcutInfo info = decode(encoded, context);
- if (info == null || (packageNames.contains(info.getTargetPackage())
- && user.equals(info.user))) {
+ if (Utilities.isEmpty(strings)) {
+ return;
+ }
+ Set newStrings = new HashSet<>(strings);
+ Iterator newStringsIter = newStrings.iterator();
+ while (newStringsIter.hasNext()) {
+ String encoded = newStringsIter.next();
+ try {
+ Decoder decoder = new Decoder(encoded, context);
+ if (packageNames.contains(getIntentPackage(decoder.launcherIntent)) &&
+ user.equals(decoder.user)) {
newStringsIter.remove();
}
+ } catch (JSONException | URISyntaxException e) {
+ Log.d(TAG, "Exception reading shortcut to add: " + e);
+ newStringsIter.remove();
}
- sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).apply();
}
+ sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).apply();
}
}
- private static ArrayList getAndClearInstallQueue(
- SharedPreferences sharedPrefs, Context context) {
+ private static ArrayList getAndClearInstallQueue(Context context) {
+ SharedPreferences sharedPrefs = Utilities.getPrefs(context);
synchronized(sLock) {
+ ArrayList infos = new ArrayList<>();
Set strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null);
if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings);
if (strings == null) {
- return new ArrayList();
+ return infos;
}
- ArrayList infos =
- new ArrayList();
for (String encoded : strings) {
PendingInstallShortcutInfo info = decode(encoded, context);
if (info != null) {
@@ -149,8 +163,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
if (info != null) {
if (!info.isLauncherActivity()) {
// Since its a custom shortcut, verify that it is safe to launch.
- if (!PackageManagerHelper.hasPermissionForActivity(
- context, info.launchIntent, null)) {
+ if (!new PackageManagerHelper(context).hasPermissionForActivity(
+ info.launchIntent, null)) {
// Target cannot be launched, or requires some special permission to launch
Log.e(TAG, "Ignoring malicious intent " + info.launchIntent.toUri(0));
return;
@@ -181,7 +195,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
return null;
}
- PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, context);
+ PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(
+ data, Process.myUserHandle(), context);
if (info.launchIntent == null || info.label == null) {
if (DBG) Log.e(TAG, "Invalid install shortcut intent");
return null;
@@ -192,12 +207,45 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
public static ShortcutInfo fromShortcutIntent(Context context, Intent data) {
PendingInstallShortcutInfo info = createPendingInfo(context, data);
- return info == null ? null : info.getShortcutInfo();
+ return info == null ? null : (ShortcutInfo) info.getItemInfo();
+ }
+
+ public static void queueShortcut(ShortcutInfoCompat info, Context context) {
+ queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, context), context);
+ }
+
+ public static void queueWidget(AppWidgetProviderInfo info, int widgetId, Context context) {
+ queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, widgetId, context), context);
+ }
+
+ public static void queueActivityInfo(LauncherActivityInfo activity, Context context) {
+ queuePendingShortcutInfo(new PendingInstallShortcutInfo(activity, context), context);
+ }
+
+ public static HashSet getPendingShortcuts(Context context) {
+ HashSet result = new HashSet<>();
+
+ Set strings = Utilities.getPrefs(context).getStringSet(APPS_PENDING_INSTALL, null);
+ if (Utilities.isEmpty(strings)) {
+ return result;
+ }
+
+ for (String encoded : strings) {
+ try {
+ Decoder decoder = new Decoder(encoded, context);
+ if (decoder.optBoolean(DEEPSHORTCUT_TYPE_KEY)) {
+ result.add(ShortcutKey.fromIntent(decoder.launcherIntent, decoder.user));
+ }
+ } catch (JSONException | URISyntaxException e) {
+ Log.d(TAG, "Exception reading shortcut to add: " + e);
+ }
+ }
+ return result;
}
private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) {
// Queue the item up for adding if launcher has not loaded properly yet
- LauncherAppState app = LauncherAppState.getInstance();
+ LauncherAppState app = LauncherAppState.getInstance(context);
boolean launcherNotLoaded = app.getModel().getCallback() == null;
addToInstallQueue(Utilities.getPrefs(context), info);
@@ -213,35 +261,12 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
mUseInstallQueue = false;
flushInstallQueue(context);
}
+
static void flushInstallQueue(Context context) {
- SharedPreferences sp = Utilities.getPrefs(context);
- ArrayList installQueue = getAndClearInstallQueue(sp, context);
- if (!installQueue.isEmpty()) {
- Iterator iter = installQueue.iterator();
- ArrayList addShortcuts = new ArrayList();
- while (iter.hasNext()) {
- final PendingInstallShortcutInfo pendingInfo = iter.next();
-
- // If the intent specifies a package, make sure the package exists
- 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: "
- + pendingInfo.launchIntent);
- continue;
- }
- }
-
- // Generate a shortcut info to add into the model
- addShortcuts.add(pendingInfo.getShortcutInfo());
- }
-
- // Add the new apps to the model and bind them
- if (!addShortcuts.isEmpty()) {
- LauncherAppState app = LauncherAppState.getInstance();
- app.getModel().addAndBindAddedWorkspaceItems(context, addShortcuts);
- }
+ ArrayList items = getAndClearInstallQueue(context);
+ if (!items.isEmpty()) {
+ LauncherAppState.getInstance(context).getModel().addAndBindAddedWorkspaceItems(
+ new LazyShortcutsProvider(context.getApplicationContext(), items));
}
}
@@ -264,43 +289,86 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
private static class PendingInstallShortcutInfo {
- final LauncherActivityInfoCompat activityInfo;
+ final LauncherActivityInfo activityInfo;
+ final ShortcutInfoCompat shortcutInfo;
+ final AppWidgetProviderInfo providerInfo;
final Intent data;
final Context mContext;
final Intent launchIntent;
final String label;
- final UserHandleCompat user;
+ final UserHandle user;
/**
* Initializes a PendingInstallShortcutInfo received from a different app.
*/
- public PendingInstallShortcutInfo(Intent data, Context context) {
+ public PendingInstallShortcutInfo(Intent data, UserHandle user, Context context) {
+ activityInfo = null;
+ shortcutInfo = null;
+ providerInfo = null;
+
this.data = data;
+ this.user = user;
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;
+ public PendingInstallShortcutInfo(LauncherActivityInfo info, Context context) {
activityInfo = info;
- user = info.getUser();
+ shortcutInfo = null;
+ providerInfo = null;
- launchIntent = AppInfo.makeLaunchIntent(context, info, user);
+ data = null;
+ user = info.getUser();
+ mContext = context;
+
+ launchIntent = AppInfo.makeLaunchIntent(info);
label = info.getLabel().toString();
}
+ /**
+ * Initializes a PendingInstallShortcutInfo to represent a launcher target.
+ */
+ public PendingInstallShortcutInfo(ShortcutInfoCompat info, Context context) {
+ activityInfo = null;
+ shortcutInfo = info;
+ providerInfo = null;
+
+ data = null;
+ mContext = context;
+ user = info.getUserHandle();
+
+ launchIntent = info.makeIntent();
+ label = info.getShortLabel().toString();
+ }
+
+ /**
+ * Initializes a PendingInstallShortcutInfo to represent a launcher target.
+ */
+ public PendingInstallShortcutInfo(
+ AppWidgetProviderInfo info, int widgetId, Context context) {
+ activityInfo = null;
+ shortcutInfo = null;
+ providerInfo = info;
+
+ data = null;
+ mContext = context;
+ user = info.getProfile();
+
+ launchIntent = new Intent().setComponent(info.provider)
+ .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
+ label = info.label;
+ }
+
public String encodeToString() {
- if (activityInfo != null) {
- try {
+ try {
+ if (activityInfo != null) {
// If it a launcher target, we only need component name, and user to
// recreate this.
return new JSONStringer()
@@ -310,30 +378,45 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
.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;
+ } else if (shortcutInfo != null) {
+ // 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(DEEPSHORTCUT_TYPE_KEY).value(true)
+ .key(USER_HANDLE_KEY).value(UserManagerCompat.getInstance(mContext)
+ .getSerialNumberForUser(user))
+ .endObject().toString();
+ } else if (providerInfo != null) {
+ // 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_WIDGET_TYPE_KEY).value(true)
+ .key(USER_HANDLE_KEY).value(UserManagerCompat.getInstance(mContext)
+ .getSerialNumberForUser(user))
+ .endObject().toString();
}
- }
- 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);
- }
+ 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);
+ // 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 {
+ // Only encode the parameters which are supported by the API.
JSONStringer json = new JSONStringer()
.object()
.key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
@@ -352,57 +435,100 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
return json.endObject().toString();
} catch (JSONException e) {
Log.d(TAG, "Exception when adding shortcut: " + e);
+ return null;
}
- return null;
}
- public ShortcutInfo getShortcutInfo() {
+ public ItemInfo getItemInfo() {
if (activityInfo != null) {
- return new ShortcutInfo(activityInfo, mContext);
+ AppInfo appInfo = new AppInfo(mContext, activityInfo, user);
+ final LauncherAppState app = LauncherAppState.getInstance(mContext);
+ // Set default values until proper values is loaded.
+ appInfo.title = "";
+ appInfo.iconBitmap = app.getIconCache().getDefaultIcon(user);
+ final ShortcutInfo si = appInfo.makeShortcut();
+ if (Looper.myLooper() == LauncherModel.getWorkerLooper()) {
+ app.getIconCache().getTitleAndIcon(si, activityInfo, false /* useLowResIcon */);
+ } else {
+ app.getModel().updateAndBindShortcutInfo(new Provider() {
+ @Override
+ public ShortcutInfo get() {
+ app.getIconCache().getTitleAndIcon(
+ si, activityInfo, false /* useLowResIcon */);
+ return si;
+ }
+ });
+ }
+ return si;
+ } else if (shortcutInfo != null) {
+ ShortcutInfo si = new ShortcutInfo(shortcutInfo, mContext);
+ si.iconBitmap = LauncherIcons.createShortcutIcon(shortcutInfo, mContext);
+ return si;
+ } else if (providerInfo != null) {
+ LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
+ .fromProviderInfo(mContext, providerInfo);
+ LauncherAppWidgetInfo widgetInfo = new LauncherAppWidgetInfo(
+ launchIntent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0),
+ info.provider);
+ InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
+ widgetInfo.minSpanX = info.minSpanX;
+ widgetInfo.minSpanY = info.minSpanY;
+ widgetInfo.spanX = Math.min(info.spanX, idp.numColumns);
+ widgetInfo.spanY = Math.min(info.spanY, idp.numRows);
+ return widgetInfo;
} else {
- return LauncherAppState.getInstance().getModel().infoFromShortcutIntent(mContext, data);
+ return createShortcutInfo(data, LauncherAppState.getInstance(mContext));
}
}
- public String getTargetPackage() {
- String packageName = launchIntent.getPackage();
- if (packageName == null) {
- packageName = launchIntent.getComponent() == null ? null :
- launchIntent.getComponent().getPackageName();
- }
- return packageName;
- }
-
public boolean isLauncherActivity() {
return activityInfo != null;
}
}
+ private static String getIntentPackage(Intent intent) {
+ return intent.getComponent() == null
+ ? intent.getPackage() : intent.getComponent().getPackageName();
+ }
+
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));
- if (user == null) {
+ Decoder decoder = new Decoder(encoded, context);
+ if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) {
+ LauncherActivityInfo info = LauncherAppsCompat.getInstance(context)
+ .resolveActivity(decoder.launcherIntent, decoder.user);
+ return info == null ? null : new PendingInstallShortcutInfo(info, context);
+ } else if (decoder.optBoolean(DEEPSHORTCUT_TYPE_KEY)) {
+ DeepShortcutManager sm = DeepShortcutManager.getInstance(context);
+ List si = sm.queryForFullDetails(
+ decoder.launcherIntent.getPackage(),
+ Arrays.asList(decoder.launcherIntent.getStringExtra(
+ ShortcutInfoCompat.EXTRA_SHORTCUT_ID)),
+ decoder.user);
+ if (si.isEmpty()) {
+ return null;
+ } else {
+ return new PendingInstallShortcutInfo(si.get(0), context);
+ }
+ } else if (decoder.optBoolean(APP_WIDGET_TYPE_KEY)) {
+ int widgetId = decoder.launcherIntent
+ .getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0);
+ AppWidgetProviderInfo info = AppWidgetManager.getInstance(context)
+ .getAppWidgetInfo(widgetId);
+ if (info == null || !info.provider.equals(decoder.launcherIntent.getComponent()) ||
+ !info.getProfile().equals(decoder.user)) {
return null;
}
-
- LauncherActivityInfoCompat info = LauncherAppsCompat.getInstance(context)
- .resolveActivity(launcherIntent, user);
- return info == null ? null : new PendingInstallShortcutInfo(info, context);
+ return new PendingInstallShortcutInfo(info, widgetId, context);
}
Intent data = new Intent();
- data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, launcherIntent);
- data.putExtra(Intent.EXTRA_SHORTCUT_NAME, object.getString(NAME_KEY));
+ data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, decoder.launcherIntent);
+ data.putExtra(Intent.EXTRA_SHORTCUT_NAME, decoder.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);
+ String iconBase64 = decoder.optString(ICON_KEY);
+ String iconResourceName = decoder.optString(ICON_RESOURCE_NAME_KEY);
+ String iconResourcePackageName = decoder.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);
@@ -415,13 +541,29 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
data.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
}
- return new PendingInstallShortcutInfo(data, context);
+ return new PendingInstallShortcutInfo(data, decoder.user, context);
} catch (JSONException | URISyntaxException e) {
Log.d(TAG, "Exception reading shortcut to add: " + e);
}
return null;
}
+ private static class Decoder extends JSONObject {
+ public final Intent launcherIntent;
+ public final UserHandle user;
+
+ private Decoder(String encoded, Context context) throws JSONException, URISyntaxException {
+ super(encoded);
+ launcherIntent = Intent.parseUri(getString(LAUNCH_INTENT_KEY), 0);
+ user = has(USER_HANDLE_KEY) ? UserManagerCompat.getInstance(context)
+ .getUserForSerialNumber(getLong(USER_HANDLE_KEY))
+ : Process.myUserHandle();
+ if (user == null) {
+ throw new JSONException("Invalid user");
+ }
+ }
+ }
+
/**
* Tries to create a new PendingInstallShortcutInfo which represents the same target,
* but is an app target and not a shortcut.
@@ -433,22 +575,90 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
// Already an activity target
return original;
}
- if (!Utilities.isLauncherAppTarget(original.launchIntent)
- || !original.user.equals(UserHandleCompat.myUserHandle())) {
- // We can only convert shortcuts which point to a main activity in the current user.
+ if (!Utilities.isLauncherAppTarget(original.launchIntent)) {
return original;
}
- PackageManager pm = original.mContext.getPackageManager();
- ResolveInfo info = pm.resolveActivity(original.launchIntent, 0);
-
+ LauncherActivityInfo info = LauncherAppsCompat.getInstance(original.mContext)
+ .resolveActivity(original.launchIntent, original.user);
if (info == null) {
return original;
}
-
// Ignore any conflicts in the label name, as that can change based on locale.
- LauncherActivityInfoCompat launcherInfo = LauncherActivityInfoCompat
- .fromResolveInfo(info, original.mContext);
- return new PendingInstallShortcutInfo(launcherInfo, original.mContext);
+ return new PendingInstallShortcutInfo(info, original.mContext);
}
+
+ private static class LazyShortcutsProvider extends Provider> {
+
+ private final Context mContext;
+ private final ArrayList mPendingItems;
+
+ public LazyShortcutsProvider(Context context, ArrayList items) {
+ mContext = context;
+ mPendingItems = items;
+ }
+
+ /**
+ * This must be called on the background thread as this requires multiple calls to
+ * packageManager and icon cache.
+ */
+ @Override
+ public ArrayList get() {
+ Preconditions.assertNonUiThread();
+ ArrayList installQueue = new ArrayList<>();
+ LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
+ for (PendingInstallShortcutInfo pendingInfo : mPendingItems) {
+ // If the intent specifies a package, make sure the package exists
+ String packageName = getIntentPackage(pendingInfo.launchIntent);
+ if (!TextUtils.isEmpty(packageName) && !launcherApps.isPackageEnabledForProfile(
+ packageName, pendingInfo.user)) {
+ if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: "
+ + pendingInfo.launchIntent);
+ continue;
+ }
+
+ // Generate a shortcut info to add into the model
+ installQueue.add(pendingInfo.getItemInfo());
+ }
+ return installQueue;
+ }
+ }
+
+ private static ShortcutInfo createShortcutInfo(Intent data, LauncherAppState app) {
+ Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
+ String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
+ Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
+
+ if (intent == null) {
+ // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
+ Log.e(TAG, "Can't construct ShorcutInfo with null intent");
+ return null;
+ }
+
+ final ShortcutInfo info = new ShortcutInfo();
+
+ // Only support intents for current user for now. Intents sent from other
+ // users wouldn't get here without intent forwarding anyway.
+ info.user = Process.myUserHandle();
+
+ if (bitmap instanceof Bitmap) {
+ info.iconBitmap = LauncherIcons.createIconBitmap((Bitmap) bitmap, app.getContext());
+ } else {
+ Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
+ if (extra instanceof Intent.ShortcutIconResource) {
+ info.iconResource = (Intent.ShortcutIconResource) extra;
+ info.iconBitmap = LauncherIcons.createIconBitmap(info.iconResource, app.getContext());
+ }
+ }
+ if (info.iconBitmap == null) {
+ info.iconBitmap = app.getIconCache().getDefaultIcon(info.user);
+ }
+
+ info.title = Utilities.trim(name);
+ info.contentDescription = UserManagerCompat.getInstance(app.getContext())
+ .getBadgedLabelForUser(info.title, info.user);
+ info.intent = intent;
+ return info;
+ }
+
}
diff --git a/src/com/android/launcher3/InterruptibleInOutAnimator.java b/src/com/android/launcher3/InterruptibleInOutAnimator.java
index 29df38bae1..8501e2429b 100644
--- a/src/com/android/launcher3/InterruptibleInOutAnimator.java
+++ b/src/com/android/launcher3/InterruptibleInOutAnimator.java
@@ -48,7 +48,7 @@ public class InterruptibleInOutAnimator {
@Thunk int mDirection = STOPPED;
public InterruptibleInOutAnimator(View view, long duration, float fromValue, float toValue) {
- mAnimator = LauncherAnimUtils.ofFloat(view, fromValue, toValue).setDuration(duration);
+ mAnimator = LauncherAnimUtils.ofFloat(fromValue, toValue).setDuration(duration);
mOriginalDuration = duration;
mOriginalFromValue = fromValue;
mOriginalToValue = toValue;
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 38545e2d0f..9e214d1ec2 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -18,6 +18,7 @@ package com.android.launcher3;
import android.annotation.TargetApi;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Point;
@@ -85,10 +86,11 @@ public class InvariantDeviceProfile {
*/
public int numHotseatIcons;
float hotseatIconSize;
+ public float hotseatScale;
int defaultLayoutId;
- DeviceProfile landscapeProfile;
- DeviceProfile portraitProfile;
+ public DeviceProfile landscapeProfile;
+ public DeviceProfile portraitProfile;
public Point defaultWallpaperSize;
@@ -117,6 +119,8 @@ public class InvariantDeviceProfile {
numHotseatIcons = hs;
hotseatIconSize = his;
defaultLayoutId = dlId;
+
+ hotseatScale = hotseatIconSize / iconSize;
}
@TargetApi(23)
@@ -158,6 +162,8 @@ public class InvariantDeviceProfile {
// Supported overrides: numRows, numColumns, iconSize
applyPartnerDeviceProfileOverrides(context, dm);
+ hotseatScale = hotseatIconSize / iconSize;
+
Point realSize = new Point();
display.getRealSize(realSize);
// The real size never changes. smallSide and largeSide will remain the
@@ -321,6 +327,11 @@ public class InvariantDeviceProfile {
return rank == getAllAppsButtonRank();
}
+ public DeviceProfile getDeviceProfile(Context context) {
+ return context.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE ? landscapeProfile : portraitProfile;
+ }
+
private float weight(float x0, float y0, float x1, float y1, float pow) {
float d = dist(x0, y0, x1, y1);
if (Float.compare(d, 0f) == 0) {
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index c0c22a325a..0779a3d206 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -18,23 +18,17 @@ package com.android.launcher3;
import android.content.ComponentName;
import android.content.ContentValues;
-import android.content.Context;
import android.content.Intent;
-import android.graphics.Bitmap;
+import android.os.Process;
+import android.os.UserHandle;
-import com.android.launcher3.compat.UserHandleCompat;
-import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.util.ContentWriter;
/**
* Represents an item in the launcher.
*/
public class ItemInfo {
- /**
- * Intent extra to store the profile. Format: UserHandle
- */
- public static final String EXTRA_PROFILE = "profile";
-
public static final int NO_ID = -1;
/**
@@ -59,7 +53,7 @@ public class ItemInfo {
public long container = NO_ID;
/**
- * Iindicates the screen in which the shortcut appears.
+ * Indicates the screen in which the shortcut appears.
*/
public long screenId = -1;
@@ -108,10 +102,10 @@ public class ItemInfo {
*/
public CharSequence contentDescription;
- public UserHandleCompat user;
+ public UserHandle user;
public ItemInfo() {
- user = UserHandleCompat.myUserHandle();
+ user = Process.myUserHandle();
}
ItemInfo(ItemInfo info) {
@@ -142,15 +136,15 @@ public class ItemInfo {
return getIntent() == null ? null : getIntent().getComponent();
}
- public void writeToValues(ContentValues values) {
- values.put(LauncherSettings.Favorites.ITEM_TYPE, itemType);
- values.put(LauncherSettings.Favorites.CONTAINER, container);
- values.put(LauncherSettings.Favorites.SCREEN, screenId);
- values.put(LauncherSettings.Favorites.CELLX, cellX);
- values.put(LauncherSettings.Favorites.CELLY, cellY);
- values.put(LauncherSettings.Favorites.SPANX, spanX);
- values.put(LauncherSettings.Favorites.SPANY, spanY);
- values.put(LauncherSettings.Favorites.RANK, rank);
+ public void writeToValues(ContentWriter writer) {
+ writer.put(LauncherSettings.Favorites.ITEM_TYPE, itemType)
+ .put(LauncherSettings.Favorites.CONTAINER, container)
+ .put(LauncherSettings.Favorites.SCREEN, screenId)
+ .put(LauncherSettings.Favorites.CELLX, cellX)
+ .put(LauncherSettings.Favorites.CELLY, cellY)
+ .put(LauncherSettings.Favorites.SPANX, spanX)
+ .put(LauncherSettings.Favorites.SPANY, spanY)
+ .put(LauncherSettings.Favorites.RANK, rank);
}
public void readFromValues(ContentValues values) {
@@ -166,26 +160,15 @@ public class ItemInfo {
/**
* Write the fields of this item to the DB
- *
- * @param context A context object to use for getting UserManagerCompat
- * @param values
*/
- void onAddToDatabase(Context context, ContentValues values) {
- writeToValues(values);
- long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user);
- values.put(LauncherSettings.Favorites.PROFILE_ID, serialNumber);
-
+ public void onAddToDatabase(ContentWriter writer) {
if (screenId == Workspace.EXTRA_EMPTY_SCREEN_ID) {
// We should never persist an item on the extra empty screen.
throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID");
}
- }
- static void writeBitmap(ContentValues values, Bitmap bitmap) {
- if (bitmap != null) {
- byte[] data = Utilities.flattenBitmap(bitmap);
- values.put(LauncherSettings.Favorites.ICON, data);
- }
+ writeToValues(writer);
+ writer.put(LauncherSettings.Favorites.PROFILE_ID, user);
}
@Override
diff --git a/src/com/android/launcher3/LauncherTransitionable.java b/src/com/android/launcher3/ItemInfoWithIcon.java
similarity index 50%
rename from src/com/android/launcher3/LauncherTransitionable.java
rename to src/com/android/launcher3/ItemInfoWithIcon.java
index b97aaec7d6..1e020e2587 100644
--- a/src/com/android/launcher3/LauncherTransitionable.java
+++ b/src/com/android/launcher3/ItemInfoWithIcon.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -16,12 +16,28 @@
package com.android.launcher3;
+import android.graphics.Bitmap;
+
/**
- * An interface to get callbacks during a launcher transition.
+ * Represents an ItemInfo which also holds an icon.
*/
-public interface LauncherTransitionable {
- void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean multiplePagesVisible);
- void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace);
- void onLauncherTransitionStep(Launcher l, float t);
- void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace);
+public abstract class ItemInfoWithIcon extends ItemInfo {
+
+ /**
+ * A bitmap version of the application icon.
+ */
+ public Bitmap iconBitmap;
+
+ /**
+ * Indicates whether we're using a low res icon
+ */
+ public boolean usingLowResIcon;
+
+ protected ItemInfoWithIcon() { }
+
+ protected ItemInfoWithIcon(ItemInfoWithIcon info) {
+ super(info);
+ iconBitmap = info.iconBitmap;
+ usingLowResIcon = info.usingLowResIcon;
+ }
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4672e080ac..eef578d8fc 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -18,19 +18,15 @@ package com.android.launcher3;
import android.Manifest;
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
-import android.app.Activity;
import android.app.ActivityOptions;
import android.app.AlertDialog;
import android.app.SearchManager;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
@@ -45,22 +41,20 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
-import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.PorterDuff;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Message;
+import android.os.Process;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
+import android.support.annotation.Nullable;
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
@@ -69,20 +63,19 @@ import android.util.Log;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
+import android.view.KeyboardShortcutInfo;
import android.view.Menu;
import android.view.MotionEvent;
-import android.view.Surface;
import android.view.View;
-import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.OvershootInterpolator;
import android.view.inputmethod.InputMethodManager;
-import android.widget.Advanceable;
-import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
@@ -92,39 +85,50 @@ import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.allapps.DefaultAppSearchController;
+import com.android.launcher3.anim.AnimationLayerSet;
import com.android.launcher3.compat.AppWidgetManagerCompat;
-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.compat.PinItemRequestCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.dragndrop.PinItemDragListener;
import com.android.launcher3.dynamicui.ExtractedColors;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.keyboard.CustomActionsPopup;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.model.ModelWriter;
+import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.pageindicators.PageIndicator;
+import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.DeepShortcutsContainer;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import com.android.launcher3.util.ActivityResultInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.PendingRequestArgs;
import com.android.launcher3.util.TestingUtils;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.WidgetAddFlowHandler;
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetsContainerView;
@@ -132,15 +136,15 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Default launcher application.
*/
-public class Launcher extends Activity
+public class Launcher extends BaseActivity
implements LauncherExterns, View.OnClickListener, OnLongClickListener,
LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener,
AccessibilityManager.AccessibilityStateChangeListener {
@@ -170,17 +174,15 @@ public class Launcher extends Activity
*/
protected static final int REQUEST_LAST = 100;
- // To turn on these properties, type
- // adb shell setprop logTap.tag.PROPERTY_NAME [VERBOSE | SUPPRESS]
- static final String DUMP_STATE_PROPERTY = "launcher_dump_state";
+ private static final int SOFT_INPUT_MODE_DEFAULT =
+ WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
+ private static final int SOFT_INPUT_MODE_ALL_APPS =
+ WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
// The Intent extra that defines whether to ignore the launch animation
static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
"com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
- public static final String ACTION_APPWIDGET_HOST_RESET =
- "com.android.launcher3.intent.ACTION_APPWIDGET_HOST_RESET";
-
// Type: int
private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
// Type: int
@@ -201,7 +203,7 @@ public class Launcher extends Activity
private boolean mIsSafeModeEnabled;
- static final int APPWIDGET_HOST_ID = 1024;
+ public static final int APPWIDGET_HOST_ID = 1024;
public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 500;
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
private static final int ACTIVITY_START_DELAY = 1000;
@@ -211,18 +213,6 @@ public class Launcher extends Activity
private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
@Thunk static int NEW_APPS_ANIMATION_DELAY = 500;
- private final BroadcastReceiver mUiBroadcastReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (ACTION_APPWIDGET_HOST_RESET.equals(intent.getAction())) {
- if (mAppWidgetHost != null) {
- mAppWidgetHost.startListening();
- }
- }
- }
- };
-
@Thunk Workspace mWorkspace;
private View mLauncherView;
@Thunk DragLayer mDragLayer;
@@ -250,9 +240,8 @@ public class Launcher extends Activity
// Main container view and the model for the widget tray screen.
@Thunk WidgetsContainerView mWidgetsView;
- @Thunk WidgetsModel mWidgetsModel;
+ @Thunk MultiHashMap mAllWidgets;
- private Bundle mSavedState;
// We set the state in both onCreate and then onNewIntent in some cases, which causes both
// scroll issues (because the workspace may not have been measured yet) and extra work.
// Instead, just save the state that we need to restore Launcher to, and commit it in onResume.
@@ -270,30 +259,19 @@ public class Launcher extends Activity
private ViewOnDrawExecutor mPendingExecutor;
private LauncherModel mModel;
+ private ModelWriter mModelWriter;
private IconCache mIconCache;
private ExtractedColors mExtractedColors;
private LauncherAccessibilityDelegate mAccessibilityDelegate;
+ private Handler mHandler = new Handler();
private boolean mIsResumeFromActionScreenOff;
- @Thunk boolean mUserPresent = true;
- private boolean mVisible;
- private boolean mHasFocus;
- private boolean mAttached;
+ private boolean mHasFocus = false;
+ private boolean mAttached = false;
- /** Maps launcher activity components to their list of shortcut ids. */
- private MultiHashMap mDeepShortcutMap = new MultiHashMap<>();
+ private PopupDataProvider mPopupDataProvider;
private View.OnTouchListener mHapticFeedbackTouchListener;
- // Related to the auto-advancing of widgets
- private final int ADVANCE_MSG = 1;
- private static final int ADVANCE_INTERVAL = 20000;
- private static final int ADVANCE_STAGGER = 250;
-
- private boolean mAutoAdvanceRunning = false;
- private long mAutoAdvanceSentTime;
- private long mAutoAdvanceTimeLeft = -1;
- @Thunk HashMap mWidgetsToAdvance = new HashMap<>();
-
// Determines how long to wait after a rotation before restoring the screen orientation to
// match the sensor state.
private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
@@ -304,15 +282,6 @@ public class Launcher extends Activity
// it from the context.
private SharedPreferences mSharedPrefs;
- // Holds the page that we need to animate to, and the icon views that we need to animate up
- // when we scroll to that page on resume.
- @Thunk ImageView mFolderIconImageView;
- private Bitmap mFolderIconBitmap;
- private Canvas mFolderIconCanvas;
- private Rect mRectForFolderAnimation = new Rect();
-
- private DeviceProfile mDeviceProfile;
-
private boolean mMoveToDefaultScreenFromNewIntent;
// This is set to the view that launched the activity that navigated the user away from
@@ -350,7 +319,7 @@ public class Launcher extends Activity
*/
private PendingRequestArgs mPendingRequestArgs;
- private UserEventDispatcher mUserEventDispatcher;
+ private float mLastDispatchTouchEventX = 0.0f;
public ViewGroupFocusHelper mFocusHandler;
private boolean mRotationEnabled = false;
@@ -392,17 +361,21 @@ public class Launcher extends Activity
super.onCreate(savedInstanceState);
- LauncherAppState app = LauncherAppState.getInstance();
+ LauncherAppState app = LauncherAppState.getInstance(this);
// Load configuration-specific DeviceProfile
- mDeviceProfile = getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE ?
- app.getInvariantDeviceProfile().landscapeProfile
- : app.getInvariantDeviceProfile().portraitProfile;
+ mDeviceProfile = app.getInvariantDeviceProfile().getDeviceProfile(this);
+ if (isInMultiWindowModeCompat()) {
+ Display display = getWindowManager().getDefaultDisplay();
+ Point mwSize = new Point();
+ display.getSize(mwSize);
+ mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
+ }
mSharedPrefs = Utilities.getPrefs(this);
mIsSafeModeEnabled = getPackageManager().isSafeMode();
mModel = app.setLauncher(this);
+ mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout());
mIconCache = app.getIconCache();
mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
@@ -420,20 +393,21 @@ public class Launcher extends Activity
// LauncherModel load.
mPaused = false;
- setContentView(R.layout.launcher);
+ mLauncherView = getLayoutInflater().inflate(R.layout.launcher, null);
setupViews();
mDeviceProfile.layout(this, false /* notifyListeners */);
mExtractedColors = new ExtractedColors();
loadExtractedColorsAndColorItems();
+ mPopupDataProvider = new PopupDataProvider(this);
+
((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
.addAccessibilityStateChangeListener(this);
lockAllApps();
- mSavedState = savedInstanceState;
- restoreState(mSavedState);
+ restoreState(savedInstanceState);
if (LauncherAppState.PROFILE_STARTUP) {
Trace.endSection();
@@ -441,11 +415,18 @@ public class Launcher extends Activity
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
- if (!mModel.startLoader(mWorkspace.getRestorePage())) {
+ int currentScreen = PagedView.INVALID_RESTORE_PAGE;
+ if (savedInstanceState != null) {
+ currentScreen = savedInstanceState.getInt(RUNTIME_STATE_CURRENT_SCREEN, currentScreen);
+ }
+ if (!mModel.startLoader(currentScreen)) {
// If we are not binding synchronously, show a fade in animation when
// the first page bind completes.
mDragLayer.setAlpha(0);
} else {
+ // Pages bound synchronously.
+ mWorkspace.setCurrentPage(currentScreen);
+
setWorkspaceLoading(true);
}
@@ -453,9 +434,6 @@ public class Launcher extends Activity
mDefaultKeySsb = new SpannableStringBuilder();
Selection.setSelection(mDefaultKeySsb, 0);
- IntentFilter filter = new IntentFilter(ACTION_APPWIDGET_HOST_RESET);
- registerReceiver(mUiBroadcastReceiver, filter);
-
mRotationEnabled = getResources().getBoolean(R.bool.allow_rotation);
// In case we are on a device with locked rotation, we should look at preferences to check
// if the user has specifically allowed rotation.
@@ -465,47 +443,82 @@ public class Launcher extends Activity
mSharedPrefs.registerOnSharedPreferenceChangeListener(mRotationPrefChangeHandler);
}
+ if (PinItemDragListener.handleDragRequest(this, getIntent())) {
+ // Temporarily enable the rotation
+ mRotationEnabled = true;
+ }
+
// On large interfaces, or on devices that a user has specifically enabled screen rotation,
// we want the screen to auto-rotate based on the current orientation
setOrientation();
+ setContentView(mLauncherView);
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
}
+ @Override
+ public View findViewById(int id) {
+ return mLauncherView.findViewById(id);
+ }
+
@Override
public void onExtractedColorsChanged() {
loadExtractedColorsAndColorItems();
}
- private void loadExtractedColorsAndColorItems() {
- // TODO: do this in pre-N as well, once the extraction part is complete.
- if (Utilities.isNycOrAbove()) {
- mExtractedColors.load(this);
- mHotseat.updateColor(mExtractedColors, !mPaused);
- mWorkspace.getPageIndicator().updateColor(mExtractedColors);
- // It's possible that All Apps is visible when this is run,
- // so always use light status bar in that case.
- activateLightStatusBar(isAllAppsVisible());
+ @Override
+ public void onAppWidgetHostReset() {
+ if (mAppWidgetHost != null) {
+ mAppWidgetHost.startListening();
}
}
+ private void loadExtractedColorsAndColorItems() {
+ // TODO: do this in pre-N as well, once the extraction part is complete.
+ if (Utilities.ATLEAST_NOUGAT) {
+ mExtractedColors.load(this);
+ mHotseat.updateColor(mExtractedColors, !mPaused);
+ mWorkspace.getPageIndicator().updateColor(mExtractedColors);
+ boolean lightStatusBar = (FeatureFlags.LIGHT_STATUS_BAR
+ && mExtractedColors.getColor(ExtractedColors.STATUS_BAR_INDEX,
+ ExtractedColors.DEFAULT_DARK) == ExtractedColors.DEFAULT_LIGHT);
+ // It's possible that All Apps is visible when this is run,
+ // so always use light status bar in that case. Only change nav bar color to status bar
+ // color when All Apps is visible.
+ activateLightSystemBars(lightStatusBar || isAllAppsVisible(), true, isAllAppsVisible());
+ }
+ }
+
+ // TODO: use platform flag on API >= 26
+ private static final int SYSTEM_UI_FLAG_LIGHT_NAV_BAR = 0x10;
+
/**
- * Sets the status bar to be light or not. Light status bar means dark icons.
- * @param activate if true, make sure the status bar is light, otherwise base on wallpaper.
+ * Sets the status and/or nav bar to be light or not. Light status bar means dark icons.
+ * @param isLight make sure the system bar is light.
+ * @param statusBar if true, make the status bar theme match the isLight param.
+ * @param navBar if true, make the nav bar theme match the isLight param.
*/
- public void activateLightStatusBar(boolean activate) {
- boolean lightStatusBar = activate || (FeatureFlags.LIGHT_STATUS_BAR
- && mExtractedColors.getColor(ExtractedColors.STATUS_BAR_INDEX,
- ExtractedColors.DEFAULT_DARK) == ExtractedColors.DEFAULT_LIGHT);
+ public void activateLightSystemBars(boolean isLight, boolean statusBar, boolean navBar) {
int oldSystemUiFlags = getWindow().getDecorView().getSystemUiVisibility();
int newSystemUiFlags = oldSystemUiFlags;
- if (lightStatusBar) {
- newSystemUiFlags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ if (isLight) {
+ if (statusBar) {
+ newSystemUiFlags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ }
+ if (navBar && Utilities.isAtLeastO()) {
+ newSystemUiFlags |= SYSTEM_UI_FLAG_LIGHT_NAV_BAR;
+ }
} else {
- newSystemUiFlags &= ~(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+ if (statusBar) {
+ newSystemUiFlags &= ~(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+ }
+ if (navBar && Utilities.isAtLeastO()) {
+ newSystemUiFlags &= ~(SYSTEM_UI_FLAG_LIGHT_NAV_BAR);
+ }
}
+
if (newSystemUiFlags != oldSystemUiFlags) {
getWindow().getDecorView().setSystemUiVisibility(newSystemUiFlags);
}
@@ -582,7 +595,7 @@ public class Launcher extends Activity
}
@Override
- public void onLauncherProviderChange() {
+ public void onLauncherProviderChanged() {
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onLauncherProviderChange();
}
@@ -608,7 +621,7 @@ public class Launcher extends Activity
}
/**
- * Invoked by subclasses to signal a change to the {@link #addCustomContentToLeft} value to
+ * Invoked by subclasses to signal a change to the {@link #addToCustomContentPage} value to
* ensure the custom content page is added or removed if necessary.
*/
protected void invalidateHasCustomContentToLeft() {
@@ -626,23 +639,6 @@ public class Launcher extends Activity
}
}
- public UserEventDispatcher getUserEventDispatcher() {
- if (mLauncherCallbacks != null) {
- UserEventDispatcher dispatcher = mLauncherCallbacks.getUserEventDispatcher();
- if (dispatcher != null) {
- return dispatcher;
- }
- }
-
- // Logger object is a singleton and does not have to be coupled with the foreground
- // activity. Since most user event logging is done on the UI, the object is retrieved
- // from the callback for convenience.
- if (mUserEventDispatcher == null) {
- mUserEventDispatcher = new UserEventDispatcher();
- }
- return mUserEventDispatcher;
- }
-
public boolean isDraggingEnabled() {
// We prevent dragging when we are loading the workspace as it is possible to pick up a view
// that is subsequently removed from the workspace in startBinding().
@@ -657,6 +653,10 @@ public class Launcher extends Activity
return (int) info.id;
}
+ public PopupDataProvider getPopupDataProvider() {
+ return mPopupDataProvider;
+ }
+
/**
* Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
* a configuration step, this allows the proper animations to run after other transitions.
@@ -688,8 +688,9 @@ public class Launcher extends Activity
// Since the view was just bound, also launch the configure activity if needed
LauncherAppWidgetProviderInfo provider = mAppWidgetManager
.getLauncherAppWidgetInfo(widgetId);
- if (provider != null && provider.configure != null) {
- startRestoredWidgetReconfigActivity(provider, widgetInfo);
+ if (provider != null) {
+ new WidgetAddFlowHandler(provider)
+ .startConfigActivity(this, widgetInfo, REQUEST_RECONFIGURE_APPWIDGET);
}
}
break;
@@ -736,7 +737,7 @@ public class Launcher extends Activity
} else if (resultCode == RESULT_OK) {
addAppWidgetImpl(
appWidgetId, requestArgs, null,
- requestArgs.getWidgetProvider(),
+ requestArgs.getWidgetHandler(),
ON_ACTIVITY_RESULT_ANIMATION_DELAY);
}
return;
@@ -895,7 +896,7 @@ public class Launcher extends Activity
if (resultCode == RESULT_OK) {
animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION;
final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId,
- requestArgs.getWidgetProvider());
+ requestArgs.getWidgetHandler().getProviderInfo(this));
boundWidget = layout;
onCompleteRunnable = new Runnable() {
@Override
@@ -928,9 +929,11 @@ public class Launcher extends Activity
mLauncherCallbacks.onStop();
}
- if (Utilities.isNycMR1OrAbove()) {
+ if (Utilities.ATLEAST_NOUGAT_MR1) {
mAppWidgetHost.stopListening();
}
+
+ NotificationListener.removeNotificationsChangedListener();
}
@Override
@@ -942,9 +945,13 @@ public class Launcher extends Activity
mLauncherCallbacks.onStart();
}
- if (Utilities.isNycMR1OrAbove()) {
+ if (Utilities.ATLEAST_NOUGAT_MR1) {
mAppWidgetHost.startListening();
}
+
+ if (!isWorkspaceLoading()) {
+ NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
+ }
}
@Override
@@ -1059,6 +1066,7 @@ public class Launcher extends Activity
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onResume();
}
+
}
@Override
@@ -1151,9 +1159,10 @@ public class Launcher extends Activity
if (mLauncherCallbacks != null) {
return mLauncherCallbacks.hasSettings();
} else {
- // On devices with a locked orientation, we will at least have the allow rotation
- // setting.
- return !getResources().getBoolean(R.bool.allow_rotation);
+ // On O and above we there is always some setting present settings (add icon to
+ // home screen or icon badging). On earlier APIs we will have the allow rotation
+ // setting, on devices with a locked orientation,
+ return Utilities.isAtLeastO() || !getResources().getBoolean(R.bool.allow_rotation);
}
}
@@ -1227,11 +1236,8 @@ public class Launcher extends Activity
if (keyCode == KeyEvent.KEYCODE_MENU) {
// Ignore the menu key if we are currently dragging or are on the custom content screen
if (!isOnCustomContent() && !mDragController.isDragging()) {
- // Close any open folders
- closeFolder();
-
- // Close any shortcuts containers
- closeShortcutsContainer();
+ // Close any open floating view
+ AbstractFloatingView.closeAllOpenViews(this);
// Stop resizing any widgets
mWorkspace.exitWidgetResizeMode();
@@ -1259,22 +1265,6 @@ public class Launcher extends Activity
Selection.setSelection(mDefaultKeySsb, 0);
}
- /**
- * Given the integer (ordinal) value of a State enum instance, convert it to a variable of type
- * State
- */
- private static State intToState(int stateOrdinal) {
- State state = State.WORKSPACE;
- final State[] stateValues = State.values();
- for (int i = 0; i < stateValues.length; i++) {
- if (stateValues[i].ordinal() == stateOrdinal) {
- state = stateValues[i];
- break;
- }
- }
- return state;
- }
-
/**
* Restores the previous state, if it exists.
*
@@ -1285,17 +1275,14 @@ public class Launcher extends Activity
return;
}
- State state = intToState(savedState.getInt(RUNTIME_STATE, State.WORKSPACE.ordinal()));
+ int stateOrdinal = savedState.getInt(RUNTIME_STATE, State.WORKSPACE.ordinal());
+ State[] stateValues = State.values();
+ State state = (stateOrdinal >= 0 && stateOrdinal < stateValues.length)
+ ? stateValues[stateOrdinal] : State.WORKSPACE;
if (state == State.APPS || state == State.WIDGETS) {
mOnResumeState = state;
}
- int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN,
- PagedView.INVALID_RESTORE_PAGE);
- if (currentScreen != PagedView.INVALID_RESTORE_PAGE) {
- mWorkspace.setRestorePage(currentScreen);
- }
-
PendingRequestArgs requestArgs = savedState.getParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS);
if (requestArgs != null) {
setWaitingForResult(requestArgs);
@@ -1308,7 +1295,6 @@ public class Launcher extends Activity
* Finds all the views we need and configure them properly.
*/
private void setupViews() {
- mLauncherView = findViewById(R.id.launcher);
mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
mFocusHandler = mDragLayer.getFocusIndicatorHelper();
mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
@@ -1355,8 +1341,6 @@ public class Launcher extends Activity
}
// Setup the drag controller (drop targets have to be added in reverse order in priority)
- mDragController.setDragScoller(mWorkspace);
- mDragController.setScrollView(mDragLayer);
mDragController.setMoveTarget(mWorkspace);
mDragController.addDropTarget(mWorkspace);
mDropTargetBar.setup(mDragController);
@@ -1373,53 +1357,36 @@ public class Launcher extends Activity
private void setupOverviewPanel() {
mOverviewPanel = (ViewGroup) findViewById(R.id.overview_panel);
- // Long-clicking buttons in the overview panel does the same thing as clicking them.
- OnLongClickListener performClickOnLongClick = new OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- return v.performClick();
- }
- };
-
// Bind wallpaper button actions
View wallpaperButton = findViewById(R.id.wallpaper_button);
- wallpaperButton.setOnClickListener(new OnClickListener() {
+ new OverviewButtonClickListener(ControlType.WALLPAPER_BUTTON) {
@Override
- public void onClick(View view) {
- if (!mWorkspace.isSwitchingState()) {
- onClickWallpaperPicker(view);
- }
+ public void handleViewClick(View view) {
+ onClickWallpaperPicker(view);
}
- });
- wallpaperButton.setOnLongClickListener(performClickOnLongClick);
+ }.attachTo(wallpaperButton);
wallpaperButton.setOnTouchListener(getHapticFeedbackTouchListener());
// Bind widget button actions
mWidgetsButton = findViewById(R.id.widget_button);
- mWidgetsButton.setOnClickListener(new OnClickListener() {
+ new OverviewButtonClickListener(ControlType.WIDGETS_BUTTON) {
@Override
- public void onClick(View view) {
- if (!mWorkspace.isSwitchingState()) {
- onClickAddWidgetButton(view);
- }
+ public void handleViewClick(View view) {
+ onClickAddWidgetButton(view);
}
- });
- mWidgetsButton.setOnLongClickListener(performClickOnLongClick);
+ }.attachTo(mWidgetsButton);
mWidgetsButton.setOnTouchListener(getHapticFeedbackTouchListener());
// Bind settings actions
View settingsButton = findViewById(R.id.settings_button);
boolean hasSettings = hasSettings();
if (hasSettings) {
- settingsButton.setOnClickListener(new OnClickListener() {
+ new OverviewButtonClickListener(ControlType.SETTINGS_BUTTON) {
@Override
- public void onClick(View view) {
- if (!mWorkspace.isSwitchingState()) {
- onClickSettingsButton(view);
- }
+ public void handleViewClick(View view) {
+ onClickSettingsButton(view);
}
- });
- settingsButton.setOnLongClickListener(performClickOnLongClick);
+ }.attachTo(settingsButton);
settingsButton.setOnTouchListener(getHapticFeedbackTouchListener());
} else {
settingsButton.setVisibility(View.GONE);
@@ -1464,7 +1431,7 @@ public class Launcher extends Activity
public View createShortcut(ViewGroup parent, ShortcutInfo info) {
BubbleTextView favorite = (BubbleTextView) getLayoutInflater().inflate(R.layout.app_icon,
parent, false);
- favorite.applyFromShortcutInfo(info, mIconCache);
+ favorite.applyFromShortcutInfo(info);
favorite.setCompoundDrawablePadding(mDeviceProfile.iconDrawablePaddingPx);
favorite.setOnClickListener(this);
favorite.setOnFocusChangeListener(mFocusHandler);
@@ -1481,19 +1448,34 @@ public class Launcher extends Activity
int[] cellXY = mTmpAddItemCellCoordinates;
CellLayout layout = getCellLayout(container, screenId);
- ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(this, data);
- if (info == null || args.getRequestCode() != REQUEST_CREATE_SHORTCUT ||
+ if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT ||
args.getPendingIntent().getComponent() == null) {
return;
}
- if (!PackageManagerHelper.hasPermissionForActivity(
- this, info.intent, args.getPendingIntent().getComponent().getPackageName())) {
- // The app is trying to add a shortcut without sufficient permissions
- Log.e(TAG, "Ignoring malicious intent " + info.intent.toUri(0));
- return;
- }
- final View view = createShortcut(info);
+ ShortcutInfo info = null;
+ if (Utilities.isAtLeastO()) {
+ info = LauncherAppsCompat.createShortcutInfoFromPinItemRequest(
+ this, PinItemRequestCompat.getPinItemRequest(data), 0);
+ }
+
+ if (info == null) {
+ // Legacy shortcuts are only supported for primary profile.
+ info = Process.myUserHandle().equals(args.user)
+ ? InstallShortcutReceiver.fromShortcutIntent(this, data) : null;
+
+ if (info == null) {
+ Log.e(TAG, "Unable to parse a valid custom shortcut result");
+ return;
+ } else if (!new PackageManagerHelper(this).hasPermissionForActivity(
+ info.intent, args.getPendingIntent().getComponent().getPackageName())) {
+ // The app is trying to add a shortcut without sufficient permissions
+ Log.e(TAG, "Ignoring malicious intent " + info.intent.toUri(0));
+ return;
+ }
+ }
+
+ final View view = createShortcut(info);
boolean foundCellSpan = false;
// First we check if we already know the exact location where we want to add this item.
if (cellX >= 0 && cellY >= 0) {
@@ -1517,14 +1499,12 @@ public class Launcher extends Activity
}
if (!foundCellSpan) {
- showOutOfSpaceMessage(isHotseatLayout(layout));
+ mWorkspace.onNoCellFound(layout);
return;
}
- LauncherModel.addItemToDatabase(this, info, container, screenId, cellXY[0], cellXY[1]);
-
- mWorkspace.addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1,
- isWorkspaceLocked());
+ getModelWriter().addItemToDatabase(info, container, screenId, cellXY[0], cellXY[1]);
+ mWorkspace.addInScreen(view, info);
}
/**
@@ -1549,9 +1529,9 @@ public class Launcher extends Activity
launcherInfo.spanY = itemInfo.spanY;
launcherInfo.minSpanX = itemInfo.minSpanX;
launcherInfo.minSpanY = itemInfo.minSpanY;
- launcherInfo.user = mAppWidgetManager.getUser(appWidgetInfo);
+ launcherInfo.user = appWidgetInfo.getUser();
- LauncherModel.addItemToDatabase(this, launcherInfo,
+ getModelWriter().addItemToDatabase(launcherInfo,
itemInfo.container, itemInfo.screenId, itemInfo.cellX, itemInfo.cellY);
if (hostView == null) {
@@ -1559,24 +1539,15 @@ public class Launcher extends Activity
hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
}
hostView.setVisibility(View.VISIBLE);
- addAppWidgetToWorkspace(hostView, launcherInfo, appWidgetInfo, isWorkspaceLocked());
+ prepareAppWidget(hostView, launcherInfo);
+ mWorkspace.addInScreen(hostView, launcherInfo);
}
- private void addAppWidgetToWorkspace(
- AppWidgetHostView hostView, LauncherAppWidgetInfo item,
- LauncherAppWidgetProviderInfo appWidgetInfo, boolean insert) {
+ private void prepareAppWidget(AppWidgetHostView hostView, LauncherAppWidgetInfo item) {
hostView.setTag(item);
item.onBindAppWidget(this, hostView);
-
hostView.setFocusable(true);
hostView.setOnFocusChangeListener(mFocusHandler);
-
- mWorkspace.addInScreen(hostView, item.container, item.screenId,
- item.cellX, item.cellY, item.spanX, item.spanY, insert);
-
- if (!item.isCustomWidget()) {
- addWidgetToAutoAdvanceIfNeeded(hostView, appWidgetInfo);
- }
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -1584,9 +1555,7 @@ public class Launcher extends Activity
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (Intent.ACTION_SCREEN_OFF.equals(action)) {
- mUserPresent = false;
- mDragLayer.clearAllResizeFrames();
- updateAutoAdvanceState();
+ mDragLayer.clearResizeFrame();
// Reset AllApps to its initial state only if we are not in the middle of
// processing a multi-step drop
@@ -1597,13 +1566,28 @@ public class Launcher extends Activity
}
}
mIsResumeFromActionScreenOff = true;
- } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
- mUserPresent = true;
- updateAutoAdvanceState();
}
}
};
+ public void updateIconBadges(final Set updatedBadges) {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ mWorkspace.updateIconBadges(updatedBadges);
+ mAppsView.updateIconBadges(updatedBadges);
+
+ PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(Launcher.this);
+ if (popup != null) {
+ popup.updateNotificationHeader(updatedBadges);
+ }
+ }
+ };
+ if (!waitUntilResume(r)) {
+ r.run();
+ }
+ }
+
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -1611,11 +1595,9 @@ public class Launcher extends Activity
// Listen for broadcasts related to user-presence
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Intent.ACTION_USER_PRESENT);
registerReceiver(mReceiver, filter);
FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
mAttached = true;
- mVisible = true;
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onAttachedToWindow();
@@ -1625,13 +1607,10 @@ public class Launcher extends Activity
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
- mVisible = false;
-
if (mAttached) {
unregisterReceiver(mReceiver);
mAttached = false;
}
- updateAutoAdvanceState();
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onDetachedFromWindow();
@@ -1639,12 +1618,10 @@ public class Launcher extends Activity
}
public void onWindowVisibilityChanged(int visibility) {
- mVisible = visibility == View.VISIBLE;
- updateAutoAdvanceState();
// The following code used to be in onResume, but it turns out onResume is called when
// you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged
// is a more appropriate event to handle
- if (mVisible) {
+ if (visibility == View.VISIBLE) {
if (!mWorkspaceLoading) {
final ViewTreeObserver observer = mWorkspace.getViewTreeObserver();
// We want to let Launcher draw itself at least once before we force it to build
@@ -1679,77 +1656,6 @@ public class Launcher extends Activity
}
}
- @Thunk void sendAdvanceMessage(long delay) {
- mHandler.removeMessages(ADVANCE_MSG);
- Message msg = mHandler.obtainMessage(ADVANCE_MSG);
- mHandler.sendMessageDelayed(msg, delay);
- mAutoAdvanceSentTime = System.currentTimeMillis();
- }
-
- @Thunk void updateAutoAdvanceState() {
- boolean autoAdvanceRunning = mVisible && mUserPresent && !mWidgetsToAdvance.isEmpty();
- if (autoAdvanceRunning != mAutoAdvanceRunning) {
- mAutoAdvanceRunning = autoAdvanceRunning;
- if (autoAdvanceRunning) {
- long delay = mAutoAdvanceTimeLeft == -1 ? ADVANCE_INTERVAL : mAutoAdvanceTimeLeft;
- sendAdvanceMessage(delay);
- } else {
- if (!mWidgetsToAdvance.isEmpty()) {
- mAutoAdvanceTimeLeft = Math.max(0, ADVANCE_INTERVAL -
- (System.currentTimeMillis() - mAutoAdvanceSentTime));
- }
- mHandler.removeMessages(ADVANCE_MSG);
- mHandler.removeMessages(0); // Remove messages sent using postDelayed()
- }
- }
- }
-
- @Thunk final Handler mHandler = new Handler(new Handler.Callback() {
-
- @Override
- public boolean handleMessage(Message msg) {
- if (msg.what == ADVANCE_MSG) {
- int i = 0;
- for (View key: mWidgetsToAdvance.keySet()) {
- final View v = key.findViewById(mWidgetsToAdvance.get(key).autoAdvanceViewId);
- final int delay = ADVANCE_STAGGER * i;
- if (v instanceof Advanceable) {
- mHandler.postDelayed(new Runnable() {
- public void run() {
- ((Advanceable) v).advance();
- }
- }, delay);
- }
- i++;
- }
- sendAdvanceMessage(ADVANCE_INTERVAL);
- }
- return true;
- }
- });
-
- private void addWidgetToAutoAdvanceIfNeeded(View hostView, AppWidgetProviderInfo appWidgetInfo) {
- if (appWidgetInfo == null || appWidgetInfo.autoAdvanceViewId == -1) return;
- View v = hostView.findViewById(appWidgetInfo.autoAdvanceViewId);
- if (v instanceof Advanceable) {
- mWidgetsToAdvance.put(hostView, appWidgetInfo);
- ((Advanceable) v).fyiWillBeAdvancedByHostKThx();
- updateAutoAdvanceState();
- }
- }
-
- private void removeWidgetToAutoAdvance(View hostView) {
- if (mWidgetsToAdvance.containsKey(hostView)) {
- mWidgetsToAdvance.remove(hostView);
- updateAutoAdvanceState();
- }
- }
-
- public void showOutOfSpaceMessage(boolean isHotseatLayout) {
- int strId = (isHotseatLayout ? R.string.hotseat_out_of_space : R.string.out_of_space);
- Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show();
- }
-
public DragLayer getDragLayer() {
return mDragLayer;
}
@@ -1790,21 +1696,14 @@ public class Launcher extends Activity
return mModel;
}
+ public ModelWriter getModelWriter() {
+ return mModelWriter;
+ }
+
public SharedPreferences getSharedPrefs() {
return mSharedPrefs;
}
- public DeviceProfile getDeviceProfile() {
- return mDeviceProfile;
- }
-
- public void closeSystemDialogs() {
- getWindow().closeAllPanels();
-
- // Whatever we were doing is hereby canceled.
- setWaitingForResult(null);
- }
-
@Override
protected void onNewIntent(Intent intent) {
long startTime = 0;
@@ -1819,22 +1718,36 @@ public class Launcher extends Activity
// Check this condition before handling isActionMain, as this will get reset.
boolean shouldMoveToDefaultScreen = alreadyOnHome &&
- mState == State.WORKSPACE && getTopFloatingView() == null;
+ mState == State.WORKSPACE && AbstractFloatingView.getTopOpenView(this) == null;
boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
if (isActionMain) {
- // also will cancel mWaitingForResult.
- closeSystemDialogs();
-
if (mWorkspace == null) {
// Can be cases where mWorkspace is null, this prevents a NPE
return;
}
- // In all these cases, only animate if we're already on home
+
+ // Note: There should be at most one log per method call. This is enforced implicitly
+ // by using if-else statements.
+ UserEventDispatcher ued = getUserEventDispatcher();
+
+ // TODO: Log this case.
mWorkspace.exitWidgetResizeMode();
- closeFolder(alreadyOnHome);
- closeShortcutsContainer(alreadyOnHome);
+ AbstractFloatingView topOpenView = AbstractFloatingView.getTopOpenView(this);
+ if (topOpenView instanceof PopupContainerWithArrow) {
+ ued.logActionCommand(Action.Command.HOME_INTENT,
+ topOpenView.getExtendedTouchView(), ContainerType.DEEPSHORTCUTS);
+ } else if (topOpenView instanceof Folder) {
+ ued.logActionCommand(Action.Command.HOME_INTENT,
+ ((Folder) topOpenView).getFolderIcon(), ContainerType.FOLDER);
+ } else if (alreadyOnHome) {
+ ued.logActionCommand(Action.Command.HOME_INTENT,
+ mWorkspace.getState().containerType, mWorkspace.getCurrentPage());
+ }
+
+ // In all these cases, only animate if we're already on home
+ AbstractFloatingView.closeAllOpenViews(this, alreadyOnHome);
exitSpringLoadedDragMode();
// If we are already on home, then just animate back to the workspace,
@@ -1866,6 +1779,7 @@ public class Launcher extends Activity
mLauncherCallbacks.onHomeIntent();
}
}
+ PinItemDragListener.handleDragRequest(this, intent);
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onNewIntent(intent);
@@ -1917,11 +1831,9 @@ public class Launcher extends Activity
super.onSaveInstanceState(outState);
outState.putInt(RUNTIME_STATE, mState.ordinal());
- // We close any open folder since it will not be re-opened, and we need to make sure
- // this state is reflected.
- // TODO: Move folderInfo.isOpened out of the model and make it a UI state.
- closeFolder(false);
- closeShortcutsContainer(false);
+ // We close any open folders and shortcut containers since they will not be re-opened,
+ // and we need to make sure this state is reflected.
+ AbstractFloatingView.closeAllOpenViews(this, false);
if (mPendingRequestArgs != null) {
outState.putParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS, mPendingRequestArgs);
@@ -1939,9 +1851,6 @@ public class Launcher extends Activity
public void onDestroy() {
super.onDestroy();
- // Remove all pending runnables
- mHandler.removeMessages(ADVANCE_MSG);
- mHandler.removeMessages(0);
mWorkspace.removeCallbacks(mBuildLayersRunnable);
mWorkspace.removeFolderListeners();
@@ -1950,7 +1859,7 @@ public class Launcher extends Activity
// been created. In this case, don't interfere with the new Launcher.
if (mModel.isCurrentCallbacks(this)) {
mModel.stopLoader();
- LauncherAppState.getInstance().setLauncher(null);
+ LauncherAppState.getInstance(this).setLauncher(null);
}
if (mRotationPrefChangeHandler != null) {
@@ -1964,15 +1873,11 @@ public class Launcher extends Activity
}
mAppWidgetHost = null;
- mWidgetsToAdvance.clear();
-
TextKeyListener.getInstance().release();
((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
.removeAccessibilityStateChangeListener(this);
- unregisterReceiver(mUiBroadcastReceiver);
-
LauncherAnimUtils.onDestroyActivity();
if (mLauncherCallbacks != null) {
@@ -2107,7 +2012,7 @@ public class Launcher extends Activity
}
}
- private void setWaitingForResult(PendingRequestArgs args) {
+ public void setWaitingForResult(PendingRequestArgs args) {
boolean isLocked = isWorkspaceLocked();
mPendingRequestArgs = args;
if (isLocked != isWorkspaceLocked()) {
@@ -2122,24 +2027,18 @@ public class Launcher extends Activity
}
void addAppWidgetFromDropImpl(int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget,
- LauncherAppWidgetProviderInfo appWidgetInfo) {
+ WidgetAddFlowHandler addFlowHandler) {
if (LOGD) {
Log.d(TAG, "Adding widget from drop");
}
- addAppWidgetImpl(appWidgetId, info, boundWidget, appWidgetInfo, 0);
+ addAppWidgetImpl(appWidgetId, info, boundWidget, addFlowHandler, 0);
}
void addAppWidgetImpl(int appWidgetId, ItemInfo info,
- AppWidgetHostView boundWidget, LauncherAppWidgetProviderInfo appWidgetInfo,
- int delay) {
- if (appWidgetInfo.configure != null) {
- setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, appWidgetInfo, info));
+ AppWidgetHostView boundWidget, WidgetAddFlowHandler addFlowHandler, int delay) {
+ if (!addFlowHandler.startConfigActivity(this, appWidgetId, info, REQUEST_CREATE_APPWIDGET)) {
+ // If the configuration flow was not started, add the widget
- // Launch over to configure widget, if needed
- mAppWidgetManager.startConfigActivity(appWidgetInfo, appWidgetId, this,
- mAppWidgetHost, REQUEST_CREATE_APPWIDGET);
- } else {
- // Otherwise just add it
Runnable onComplete = new Runnable() {
@Override
public void run() {
@@ -2148,14 +2047,14 @@ public class Launcher extends Activity
null);
}
};
- completeAddAppWidget(appWidgetId, info, boundWidget, appWidgetInfo);
+ completeAddAppWidget(appWidgetId, info, boundWidget, addFlowHandler.getProviderInfo(this));
mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete, delay, false);
}
}
protected void moveToCustomContentScreen(boolean animate) {
// Close any folders that may be open.
- closeFolder();
+ AbstractFloatingView.closeAllOpenViews(this, animate);
mWorkspace.moveToCustomContentScreen(animate);
}
@@ -2176,7 +2075,7 @@ public class Launcher extends Activity
addAppWidgetFromDrop((PendingAddWidgetInfo) info);
break;
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
- processShortcutFromDrop(info);
+ processShortcutFromDrop((PendingAddShortcutInfo) info);
break;
default:
throw new IllegalStateException("Unknown item type: " + info.itemType);
@@ -2186,10 +2085,12 @@ public class Launcher extends Activity
/**
* Process a shortcut drop.
*/
- private void processShortcutFromDrop(PendingAddItemInfo info) {
+ private void processShortcutFromDrop(PendingAddShortcutInfo info) {
Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(info.componentName);
setWaitingForResult(PendingRequestArgs.forIntent(REQUEST_CREATE_SHORTCUT, intent, info));
- Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_SHORTCUT);
+ if (!info.activityInfo.startConfigActivity(this, REQUEST_CREATE_SHORTCUT)) {
+ handleActivityResult(REQUEST_CREATE_SHORTCUT, RESULT_CANCELED, null);
+ }
}
/**
@@ -2198,6 +2099,7 @@ public class Launcher extends Activity
private void addAppWidgetFromDrop(PendingAddWidgetInfo info) {
AppWidgetHostView hostView = info.boundWidget;
int appWidgetId;
+ WidgetAddFlowHandler addFlowHandler = info.getHandler();
if (hostView != null) {
// In the case where we've prebound the widget, we remove it from the DragLayer
if (LOGD) {
@@ -2206,7 +2108,7 @@ public class Launcher extends Activity
getDragLayer().removeView(hostView);
appWidgetId = hostView.getAppWidgetId();
- addAppWidgetFromDropImpl(appWidgetId, info, hostView, info.info);
+ addAppWidgetFromDropImpl(appWidgetId, info, hostView, addFlowHandler);
// Clear the boundWidget so that it doesn't get destroyed.
info.boundWidget = null;
@@ -2219,17 +2121,9 @@ public class Launcher extends Activity
boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
appWidgetId, info.info, options);
if (success) {
- addAppWidgetFromDropImpl(appWidgetId, info, null, info.info);
+ addAppWidgetFromDropImpl(appWidgetId, info, null, addFlowHandler);
} else {
- setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, info.info, info));
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName);
- mAppWidgetManager.getUser(info.info)
- .addToIntent(intent, AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE);
- // TODO: we need to make sure that this accounts for the options bundle.
- // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
- startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
+ addFlowHandler.startBindFlow(this, appWidgetId, info, REQUEST_BIND_APPWIDGET);
}
}
}
@@ -2240,14 +2134,11 @@ public class Launcher extends Activity
folderInfo.title = getText(R.string.folder_name);
// Update the model
- LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screenId,
- cellX, cellY);
+ getModelWriter().addItemToDatabase(folderInfo, container, screenId, cellX, cellY);
// Create the view
- FolderIcon newFolder =
- FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo, mIconCache);
- mWorkspace.addInScreen(newFolder, container, screenId, cellX, cellY, 1, 1,
- isWorkspaceLocked());
+ FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo);
+ mWorkspace.addInScreen(newFolder, folderInfo);
// Force measure the new folder icon
CellLayout parent = mWorkspace.getParentCellLayoutForView(newFolder);
parent.getShortcutsAndWidgets().measureChild(newFolder);
@@ -2271,7 +2162,7 @@ public class Launcher extends Activity
mWorkspace.removeWorkspaceItem(v);
}
if (deleteFromDb) {
- LauncherModel.deleteItemFromDatabase(this, itemInfo);
+ getModelWriter().deleteItemFromDatabase(itemInfo);
}
} else if (itemInfo instanceof FolderInfo) {
final FolderInfo folderInfo = (FolderInfo) itemInfo;
@@ -2280,12 +2171,11 @@ public class Launcher extends Activity
}
mWorkspace.removeWorkspaceItem(v);
if (deleteFromDb) {
- LauncherModel.deleteFolderAndContentsFromDatabase(this, folderInfo);
+ getModelWriter().deleteFolderAndContentsFromDatabase(folderInfo);
}
} else if (itemInfo instanceof LauncherAppWidgetInfo) {
final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo;
mWorkspace.removeWorkspaceItem(v);
- removeWidgetToAutoAdvance(v);
if (deleteFromDb) {
deleteWidgetInfo(widgetInfo);
}
@@ -2310,30 +2200,12 @@ public class Launcher extends Activity
}
}.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
}
- LauncherModel.deleteItemFromDatabase(this, widgetInfo);
+ getModelWriter().deleteItemFromDatabase(widgetInfo);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_HOME:
- return true;
- case KeyEvent.KEYCODE_VOLUME_DOWN:
- if (Utilities.isPropertyEnabled(DUMP_STATE_PROPERTY)) {
- dumpState();
- return true;
- }
- break;
- }
- } else if (event.getAction() == KeyEvent.ACTION_UP) {
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_HOME:
- return true;
- }
- }
-
- return super.dispatchKeyEvent(event);
+ return (event.getKeyCode() == KeyEvent.KEYCODE_HOME) || super.dispatchKeyEvent(event);
}
@Override
@@ -2347,22 +2219,34 @@ public class Launcher extends Activity
return;
}
- if (getOpenShortcutsContainer() != null) {
- closeShortcutsContainer();
+ // Note: There should be at most one log per method call. This is enforced implicitly
+ // by using if-else statements.
+ UserEventDispatcher ued = getUserEventDispatcher();
+ AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
+ if (topView != null) {
+ if (topView.getActiveTextView() != null) {
+ topView.getActiveTextView().dispatchBackKey();
+ } else {
+ if (topView instanceof PopupContainerWithArrow) {
+ ued.logActionCommand(Action.Command.BACK,
+ topView.getExtendedTouchView(), ContainerType.DEEPSHORTCUTS);
+ } else if (topView instanceof Folder) {
+ ued.logActionCommand(Action.Command.BACK,
+ ((Folder) topView).getFolderIcon(), ContainerType.FOLDER);
+ }
+ topView.close(true);
+ }
} else if (isAppsViewVisible()) {
+ ued.logActionCommand(Action.Command.BACK, ContainerType.ALLAPPS);
showWorkspace(true);
} else if (isWidgetsViewVisible()) {
+ ued.logActionCommand(Action.Command.BACK, ContainerType.WIDGETS);
showOverviewMode(true);
} else if (mWorkspace.isInOverviewMode()) {
+ ued.logActionCommand(Action.Command.BACK, ContainerType.OVERVIEW);
showWorkspace(true);
- } else if (mWorkspace.getOpenFolder() != null) {
- Folder openFolder = mWorkspace.getOpenFolder();
- if (openFolder.isEditingName()) {
- openFolder.dismissEditingName();
- } else {
- closeFolder();
- }
} else {
+ // TODO: Log this case.
mWorkspace.exitWidgetResizeMode();
// Back button is a no-op here, but give at least some feedback for the button press
@@ -2388,6 +2272,9 @@ public class Launcher extends Activity
if (v instanceof Workspace) {
if (mWorkspace.isInOverviewMode()) {
+ getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Type.TOUCH,
+ LauncherLogProto.Action.Direction.NONE,
+ LauncherLogProto.ContainerType.OVERVIEW, mWorkspace.getCurrentPage());
showWorkspace(true);
}
return;
@@ -2395,7 +2282,11 @@ public class Launcher extends Activity
if (v instanceof CellLayout) {
if (mWorkspace.isInOverviewMode()) {
- mWorkspace.snapToPageFromOverView(mWorkspace.indexOfChild(v));
+ int page = mWorkspace.indexOfChild(v);
+ getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Type.TOUCH,
+ LauncherLogProto.Action.Direction.NONE,
+ LauncherLogProto.ContainerType.OVERVIEW, page);
+ mWorkspace.snapToPageFromOverView(page);
showWorkspace(true);
}
return;
@@ -2436,54 +2327,29 @@ public class Launcher extends Activity
final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
if (v.isReadyForClickSetup()) {
+ LauncherAppWidgetProviderInfo appWidgetInfo =
+ mAppWidgetManager.findProvider(info.providerName, info.user);
+ if (appWidgetInfo == null) {
+ return;
+ }
+ WidgetAddFlowHandler addFlowHandler = new WidgetAddFlowHandler(appWidgetInfo);
+
if (info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
if (!info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
// This should not happen, as we make sure that an Id is allocated during bind.
return;
}
- LauncherAppWidgetProviderInfo appWidgetInfo =
- mAppWidgetManager.findProvider(info.providerName, info.user);
- if (appWidgetInfo != null) {
- setWaitingForResult(PendingRequestArgs
- .forWidgetInfo(info.appWidgetId, appWidgetInfo, info));
-
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, info.appWidgetId);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, appWidgetInfo.provider);
- mAppWidgetManager.getUser(appWidgetInfo)
- .addToIntent(intent, AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE);
- startActivityForResult(intent, REQUEST_BIND_PENDING_APPWIDGET);
- }
+ addFlowHandler.startBindFlow(this, info.appWidgetId, info,
+ REQUEST_BIND_PENDING_APPWIDGET);
} else {
- LauncherAppWidgetProviderInfo appWidgetInfo =
- mAppWidgetManager.getLauncherAppWidgetInfo(info.appWidgetId);
- if (appWidgetInfo != null) {
- startRestoredWidgetReconfigActivity(appWidgetInfo, info);
- }
+ addFlowHandler.startConfigActivity(this, info, REQUEST_RECONFIGURE_APPWIDGET);
}
- } else if (info.installProgress < 0) {
- // The install has not been queued
- final String packageName = info.providerName.getPackageName();
- showBrokenAppInstallDialog(packageName,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- startActivitySafely(v, LauncherModel.getMarketIntent(packageName), info);
- }
- });
} else {
- // Download has started.
final String packageName = info.providerName.getPackageName();
- startActivitySafely(v, LauncherModel.getMarketIntent(packageName), info);
+ onClickPendingAppItem(v, packageName, info.installProgress >= 0);
}
}
- private void startRestoredWidgetReconfigActivity(
- LauncherAppWidgetProviderInfo provider, LauncherAppWidgetInfo info) {
- setWaitingForResult(PendingRequestArgs.forWidgetInfo(info.appWidgetId, provider, info));
- mAppWidgetManager.startConfigActivity(provider,
- info.appWidgetId, this, mAppWidgetHost, REQUEST_RECONFIGURE_APPWIDGET);
- }
-
/**
* Event handler for the "grid" button that appears on the home screen, which
* enters all apps mode.
@@ -2493,8 +2359,8 @@ public class Launcher extends Activity
protected void onClickAllAppsButton(View v) {
if (LOGD) Log.d(TAG, "onClickAllAppsButton");
if (!isAppsViewVisible()) {
- getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.TAP,
- LauncherLogProto.ALL_APPS_BUTTON);
+ getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.ALL_APPS_BUTTON);
showAppsView(true /* animated */, true /* updatePredictedApps */,
false /* focusSearchBar */);
}
@@ -2503,28 +2369,47 @@ public class Launcher extends Activity
protected void onLongClickAllAppsButton(View v) {
if (LOGD) Log.d(TAG, "onLongClickAllAppsButton");
if (!isAppsViewVisible()) {
- getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.LONGPRESS,
- LauncherLogProto.ALL_APPS_BUTTON);
+ getUserEventDispatcher().logActionOnControl(Action.Touch.LONGPRESS,
+ ControlType.ALL_APPS_BUTTON);
showAppsView(true /* animated */,
true /* updatePredictedApps */, true /* focusSearchBar */);
}
}
- private void showBrokenAppInstallDialog(final String packageName,
- DialogInterface.OnClickListener onSearchClickListener) {
+ private void onClickPendingAppItem(final View v, final String packageName,
+ boolean downloadStarted) {
+ if (downloadStarted) {
+ // If the download has started, simply direct to the market app.
+ startMarketIntentForPackage(v, packageName);
+ return;
+ }
new AlertDialog.Builder(this)
.setTitle(R.string.abandoned_promises_title)
.setMessage(R.string.abandoned_promise_explanation)
- .setPositiveButton(R.string.abandoned_search, onSearchClickListener)
+ .setPositiveButton(R.string.abandoned_search, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ startMarketIntentForPackage(v, packageName);
+ }
+ })
.setNeutralButton(R.string.abandoned_clean_this,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
- final UserHandleCompat user = UserHandleCompat.myUserHandle();
+ final UserHandle user = Process.myUserHandle();
mWorkspace.removeAbandonedPromise(packageName, user);
}
})
.create().show();
- return;
+ }
+
+ private void startMarketIntentForPackage(View v, String packageName) {
+ ItemInfo item = (ItemInfo) v.getTag();
+ Intent intent = PackageManagerHelper.getMarketIntent(packageName);
+ boolean success = startActivitySafely(v, intent, item);
+ if (success && v instanceof BubbleTextView) {
+ mWaitingForResume = (BubbleTextView) v;
+ mWaitingForResume.setStayPressed(true);
+ }
}
/**
@@ -2568,17 +2453,14 @@ public class Launcher extends Activity
}
// Check for abandoned promise
- if ((v instanceof BubbleTextView)
- && shortcut.isPromise()
- && !shortcut.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE)) {
- showBrokenAppInstallDialog(
- shortcut.getTargetComponent().getPackageName(),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- startAppShortcutOrInfoActivity(v);
- }
- });
- return;
+ if ((v instanceof BubbleTextView) && shortcut.isPromise()) {
+ String packageName = shortcut.intent.getComponent() != null ?
+ shortcut.intent.getComponent().getPackageName() : shortcut.intent.getPackage();
+ if (!TextUtils.isEmpty(packageName)) {
+ onClickPendingAppItem(v, packageName,
+ shortcut.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE));
+ return;
+ }
}
// Start activities
@@ -2592,7 +2474,7 @@ public class Launcher extends Activity
throw new IllegalArgumentException("Input must have a valid intent");
}
boolean success = startActivitySafely(v, intent, item);
- getUserEventDispatcher().logAppLaunch(v, intent);
+ getUserEventDispatcher().logAppLaunch(v, intent); // TODO for discovered apps b/35802115
if (success && v instanceof BubbleTextView) {
mWaitingForResume = (BubbleTextView) v;
@@ -2611,10 +2493,10 @@ public class Launcher extends Activity
throw new IllegalArgumentException("Input must be a FolderIcon");
}
- FolderIcon folderIcon = (FolderIcon) v;
- if (!folderIcon.getFolderInfo().opened && !folderIcon.getFolder().isDestroyed()) {
+ Folder folder = ((FolderIcon) v).getFolder();
+ if (!folder.isOpen() && !folder.isDestroyed()) {
// Open the requested folder
- openFolder(folderIcon);
+ folder.animateOpen();
}
}
@@ -2636,25 +2518,32 @@ public class Launcher extends Activity
* on the home screen.
*/
public void onClickWallpaperPicker(View v) {
- if (!Utilities.isWallapaperAllowed(this)) {
+ if (!Utilities.isWallpaperAllowed(this)) {
Toast.makeText(this, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show();
return;
}
- String pickerPackage = getString(R.string.wallpaper_picker_package);
- if (TextUtils.isEmpty(pickerPackage)) {
- pickerPackage = PackageManagerHelper.getWallpaperPickerPackage(getPackageManager());
- }
-
int pageScroll = mWorkspace.getScrollForPage(mWorkspace.getPageNearestToCenterOfScreen());
float offset = mWorkspace.mWallpaperOffset.wallpaperOffsetForScroll(pageScroll);
-
setWaitingForResult(new PendingRequestArgs(new ItemInfo()));
Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
- .setPackage(pickerPackage)
.putExtra(Utilities.EXTRA_WALLPAPER_OFFSET, offset);
+
+ String pickerPackage = getString(R.string.wallpaper_picker_package);
+ boolean hasTargetPackage = TextUtils.isEmpty(pickerPackage);
+ if (!hasTargetPackage) {
+ intent.setPackage(pickerPackage);
+ }
+
intent.setSourceBounds(getViewBounds(v));
- startActivityForResult(intent, REQUEST_PICK_WALLPAPER, getActivityLaunchOptions(v));
+ try {
+ startActivityForResult(intent, REQUEST_PICK_WALLPAPER,
+ // If there is no target package, use the default intent chooser animation
+ hasTargetPackage ? getActivityLaunchOptions(v) : null);
+ } catch (ActivityNotFoundException e) {
+ setWaitingForResult(null);
+ Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ }
}
/**
@@ -2666,6 +2555,7 @@ public class Launcher extends Activity
Intent intent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
.setPackage(getPackageName());
intent.setSourceBounds(getViewBounds(v));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent, getActivityLaunchOptions(v));
}
@@ -2749,7 +2639,7 @@ public class Launcher extends Activity
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
String id = ((ShortcutInfo) info).getDeepShortcutId();
String packageName = intent.getPackage();
- LauncherAppState.getInstance().getShortcutManager().startShortcut(
+ DeepShortcutManager.getInstance(this).startShortcut(
packageName, id, intent.getSourceBounds(), optsBundle, info.user);
} else {
// Could be launching some bookkeeping activity
@@ -2778,7 +2668,8 @@ public class Launcher extends Activity
}
}
- private Bundle getActivityLaunchOptions(View v) {
+ @TargetApi(Build.VERSION_CODES.M)
+ public Bundle getActivityLaunchOptions(View v) {
if (Utilities.ATLEAST_MARSHMALLOW) {
int left = 0, top = 0;
int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
@@ -2804,7 +2695,7 @@ public class Launcher extends Activity
return null;
}
- private Rect getViewBounds(View v) {
+ public Rect getViewBounds(View v) {
int[] pos = new int[2];
v.getLocationOnScreen(pos);
return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
@@ -2821,11 +2712,7 @@ public class Launcher extends Activity
!intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
Bundle optsBundle = useLaunchAnimation ? getActivityLaunchOptions(v) : null;
- UserHandleCompat user = null;
- if (intent.hasExtra(AppInfo.EXTRA_PROFILE)) {
- long serialNumber = intent.getLongExtra(AppInfo.EXTRA_PROFILE, -1);
- user = UserManagerCompat.getInstance(this).getUserForSerialNumber(serialNumber);
- }
+ UserHandle user = item == null ? null : item.user;
// Prepare intent
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -2833,13 +2720,14 @@ public class Launcher extends Activity
intent.setSourceBounds(getViewBounds(v));
}
try {
- if (Utilities.ATLEAST_MARSHMALLOW && item != null
+ if (Utilities.ATLEAST_MARSHMALLOW
+ && (item instanceof ShortcutInfo)
&& (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
- || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
- && ((ShortcutInfo) item).promisedIntent == null) {
+ || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+ && !((ShortcutInfo) item).isPromise()) {
// Shortcuts need some special checks due to legacy reasons.
startShortcutIntentSafely(intent, optsBundle, item);
- } else if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
+ } else if (user == null || user.equals(Process.myUserHandle())) {
// Could be launching some bookkeeping activity
startActivity(intent, optsBundle);
} else {
@@ -2854,227 +2742,10 @@ public class Launcher extends Activity
return false;
}
- /**
- * This method draws the FolderIcon to an ImageView and then adds and positions that ImageView
- * in the DragLayer in the exact absolute location of the original FolderIcon.
- */
- private void copyFolderIconToImage(FolderIcon fi) {
- final int width = fi.getMeasuredWidth();
- final int height = fi.getMeasuredHeight();
-
- // Lazy load ImageView, Bitmap and Canvas
- if (mFolderIconImageView == null) {
- mFolderIconImageView = new ImageView(this);
- }
- if (mFolderIconBitmap == null || mFolderIconBitmap.getWidth() != width ||
- mFolderIconBitmap.getHeight() != height) {
- mFolderIconBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- mFolderIconCanvas = new Canvas(mFolderIconBitmap);
- }
-
- DragLayer.LayoutParams lp;
- if (mFolderIconImageView.getLayoutParams() instanceof DragLayer.LayoutParams) {
- lp = (DragLayer.LayoutParams) mFolderIconImageView.getLayoutParams();
- } else {
- lp = new DragLayer.LayoutParams(width, height);
- }
-
- // The layout from which the folder is being opened may be scaled, adjust the starting
- // view size by this scale factor.
- float scale = mDragLayer.getDescendantRectRelativeToSelf(fi, mRectForFolderAnimation);
- lp.customPosition = true;
- lp.x = mRectForFolderAnimation.left;
- lp.y = mRectForFolderAnimation.top;
- lp.width = (int) (scale * width);
- lp.height = (int) (scale * height);
-
- mFolderIconCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
- fi.draw(mFolderIconCanvas);
- mFolderIconImageView.setImageBitmap(mFolderIconBitmap);
- if (fi.getFolder() != null) {
- mFolderIconImageView.setPivotX(fi.getFolder().getPivotXForIconAnimation());
- mFolderIconImageView.setPivotY(fi.getFolder().getPivotYForIconAnimation());
- }
- // Just in case this image view is still in the drag layer from a previous animation,
- // we remove it and re-add it.
- if (mDragLayer.indexOfChild(mFolderIconImageView) != -1) {
- mDragLayer.removeView(mFolderIconImageView);
- }
- mDragLayer.addView(mFolderIconImageView, lp);
- if (fi.getFolder() != null) {
- fi.getFolder().bringToFront();
- }
- }
-
- private void growAndFadeOutFolderIcon(FolderIcon fi) {
- if (fi == null) return;
- FolderInfo info = (FolderInfo) fi.getTag();
- if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- CellLayout cl = (CellLayout) fi.getParent().getParent();
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) fi.getLayoutParams();
- cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
- }
-
- // Push an ImageView copy of the FolderIcon into the DragLayer and hide the original
- copyFolderIconToImage(fi);
- fi.setVisibility(View.INVISIBLE);
-
- ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(
- mFolderIconImageView, 0, 1.5f, 1.5f);
- if (Utilities.ATLEAST_LOLLIPOP) {
- oa.setInterpolator(new LogDecelerateInterpolator(100, 0));
- }
- oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
- oa.start();
- }
-
- private void shrinkAndFadeInFolderIcon(final FolderIcon fi, boolean animate) {
- if (fi == null) return;
- final CellLayout cl = (CellLayout) fi.getParent().getParent();
-
- // We remove and re-draw the FolderIcon in-case it has changed
- mDragLayer.removeView(mFolderIconImageView);
- copyFolderIconToImage(fi);
-
- if (cl != null) {
- cl.clearFolderLeaveBehind();
- }
-
- ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(mFolderIconImageView, 1, 1, 1);
- oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
- oa.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (cl != null) {
- // Remove the ImageView copy of the FolderIcon and make the original visible.
- mDragLayer.removeView(mFolderIconImageView);
- fi.setVisibility(View.VISIBLE);
- }
- }
- });
- oa.start();
- if (!animate) {
- oa.end();
- }
- }
-
- /**
- * Opens the user folder described by the specified tag. The opening of the folder
- * is animated relative to the specified View. If the View is null, no animation
- * is played.
- *
- * @param folderIcon The FolderIcon describing the folder to open.
- */
- public void openFolder(FolderIcon folderIcon) {
-
- Folder folder = folderIcon.getFolder();
- Folder openFolder = mWorkspace != null ? mWorkspace.getOpenFolder() : null;
- if (openFolder != null && openFolder != folder) {
- // Close any open folder before opening a folder.
- closeFolder();
- }
-
- FolderInfo info = folder.mInfo;
-
- info.opened = true;
-
- // While the folder is open, the position of the icon cannot change.
- ((CellLayout.LayoutParams) folderIcon.getLayoutParams()).canReorder = false;
-
- // Just verify that the folder hasn't already been added to the DragLayer.
- // There was a one-off crash where the folder had a parent already.
- if (folder.getParent() == null) {
- mDragLayer.addView(folder);
- mDragController.addDropTarget(folder);
- } else {
- Log.w(TAG, "Opening folder (" + folder + ") which already has a parent (" +
- folder.getParent() + ").");
- }
- folder.animateOpen();
-
- growAndFadeOutFolderIcon(folderIcon);
-
- // Notify the accessibility manager that this folder "window" has appeared and occluded
- // the workspace items
- folder.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
- }
-
- public void closeFolder() {
- closeFolder(true);
- }
-
- public void closeFolder(boolean animate) {
- Folder folder = mWorkspace != null ? mWorkspace.getOpenFolder() : null;
- if (folder != null) {
- if (folder.isEditingName()) {
- folder.dismissEditingName();
- }
- closeFolder(folder, animate);
- }
- }
-
- public void closeFolder(Folder folder, boolean animate) {
- animate &= !Utilities.isPowerSaverOn(this);
-
- folder.getInfo().opened = false;
-
- ViewGroup parent = (ViewGroup) folder.getParent().getParent();
- if (parent != null) {
- FolderIcon fi = (FolderIcon) mWorkspace.getViewForTag(folder.mInfo);
- shrinkAndFadeInFolderIcon(fi, animate);
- if (fi != null) {
- ((CellLayout.LayoutParams) fi.getLayoutParams()).canReorder = true;
- }
- }
- if (animate) {
- folder.animateClosed();
- } else {
- folder.close(false);
- }
-
- // Notify the accessibility manager that this folder "window" has disappeared and no
- // longer occludes the workspace items
- getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- }
-
- public void closeShortcutsContainer() {
- closeShortcutsContainer(true);
- }
-
- public void closeShortcutsContainer(boolean animate) {
- DeepShortcutsContainer deepShortcutsContainer = getOpenShortcutsContainer();
- if (deepShortcutsContainer != null) {
- if (animate) {
- deepShortcutsContainer.animateClose();
- } else {
- deepShortcutsContainer.close();
- }
- }
- }
-
- public View getTopFloatingView() {
- View topView = getOpenShortcutsContainer();
- if (topView == null) {
- topView = getWorkspace().getOpenFolder();
- }
- return topView;
- }
-
- /**
- * @return The open shortcuts container, or null if there is none
- */
- public DeepShortcutsContainer getOpenShortcutsContainer() {
- // Iterate in reverse order. Shortcuts container is added later to the dragLayer,
- // and will be one of the last views.
- for (int i = mDragLayer.getChildCount() - 1; i >= 0; i--) {
- View child = mDragLayer.getChildAt(i);
- if (child instanceof DeepShortcutsContainer
- && ((DeepShortcutsContainer) child).isOpen()) {
- return (DeepShortcutsContainer) child;
- }
- }
- return null;
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ mLastDispatchTouchEventX = ev.getX();
+ return super.dispatchTouchEvent(ev);
}
@Override
@@ -3089,9 +2760,16 @@ public class Launcher extends Activity
return true;
}
+
+ boolean ignoreLongPressToOverview =
+ mDeviceProfile.shouldIgnoreLongPressToOverview(mLastDispatchTouchEventX);
+
if (v instanceof Workspace) {
if (!mWorkspace.isInOverviewMode()) {
- if (!mWorkspace.isTouchActive()) {
+ if (!mWorkspace.isTouchActive() && !ignoreLongPressToOverview) {
+ getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
+ Action.Direction.NONE, ContainerType.WORKSPACE,
+ mWorkspace.getCurrentPage());
showOverviewMode(true);
mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
@@ -3118,13 +2796,21 @@ public class Launcher extends Activity
if (!mDragController.isDragging()) {
if (itemUnderLongClick == null) {
// User long pressed on empty space
- mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
if (mWorkspace.isInOverviewMode()) {
mWorkspace.startReordering(v);
+ getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
+ Action.Direction.NONE, ContainerType.OVERVIEW);
} else {
+ if (ignoreLongPressToOverview) {
+ return false;
+ }
+ getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
+ Action.Direction.NONE, ContainerType.WORKSPACE,
+ mWorkspace.getCurrentPage());
showOverviewMode(true);
}
+ mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
} else {
final boolean isAllAppsButton =
!FeatureFlags.NO_ALL_APPS_ICON && isHotseatLayout(v) &&
@@ -3132,17 +2818,7 @@ public class Launcher extends Activity
longClickCellInfo.cellX, longClickCellInfo.cellY));
if (!(itemUnderLongClick instanceof Folder || isAllAppsButton)) {
// User long pressed on an item
- DragOptions dragOptions = new DragOptions();
- if (itemUnderLongClick instanceof BubbleTextView) {
- BubbleTextView icon = (BubbleTextView) itemUnderLongClick;
- if (icon.hasDeepShortcuts()) {
- DeepShortcutsContainer dsc = DeepShortcutsContainer.showForIcon(icon);
- if (dsc != null) {
- dragOptions.deferDragCondition = dsc.createDeferDragCondition(null);
- }
- }
- }
- mWorkspace.startDrag(longClickCellInfo, dragOptions);
+ mWorkspace.startDrag(longClickCellInfo, new DragOptions());
}
}
}
@@ -3150,6 +2826,7 @@ public class Launcher extends Activity
}
boolean isHotseatLayout(View layout) {
+ // TODO: Remove this method
return mHotseat != null && layout != null &&
(layout instanceof CellLayout) && (layout == mHotseat.getLayout());
}
@@ -3219,11 +2896,7 @@ public class Launcher extends Activity
}
// Change the state *after* we've called all the transition code
- mState = State.WORKSPACE;
-
- // Resume the auto-advance of widgets
- mUserPresent = true;
- updateAutoAdvanceState();
+ setState(State.WORKSPACE);
if (changed) {
// Send an accessibility event to announce the context change
@@ -3260,12 +2933,30 @@ public class Launcher extends Activity
mWorkspace.setVisibility(View.VISIBLE);
mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
Workspace.State.OVERVIEW, animated, postAnimRunnable);
- mState = State.WORKSPACE;
+ setState(State.WORKSPACE);
+
// If animated from long press, then don't allow any of the controller in the drag
// layer to intercept any remaining touch.
mWorkspace.requestDisallowInterceptTouchEvent(animated);
}
+ private void setState(State state) {
+ this.mState = state;
+ updateSoftInputMode();
+ }
+
+ private void updateSoftInputMode() {
+ if (FeatureFlags.LAUNCHER3_UPDATE_SOFT_INPUT_MODE) {
+ final int mode;
+ if (isAppsViewVisible()) {
+ mode = SOFT_INPUT_MODE_ALL_APPS;
+ } else {
+ mode = SOFT_INPUT_MODE_DEFAULT;
+ }
+ getWindow().setSoftInputMode(mode);
+ }
+ }
+
/**
* Shows the apps view.
*/
@@ -3321,20 +3012,14 @@ public class Launcher extends Activity
}
if (toState == State.APPS) {
- mStateTransitionAnimation.startAnimationToAllApps(mWorkspace.getState(), animated,
- focusSearchBar);
+ mStateTransitionAnimation.startAnimationToAllApps(animated, focusSearchBar);
} else {
- mStateTransitionAnimation.startAnimationToWidgets(mWorkspace.getState(), animated);
+ mStateTransitionAnimation.startAnimationToWidgets(animated);
}
// Change the state *after* we've called all the transition code
- mState = toState;
-
- // Pause the auto-advance of widgets until we are out of AllApps
- mUserPresent = false;
- updateAutoAdvanceState();
- closeFolder();
- closeShortcutsContainer();
+ setState(toState);
+ AbstractFloatingView.closeAllOpenViews(this);
// Send an accessibility event to announce the context change
getWindow().getDecorView()
@@ -3347,7 +3032,7 @@ public class Launcher extends Activity
* new state.
*/
public Animator startWorkspaceStateChangeAnimation(Workspace.State toState,
- boolean animated, HashMap layerViews) {
+ boolean animated, AnimationLayerSet layerViews) {
Workspace.State fromState = mWorkspace.getState();
Animator anim = mWorkspace.setStateWithAnimation(toState, animated, layerViews);
updateInteraction(fromState, toState);
@@ -3363,16 +3048,7 @@ public class Launcher extends Activity
mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
Workspace.State.SPRING_LOADED, true /* animated */,
null /* onCompleteRunnable */);
-
- if (isAppsViewVisible()) {
- mState = State.APPS_SPRING_LOADED;
- } else if (isWidgetsViewVisible()) {
- mState = State.WIDGETS_SPRING_LOADED;
- } else if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
- mState = State.WORKSPACE_SPRING_LOADED;
- } else {
- mState = State.WORKSPACE;
- }
+ setState(State.WORKSPACE_SPRING_LOADED);
}
public void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay,
@@ -3407,7 +3083,7 @@ public class Launcher extends Activity
|| mState == State.WIDGETS_SPRING_LOADED;
}
- void exitSpringLoadedDragMode() {
+ public void exitSpringLoadedDragMode() {
if (mState == State.APPS_SPRING_LOADED) {
showAppsView(true /* animated */,
false /* updatePredictedApps */, false /* focusSearchBar */);
@@ -3552,13 +3228,15 @@ public class Launcher extends Activity
if (LauncherAppState.PROFILE_STARTUP) {
Trace.beginSection("Starting page bind");
}
+
+ AbstractFloatingView.closeAllOpenViews(this);
+
setWorkspaceLoading(true);
// Clear the workspace because it's going to be rebound
mWorkspace.clearDropTargets();
mWorkspace.removeAllWorkspaceScreens();
- mWidgetsToAdvance.clear();
if (mHotseat != null) {
mHotseat.resetLayout();
}
@@ -3648,25 +3326,25 @@ public class Launcher extends Activity
* Implementation of the method from LauncherModel.Callbacks.
*/
@Override
- public void bindItems(final ArrayList shortcuts, final int start, final int end,
+ public void bindItems(final ArrayList items, final int start, final int end,
final boolean forceAnimateIcons) {
Runnable r = new Runnable() {
public void run() {
- bindItems(shortcuts, start, end, forceAnimateIcons);
+ bindItems(items, start, end, forceAnimateIcons);
}
};
if (waitUntilResume(r)) {
return;
}
- // Get the list of added shortcuts and intersect them with the set of shortcuts here
+ // Get the list of added items and intersect them with the set of items here
final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
final Collection bounceAnims = new ArrayList();
final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
Workspace workspace = mWorkspace;
- long newShortcutsScreenId = -1;
+ long newItemsScreenId = -1;
for (int i = start; i < end; i++) {
- final ItemInfo item = shortcuts.get(i);
+ final ItemInfo item = items.get(i);
// Short circuit if we are loading dock items for a configuration which has no dock
if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
@@ -3678,15 +3356,33 @@ public class Launcher extends Activity
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
- case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+ case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
ShortcutInfo info = (ShortcutInfo) item;
view = createShortcut(info);
break;
- case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
+ }
+ case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
view = FolderIcon.fromXml(R.layout.folder_icon, this,
(ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
- (FolderInfo) item, mIconCache);
+ (FolderInfo) item);
break;
+ }
+ case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: {
+ LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item;
+ if (mIsSafeModeEnabled) {
+ view = new PendingAppWidgetHostView(this, info, mIconCache, true);
+ } else {
+ LauncherAppWidgetProviderInfo providerInfo =
+ mAppWidgetManager.getLauncherAppWidgetInfo(info.appWidgetId);
+ if (providerInfo == null) {
+ deleteWidgetInfo(info);
+ continue;
+ }
+ view = mAppWidgetHost.createView(this, info.appWidgetId, providerInfo);
+ }
+ prepareAppWidget((AppWidgetHostView) view, info);
+ break;
+ }
default:
throw new RuntimeException("Invalid Item Type");
}
@@ -3705,35 +3401,34 @@ public class Launcher extends Activity
throw (new RuntimeException(desc));
} else {
Log.d(TAG, desc);
- LauncherModel.deleteItemFromDatabase(this, item);
+ getModelWriter().deleteItemFromDatabase(item);
continue;
}
}
}
- workspace.addInScreenFromBind(view, item.container, item.screenId, item.cellX,
- item.cellY, 1, 1);
+ workspace.addInScreenFromBind(view, item);
if (animateIcons) {
// Animate all the applications up now
view.setAlpha(0f);
view.setScaleX(0f);
view.setScaleY(0f);
bounceAnims.add(createNewAppBounceAnimation(view, i));
- newShortcutsScreenId = item.screenId;
+ newItemsScreenId = item.screenId;
}
}
if (animateIcons) {
// Animate to the correct page
- if (newShortcutsScreenId > -1) {
+ if (newItemsScreenId > -1) {
long currentScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getNextPage());
- final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newShortcutsScreenId);
+ final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newItemsScreenId);
final Runnable startBounceAnimRunnable = new Runnable() {
public void run() {
anim.playTogether(bounceAnims);
anim.start();
}
};
- if (newShortcutsScreenId != currentScreenId) {
+ if (newItemsScreenId != currentScreenId) {
// We post the animation slightly delayed to prevent slowdowns
// when we are loading right after we return to launcher.
mWorkspace.postDelayed(new Runnable() {
@@ -3753,15 +3448,6 @@ public class Launcher extends Activity
workspace.requestLayout();
}
- private void bindSafeModeWidget(LauncherAppWidgetInfo item) {
- PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item, true);
- view.updateIcon(mIconCache);
- view.updateAppWidget(null);
- view.setOnClickListener(this);
- addAppWidgetToWorkspace(view, item, null, false);
- mWorkspace.requestLayout();
- }
-
/**
* Add the views for a widget to the workspace.
*
@@ -3778,7 +3464,11 @@ public class Launcher extends Activity
}
if (mIsSafeModeEnabled) {
- bindSafeModeWidget(item);
+ PendingAppWidgetHostView view =
+ new PendingAppWidgetHostView(this, item, mIconCache, true);
+ prepareAppWidget(view, item);
+ mWorkspace.addInScreen(view, item);
+ mWorkspace.requestLayout();
return;
}
@@ -3806,9 +3496,9 @@ public class Launcher extends Activity
if (DEBUG_WIDGETS) {
Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
+ " belongs to component " + item.providerName
- + ", as the povider is null");
+ + ", as the provider is null");
}
- LauncherModel.deleteItemFromDatabase(this, item);
+ getModelWriter().deleteItemFromDatabase(item);
return;
}
@@ -3821,7 +3511,7 @@ public class Launcher extends Activity
// Also try to bind the widget. If the bind fails, the user will be shown
// a click to setup UI, which will ask for the bind permission.
- PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(this, appWidgetInfo);
+ PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo);
pendingInfo.spanX = item.spanX;
pendingInfo.spanY = item.spanY;
pendingInfo.minSpanX = item.minSpanX;
@@ -3855,17 +3545,18 @@ public class Launcher extends Activity
: LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
}
- LauncherModel.updateItemInDatabase(this, item);
+ getModelWriter().updateItemInDatabase(item);
}
} else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
&& (appWidgetInfo.configure == null)) {
// The widget was marked as UI not ready, but there is no configure activity to
// update the UI.
item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
- LauncherModel.updateItemInDatabase(this, item);
+ getModelWriter().updateItemInDatabase(item);
}
}
+ final AppWidgetHostView view;
if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
if (DEBUG_WIDGETS) {
Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component "
@@ -3881,16 +3572,12 @@ public class Launcher extends Activity
item.minSpanX = appWidgetInfo.minSpanX;
item.minSpanY = appWidgetInfo.minSpanY;
- addAppWidgetToWorkspace(
- mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo),
- item, appWidgetInfo, false);
+ view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
} else {
- PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item, false);
- view.updateIcon(mIconCache);
- view.updateAppWidget(null);
- view.setOnClickListener(this);
- addAppWidgetToWorkspace(view, item, null, false);
+ view = new PendingAppWidgetHostView(this, item, mIconCache, false);
}
+ prepareAppWidget(view, item);
+ mWorkspace.addInScreen(view, item);
mWorkspace.requestLayout();
if (DEBUG_WIDGETS) {
@@ -3915,7 +3602,7 @@ public class Launcher extends Activity
info.restoreStatus = finalRestoreFlag;
mWorkspace.reinflateWidgetsIfNecessary();
- LauncherModel.updateItemInDatabase(this, info);
+ getModelWriter().updateItemInDatabase(info);
return info;
}
@@ -3981,14 +3668,6 @@ public class Launcher extends Activity
if (LauncherAppState.PROFILE_STARTUP) {
Trace.beginSection("Page bind completed");
}
- if (mSavedState != null) {
- if (!mWorkspace.hasFocus()) {
- mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
- }
-
- mSavedState = null;
- }
-
mWorkspace.restoreInstanceStateForRemainingPages();
setWorkspaceLoading(false);
@@ -4001,6 +3680,8 @@ public class Launcher extends Activity
InstallShortcutReceiver.disableAndFlushInstallQueue(this);
+ NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
+
if (mLauncherCallbacks != null) {
mLauncherCallbacks.finishBindingItems(false);
}
@@ -4070,21 +3751,7 @@ public class Launcher extends Activity
*/
@Override
public void bindDeepShortcutMap(MultiHashMap deepShortcutMapCopy) {
- mDeepShortcutMap = deepShortcutMapCopy;
- if (LOGD) Log.d(TAG, "bindDeepShortcutMap: " + mDeepShortcutMap);
- }
-
- public List getShortcutIdsForItem(ItemInfo info) {
- if (!DeepShortcutManager.supportsShortcuts(info)) {
- return Collections.EMPTY_LIST;
- }
- ComponentName component = info.getTargetComponent();
- if (component == null) {
- return Collections.EMPTY_LIST;
- }
-
- List ids = mDeepShortcutMap.get(new ComponentKey(component, info.user));
- return ids == null ? Collections.EMPTY_LIST : ids;
+ mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy);
}
/**
@@ -4131,7 +3798,7 @@ public class Launcher extends Activity
*/
@Override
public void bindShortcutsChanged(final ArrayList updated,
- final ArrayList removed, final UserHandleCompat user) {
+ final ArrayList removed, final UserHandle user) {
Runnable r = new Runnable() {
public void run() {
bindShortcutsChanged(updated, removed, user);
@@ -4151,7 +3818,7 @@ public class Launcher extends Activity
for (ShortcutInfo si : removed) {
if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- removedDeepShortcuts.add(ShortcutKey.fromShortcutInfo(si));
+ removedDeepShortcuts.add(ShortcutKey.fromItemInfo(si));
} else {
removedComponents.add(si.getTargetComponent());
}
@@ -4200,7 +3867,7 @@ public class Launcher extends Activity
@Override
public void bindWorkspaceComponentsRemoved(
final HashSet packageNames, final HashSet components,
- final UserHandleCompat user) {
+ final UserHandle user) {
Runnable r = new Runnable() {
public void run() {
bindWorkspaceComponentsRemoved(packageNames, components, user);
@@ -4236,76 +3903,56 @@ public class Launcher extends Activity
// Update AllApps
if (mAppsView != null) {
mAppsView.removeApps(appInfos);
+ tryAndUpdatePredictedApps();
}
}
- private Runnable mBindWidgetModelRunnable = new Runnable() {
+ private Runnable mBindAllWidgetsRunnable = new Runnable() {
public void run() {
- bindWidgetsModel(mWidgetsModel);
+ bindAllWidgets(mAllWidgets);
}
};
@Override
- public void bindWidgetsModel(WidgetsModel model) {
- if (waitUntilResume(mBindWidgetModelRunnable, true)) {
- mWidgetsModel = model;
+ public void bindAllWidgets(MultiHashMap allWidgets) {
+ if (waitUntilResume(mBindAllWidgetsRunnable, true)) {
+ mAllWidgets = allWidgets;
return;
}
- if (mWidgetsView != null && model != null) {
- mWidgetsView.addWidgets(model);
- mWidgetsModel = null;
+ if (mWidgetsView != null && allWidgets != null) {
+ mWidgetsView.setWidgets(allWidgets);
+ mAllWidgets = null;
}
+
+ AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
+ if (topView != null) {
+ topView.onWidgetsBound();
+ }
+ }
+
+ public List getWidgetsForPackageUser(PackageUserKey packageUserKey) {
+ return mWidgetsView.getWidgetsForPackageUser(packageUserKey);
}
@Override
public void notifyWidgetProvidersChanged() {
if (mWorkspace.getState().shouldUpdateWidget) {
- mModel.refreshAndBindWidgetsAndShortcuts(this, mWidgetsView.isEmpty());
+ refreshAndBindWidgetsForPackageUser(null);
}
}
- private int mapConfigurationOriActivityInfoOri(int configOri) {
- final Display d = getWindowManager().getDefaultDisplay();
- int naturalOri = Configuration.ORIENTATION_LANDSCAPE;
- switch (d.getRotation()) {
- case Surface.ROTATION_0:
- case Surface.ROTATION_180:
- // We are currently in the same basic orientation as the natural orientation
- naturalOri = configOri;
- break;
- case Surface.ROTATION_90:
- case Surface.ROTATION_270:
- // We are currently in the other basic orientation to the natural orientation
- naturalOri = (configOri == Configuration.ORIENTATION_LANDSCAPE) ?
- Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
- break;
- }
-
- int[] oriMap = {
- ActivityInfo.SCREEN_ORIENTATION_PORTRAIT,
- ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
- ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT,
- ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
- };
- // Since the map starts at portrait, we need to offset if this device's natural orientation
- // is landscape.
- int indexOffset = 0;
- if (naturalOri == Configuration.ORIENTATION_LANDSCAPE) {
- indexOffset = 1;
- }
- return oriMap[(d.getRotation() + indexOffset) % 4];
+ /**
+ * @param packageUser if null, refreshes all widgets and shortcuts, otherwise only
+ * refreshes the widgets and shortcuts associated with the given package/user
+ */
+ public void refreshAndBindWidgetsForPackageUser(@Nullable PackageUserKey packageUser) {
+ mModel.refreshAndBindWidgetsAndShortcuts(this, mWidgetsView.isEmpty(), packageUser);
}
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public void lockScreenOrientation() {
if (mRotationEnabled) {
- if (Utilities.ATLEAST_JB_MR2) {
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
- } else {
- setRequestedOrientation(mapConfigurationOriActivityInfoOri(getResources()
- .getConfiguration().orientation));
- }
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
}
}
@@ -4346,117 +3993,118 @@ public class Launcher extends Activity
return true;
}
- // TODO: These method should be a part of LauncherSearchCallback
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- public ItemInfo createAppDragInfo(Intent appLaunchIntent) {
- // Called from search suggestion
- UserHandleCompat user = null;
- if (Utilities.ATLEAST_LOLLIPOP) {
- UserHandle userHandle = appLaunchIntent.getParcelableExtra(Intent.EXTRA_USER);
- if (userHandle != null) {
- user = UserHandleCompat.fromUser(userHandle);
- }
- }
- return createAppDragInfo(appLaunchIntent, user);
- }
-
- // TODO: This method should be a part of LauncherSearchCallback
- public ItemInfo createAppDragInfo(Intent intent, UserHandleCompat user) {
- if (user == null) {
- user = UserHandleCompat.myUserHandle();
- }
-
- // Called from search suggestion, add the profile extra to the intent to ensure that we
- // can launch it correctly
- LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
- LauncherActivityInfoCompat activityInfo = launcherApps.resolveActivity(intent, user);
- if (activityInfo == null) {
- return null;
- }
- return new AppInfo(this, activityInfo, user, mIconCache);
- }
-
- // TODO: This method should be a part of LauncherSearchCallback
- public ItemInfo createShortcutDragInfo(Intent shortcutIntent, CharSequence caption,
- Bitmap icon) {
- return new ShortcutInfo(shortcutIntent, caption, caption, icon,
- UserHandleCompat.myUserHandle());
- }
-
protected void moveWorkspaceToDefaultScreen() {
mWorkspace.moveToDefaultScreen(false);
}
/**
- * Returns a FastBitmapDrawable with the icon, accurately sized.
+ * $ adb shell dumpsys activity com.android.launcher3.Launcher [--all]
*/
- public FastBitmapDrawable createIconDrawable(Bitmap icon) {
- FastBitmapDrawable d = new FastBitmapDrawable(icon);
- d.setFilterBitmap(true);
- resizeIconDrawable(d);
- return d;
- }
-
- /**
- * Resizes an icon drawable to the correct icon size.
- */
- public Drawable resizeIconDrawable(Drawable icon) {
- icon.setBounds(0, 0, mDeviceProfile.iconSizePx, mDeviceProfile.iconSizePx);
- return icon;
- }
-
- /**
- * Prints out out state for debugging.
- */
- public void dumpState() {
- Log.d(TAG, "BEGIN launcher3 dump state for launcher " + this);
- Log.d(TAG, "mSavedState=" + mSavedState);
- Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading);
- Log.d(TAG, "mPendingRequestArgs=" + mPendingRequestArgs);
- Log.d(TAG, "mPendingActivityResult=" + mPendingActivityResult);
- mModel.dumpState();
- // TODO(hyunyoungs): add mWidgetsView.dumpState(); or mWidgetsModel.dumpState();
-
- Log.d(TAG, "END launcher3 dump state");
- }
-
@Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
super.dump(prefix, fd, writer, args);
- // Dump workspace
- writer.println(prefix + "Workspace Items");
- for (int i = mWorkspace.numCustomPages(); i < mWorkspace.getPageCount(); i++) {
- writer.println(prefix + " Homescreen " + i);
- ViewGroup layout = ((CellLayout) mWorkspace.getPageAt(i)).getShortcutsAndWidgets();
+ if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
+ writer.println(prefix + "Workspace Items");
+ for (int i = mWorkspace.numCustomPages(); i < mWorkspace.getPageCount(); i++) {
+ writer.println(prefix + " Homescreen " + i);
+
+ ViewGroup layout = ((CellLayout) mWorkspace.getPageAt(i)).getShortcutsAndWidgets();
+ for (int j = 0; j < layout.getChildCount(); j++) {
+ Object tag = layout.getChildAt(j).getTag();
+ if (tag != null) {
+ writer.println(prefix + " " + tag.toString());
+ }
+ }
+ }
+
+ writer.println(prefix + " Hotseat");
+ ViewGroup layout = mHotseat.getLayout().getShortcutsAndWidgets();
for (int j = 0; j < layout.getChildCount(); j++) {
Object tag = layout.getChildAt(j).getTag();
if (tag != null) {
writer.println(prefix + " " + tag.toString());
}
}
- }
- writer.println(prefix + " Hotseat");
- ViewGroup layout = mHotseat.getLayout().getShortcutsAndWidgets();
- for (int j = 0; j < layout.getChildCount(); j++) {
- Object tag = layout.getChildAt(j).getTag();
- if (tag != null) {
- writer.println(prefix + " " + tag.toString());
+ try {
+ FileLog.flushAll(writer);
+ } catch (Exception e) {
+ // Ignore
}
}
- try {
- FileLog.flushAll(writer);
- } catch (Exception e) {
- // Ignore
- }
+ writer.println(prefix + "Misc:");
+ writer.print(prefix + "\tmWorkspaceLoading=" + mWorkspaceLoading);
+ writer.print(" mPendingRequestArgs=" + mPendingRequestArgs);
+ writer.println(" mPendingActivityResult=" + mPendingActivityResult);
+
+ mModel.dumpState(prefix, fd, writer, args);
if (mLauncherCallbacks != null) {
mLauncherCallbacks.dump(prefix, fd, writer, args);
}
}
+ @Override
+ @TargetApi(Build.VERSION_CODES.N)
+ public void onProvideKeyboardShortcuts(
+ List data, Menu menu, int deviceId) {
+
+ ArrayList shortcutInfos = new ArrayList<>();
+ if (mState == State.WORKSPACE) {
+ shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.all_apps_button_label),
+ KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON));
+ }
+ View currentFocus = getCurrentFocus();
+ if (new CustomActionsPopup(this, currentFocus).canShow()) {
+ shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.custom_actions),
+ KeyEvent.KEYCODE_O, KeyEvent.META_CTRL_ON));
+ }
+ if (currentFocus instanceof BubbleTextView &&
+ ((BubbleTextView) currentFocus).hasDeepShortcuts()) {
+ shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.action_deep_shortcut),
+ KeyEvent.KEYCODE_S, KeyEvent.META_CTRL_ON));
+ }
+ if (!shortcutInfos.isEmpty()) {
+ data.add(new KeyboardShortcutGroup(getString(R.string.home_screen), shortcutInfos));
+ }
+
+ super.onProvideKeyboardShortcuts(data, menu, deviceId);
+ }
+
+ @Override
+ public boolean onKeyShortcut(int keyCode, KeyEvent event) {
+ if (event.hasModifiers(KeyEvent.META_CTRL_ON)) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_A:
+ if (mState == State.WORKSPACE) {
+ showAppsView(true, true, false);
+ return true;
+ }
+ break;
+ case KeyEvent.KEYCODE_S: {
+ View focusedView = getCurrentFocus();
+ if (focusedView instanceof BubbleTextView
+ && focusedView.getTag() instanceof ItemInfo
+ && mAccessibilityDelegate.performAction(focusedView,
+ (ItemInfo) focusedView.getTag(),
+ LauncherAccessibilityDelegate.DEEP_SHORTCUTS)) {
+ PopupContainerWithArrow.getOpen(this).requestFocus();
+ return true;
+ }
+ break;
+ }
+ case KeyEvent.KEYCODE_O:
+ if (new CustomActionsPopup(this, getCurrentFocus()).show()) {
+ return true;
+ }
+ break;
+ }
+ }
+ return super.onKeyShortcut(keyCode, event);
+ }
+
public static CustomAppWidget getCustomAppWidget(String name) {
return sCustomAppWidgets.get(name);
}
@@ -4465,14 +4113,6 @@ public class Launcher extends Activity
return sCustomAppWidgets;
}
- public static List getFolderContents(View icon) {
- if (icon instanceof FolderIcon) {
- return ((FolderIcon) icon).getFolder().getItemsInReadingOrder();
- } else {
- return Collections.EMPTY_LIST;
- }
- }
-
public static Launcher getLauncher(Context context) {
if (context instanceof Launcher) {
return (Launcher) context;
@@ -4480,22 +4120,16 @@ public class Launcher extends Activity
return ((Launcher) ((ContextWrapper) context).getBaseContext());
}
- private class RotationPrefChangeHandler implements OnSharedPreferenceChangeListener, Runnable {
+ private class RotationPrefChangeHandler implements OnSharedPreferenceChangeListener {
@Override
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(key)) {
- mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext());
- if (!waitUntilResume(this, true)) {
- run();
- }
+ // Finish this instance of the activity. When the activity is recreated,
+ // it will initialize the rotation preference again.
+ finish();
}
}
-
- @Override
- public void run() {
- setOrientation();
- }
}
}
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index 01e73d4a1a..aa7f5ee5fe 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -23,7 +23,9 @@ import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.util.Property;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.widget.ViewAnimator;
import java.util.HashSet;
import java.util.WeakHashMap;
@@ -92,7 +94,7 @@ public class LauncherAnimUtils {
return anim;
}
- public static ValueAnimator ofFloat(View target, float... values) {
+ public static ValueAnimator ofFloat(float... values) {
ValueAnimator anim = new ValueAnimator();
anim.setFloatValues(values);
cancelOnDestroyActivity(anim);
@@ -127,4 +129,5 @@ public class LauncherAnimUtils {
new FirstFrameAnimatorHelper(anim, view);
return anim;
}
+
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 7861a106fd..180c202fa4 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -16,10 +16,11 @@
package com.android.launcher3;
-import android.content.BroadcastReceiver;
+import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.Looper;
import android.util.Log;
import com.android.launcher3.compat.LauncherAppsCompat;
@@ -27,37 +28,43 @@ import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dynamicui.ExtractionUtils;
-import com.android.launcher3.logging.FileLog;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutCache;
import com.android.launcher3.util.ConfigMonitor;
+import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.TestingUtils;
-import com.android.launcher3.util.Thunk;
-import java.lang.ref.WeakReference;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
public class LauncherAppState {
public static final boolean PROFILE_STARTUP = ProviderConfig.IS_DOGFOOD_BUILD;
- private final AppFilter mAppFilter;
- @Thunk final LauncherModel mModel;
- private final IconCache mIconCache;
- private final WidgetPreviewLoader mWidgetCache;
- private final DeepShortcutManager mDeepShortcutManager;
-
- @Thunk boolean mWallpaperChangedSinceLastCheck;
-
- private static WeakReference sLauncherProvider;
- private static Context sContext;
-
+ // We do not need any synchronization for this variable as its only written on UI thread.
private static LauncherAppState INSTANCE;
- private InvariantDeviceProfile mInvariantDeviceProfile;
+ private final Context mContext;
+ private final LauncherModel mModel;
+ private final IconCache mIconCache;
+ private final WidgetPreviewLoader mWidgetCache;
+ private final InvariantDeviceProfile mInvariantDeviceProfile;
- public static LauncherAppState getInstance() {
+
+ public static LauncherAppState getInstance(final Context context) {
if (INSTANCE == null) {
- INSTANCE = new LauncherAppState();
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ INSTANCE = new LauncherAppState(context.getApplicationContext());
+ } else {
+ try {
+ return new MainThreadExecutor().submit(new Callable() {
+ @Override
+ public LauncherAppState call() throws Exception {
+ return LauncherAppState.getInstance(context);
+ }
+ }).get();
+ } catch (InterruptedException|ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
return INSTANCE;
}
@@ -67,43 +74,30 @@ public class LauncherAppState {
}
public Context getContext() {
- return sContext;
+ return mContext;
}
- static void setLauncherProvider(LauncherProvider provider) {
- if (sLauncherProvider != null) {
- Log.w(Launcher.TAG, "setLauncherProvider called twice! old=" +
- sLauncherProvider.get() + " new=" + provider);
+ private LauncherAppState(Context context) {
+ if (getLocalProvider(context) == null) {
+ throw new RuntimeException(
+ "Initializing LauncherAppState in the absence of LauncherProvider");
}
- sLauncherProvider = new WeakReference<>(provider);
-
- // The content provider exists for the entire duration of the launcher main process and
- // is the first component to get created. Initializing application context here ensures
- // that LauncherAppState always exists in the main process.
- sContext = provider.getContext().getApplicationContext();
- FileLog.setDir(sContext.getFilesDir());
- }
-
- private LauncherAppState() {
- if (sContext == null) {
- throw new IllegalStateException("LauncherAppState inited before app context set");
- }
-
- Log.v(Launcher.TAG, "LauncherAppState inited");
+ Log.v(Launcher.TAG, "LauncherAppState initiated");
+ Preconditions.assertUIThread();
+ mContext = context;
if (TestingUtils.MEMORY_DUMP_ENABLED) {
- TestingUtils.startTrackingMemory(sContext);
+ TestingUtils.startTrackingMemory(mContext);
}
- mInvariantDeviceProfile = new InvariantDeviceProfile(sContext);
- mIconCache = new IconCache(sContext, mInvariantDeviceProfile);
- mWidgetCache = new WidgetPreviewLoader(sContext, mIconCache);
- mDeepShortcutManager = new DeepShortcutManager(sContext, new ShortcutCache());
+ mInvariantDeviceProfile = new InvariantDeviceProfile(mContext);
+ mIconCache = new IconCache(mContext, mInvariantDeviceProfile);
+ mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
- mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class));
- mModel = new LauncherModel(this, mIconCache, mAppFilter, mDeepShortcutManager);
+ mModel = new LauncherModel(this, mIconCache,
+ Utilities.getOverrideObject(AppFilter.class, mContext, R.string.app_filter_class));
- LauncherAppsCompat.getInstance(sContext).addOnAppsChangedCallback(mModel);
+ LauncherAppsCompat.getInstance(mContext).addOnAppsChangedCallback(mModel);
// Register intent receivers
IntentFilter filter = new IntentFilter();
@@ -115,48 +109,30 @@ public class LauncherAppState {
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
// For extracting colors from the wallpaper
- if (Utilities.isNycOrAbove()) {
+ if (Utilities.ATLEAST_NOUGAT) {
// TODO: add a broadcast entry to the manifest for pre-N.
filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
}
- sContext.registerReceiver(mModel, filter);
- UserManagerCompat.getInstance(sContext).enableAndResetCache();
- if (!Utilities.ATLEAST_KITKAT) {
- sContext.registerReceiver(new BroadcastReceiver() {
+ mContext.registerReceiver(mModel, filter);
+ UserManagerCompat.getInstance(mContext).enableAndResetCache();
+ new ConfigMonitor(mContext).register();
- @Override
- public void onReceive(Context context, Intent intent) {
- mWallpaperChangedSinceLastCheck = true;
- }
- }, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
- }
- new ConfigMonitor(sContext).register();
-
- ExtractionUtils.startColorExtractionServiceIfNecessary(sContext);
+ ExtractionUtils.startColorExtractionServiceIfNecessary(mContext);
}
/**
* Call from Application.onTerminate(), which is not guaranteed to ever be called.
*/
public void onTerminate() {
- sContext.unregisterReceiver(mModel);
- final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(sContext);
+ mContext.unregisterReceiver(mModel);
+ final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
launcherApps.removeOnAppsChangedCallback(mModel);
- PackageInstallerCompat.getInstance(sContext).onStop();
- }
-
- /**
- * Reloads the workspace items from the DB and re-binds the workspace. This should generally
- * not be called as DB updates are automatically followed by UI update
- */
- public void reloadWorkspace() {
- mModel.resetLoadedState(false, true);
- mModel.startLoaderFromBackground();
+ PackageInstallerCompat.getInstance(mContext).onStop();
}
LauncherModel setLauncher(Launcher launcher) {
- sLauncherProvider.get().setLauncherProviderChangeListener(launcher);
+ getLocalProvider(mContext).setLauncherProviderChangeListener(launcher);
mModel.initialize(launcher);
return mModel;
}
@@ -173,17 +149,21 @@ public class LauncherAppState {
return mWidgetCache;
}
- public DeepShortcutManager getShortcutManager() {
- return mDeepShortcutManager;
- }
-
- public boolean hasWallpaperChangedSinceLastCheck() {
- boolean result = mWallpaperChangedSinceLastCheck;
- mWallpaperChangedSinceLastCheck = false;
- return result;
- }
-
public InvariantDeviceProfile getInvariantDeviceProfile() {
return mInvariantDeviceProfile;
}
+
+ /**
+ * Shorthand for {@link #getInvariantDeviceProfile()}
+ */
+ public static InvariantDeviceProfile getIDP(Context context) {
+ return LauncherAppState.getInstance(context).getInvariantDeviceProfile();
+ }
+
+ private static LauncherProvider getLocalProvider(Context context) {
+ try (ContentProviderClient cl = context.getContentResolver()
+ .acquireContentProviderClient(LauncherProvider.AUTHORITY)) {
+ return (LauncherProvider) cl.getLocalContentProvider();
+ }
+ }
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index d3e5350026..6e8c59b66c 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -20,10 +20,8 @@ import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
-import android.os.DeadObjectException;
-import android.os.TransactionTooLargeException;
+import android.util.SparseArray;
import android.view.LayoutInflater;
-import android.view.View;
import java.util.ArrayList;
@@ -36,6 +34,7 @@ import java.util.ArrayList;
public class LauncherAppWidgetHost extends AppWidgetHost {
private final ArrayList mProviderChangeListeners = new ArrayList();
+ private final SparseArray mViews = new SparseArray<>();
private Launcher mLauncher;
@@ -45,9 +44,11 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
}
@Override
- protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
+ protected LauncherAppWidgetHostView onCreateView(Context context, int appWidgetId,
AppWidgetProviderInfo appWidget) {
- return new LauncherAppWidgetHostView(context);
+ LauncherAppWidgetHostView view = new LauncherAppWidgetHostView(context);
+ mViews.put(appWidgetId, view);
+ return view;
}
@Override
@@ -55,15 +56,13 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
try {
super.startListening();
} catch (Exception e) {
- if (e.getCause() instanceof TransactionTooLargeException ||
- e.getCause() instanceof DeadObjectException) {
- // We're willing to let this slide. The exception is being caused by the list of
- // RemoteViews which is being passed back. The startListening relationship will
- // have been established by this point, and we will end up populating the
- // widgets upon bind anyway. See issue 14255011 for more context.
- } else {
+ if (!Utilities.isBinderSizeError(e)) {
throw new RuntimeException(e);
}
+ // We're willing to let this slide. The exception is being caused by the list of
+ // RemoteViews which is being passed back. The startListening relationship will
+ // have been established by this point, and we will end up populating the
+ // widgets upon bind anyway. See issue 14255011 for more context.
}
}
@@ -98,7 +97,24 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
lahv.updateLastInflationOrientation();
return lahv;
} else {
- return super.createView(context, appWidgetId, appWidget);
+ try {
+ return super.createView(context, appWidgetId, appWidget);
+ } catch (Exception e) {
+ if (!Utilities.isBinderSizeError(e)) {
+ throw new RuntimeException(e);
+ }
+
+ // If the exception was thrown while fetching the remote views, let the view stay.
+ // This will ensure that if the widget posts a valid update later, the view
+ // will update.
+ LauncherAppWidgetHostView view = mViews.get(appWidgetId);
+ if (view == null) {
+ view = onCreateView(mLauncher, appWidgetId, appWidget);
+ }
+ view.setAppWidget(appWidgetId, appWidget);
+ view.switchToErrorView();
+ return view;
+ }
}
}
@@ -112,6 +128,18 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
super.onProviderChanged(appWidgetId, info);
// The super method updates the dimensions of the providerInfo. Update the
// launcher spans accordingly.
- info.initSpans();
+ info.initSpans(mLauncher);
+ }
+
+ @Override
+ public void deleteAppWidgetId(int appWidgetId) {
+ super.deleteAppWidgetId(appWidgetId);
+ mViews.remove(appWidgetId);
+ }
+
+ @Override
+ protected void clearViews() {
+ super.clearViews();
+ mViews.clear();
}
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index ed1079ff33..61b8a7aa8a 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -19,7 +19,12 @@ package com.android.launcher3;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
+import android.graphics.PointF;
import android.graphics.Rect;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.SparseBooleanArray;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -28,22 +33,38 @@ import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.AdapterView;
+import android.widget.Advanceable;
import android.widget.RemoteViews;
+import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener;
+import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
/**
* {@inheritDoc}
*/
-public class LauncherAppWidgetHostView extends AppWidgetHostView implements TouchCompleteListener {
+public class LauncherAppWidgetHostView extends AppWidgetHostView
+ implements TouchCompleteListener, View.OnLongClickListener {
- LayoutInflater mInflater;
+ private static final String TAG = "LauncherWidgetHostView";
+
+ // Related to the auto-advancing of widgets
+ private static final long ADVANCE_INTERVAL = 20000;
+ private static final long ADVANCE_STAGGER = 250;
+
+ // Maintains a list of widget ids which are supposed to be auto advanced.
+ private static final SparseBooleanArray sAutoAdvanceWidgetIds = new SparseBooleanArray();
+
+ protected final LayoutInflater mInflater;
+
+ private final CheckLongPressHelper mLongPressHelper;
+ private final StylusEventHelper mStylusEventHelper;
+ private final Context mContext;
- private CheckLongPressHelper mLongPressHelper;
- private StylusEventHelper mStylusEventHelper;
- private Context mContext;
@ViewDebug.ExportedProperty(category = "launcher")
private int mPreviousOrientation;
@@ -52,21 +73,54 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mChildrenFocused;
- protected int mErrorViewId = R.layout.appwidget_error;
+ private boolean mIsScrollable;
+ private boolean mIsAttachedToWindow;
+ private boolean mIsAutoAdvanceRegistered;
+ private Runnable mAutoAdvanceRunnable;
+
+ /**
+ * The scaleX and scaleY value such that the widget fits within its cellspans, scaleX = scaleY.
+ */
+ private float mScaleToFit = 1f;
+
+ /**
+ * The translation values to center the widget within its cellspans.
+ */
+ private final PointF mTranslationForCentering = new PointF(0, 0);
public LauncherAppWidgetHostView(Context context) {
super(context);
mContext = context;
- mLongPressHelper = new CheckLongPressHelper(this);
+ mLongPressHelper = new CheckLongPressHelper(this, this);
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
- mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mInflater = LayoutInflater.from(context);
setAccessibilityDelegate(Launcher.getLauncher(context).getAccessibilityDelegate());
setBackgroundResource(R.drawable.widget_internal_focus_bg);
+
+ if (Utilities.isAtLeastO()) {
+ try {
+ Method asyncMethod = AppWidgetHostView.class
+ .getMethod("setAsyncExecutor", Executor.class);
+ asyncMethod.invoke(this, Utilities.THREAD_POOL_EXECUTOR);
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to set async executor", e);
+ }
+ }
+ }
+
+ @Override
+ public boolean onLongClick(View view) {
+ if (mIsScrollable) {
+ DragLayer dragLayer = Launcher.getLauncher(getContext()).getDragLayer();
+ dragLayer.requestDisallowInterceptTouchEvent(false);
+ }
+ view.performLongClick();
+ return true;
}
@Override
protected View getErrorView() {
- return mInflater.inflate(mErrorViewId, this, false);
+ return mInflater.inflate(R.layout.appwidget_error, this, false);
}
public void updateLastInflationOrientation() {
@@ -78,6 +132,25 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc
// Store the orientation in which the widget was inflated
updateLastInflationOrientation();
super.updateAppWidget(remoteViews);
+
+ // The provider info or the views might have changed.
+ checkIfAutoAdvance();
+ }
+
+ private boolean checkScrollableRecursively(ViewGroup viewGroup) {
+ if (viewGroup instanceof AdapterView) {
+ return true;
+ } else {
+ for (int i=0; i < viewGroup.getChildCount(); i++) {
+ View child = viewGroup.getChildAt(i);
+ if (child instanceof ViewGroup) {
+ if (checkScrollableRecursively((ViewGroup) child)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
}
public boolean isReinflateRequired() {
@@ -108,12 +181,18 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc
mLongPressHelper.cancelLongPress();
return true;
}
+
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
+ DragLayer dragLayer = Launcher.getLauncher(getContext()).getDragLayer();
+
+ if (mIsScrollable) {
+ dragLayer.requestDisallowInterceptTouchEvent(true);
+ }
if (!mStylusEventHelper.inStylusButtonPressed()) {
mLongPressHelper.postCheckForLongPress();
}
- Launcher.getLauncher(getContext()).getDragLayer().setTouchCompleteListener(this);
+ dragLayer.setTouchCompleteListener(this);
break;
}
@@ -153,6 +232,19 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+
+ mIsAttachedToWindow = true;
+ checkIfAutoAdvance();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ // We can't directly use isAttachedToWindow() here, as this is called before the internal
+ // state is updated. So isAttachedToWindow() will return true until next frame.
+ mIsAttachedToWindow = false;
+ checkIfAutoAdvance();
}
@Override
@@ -171,10 +263,6 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc
return info;
}
- public LauncherAppWidgetProviderInfo getLauncherAppWidgetProviderInfo() {
- return (LauncherAppWidgetProviderInfo) getAppWidgetInfo();
- }
-
@Override
public void onTouchComplete() {
if (!mLongPressHelper.hasPerformedLongPress()) {
@@ -276,6 +364,11 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc
setSelected(childIsFocused);
}
+ public void switchToErrorView() {
+ // Update the widget with 0 Layout id, to reset the view to error view.
+ updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
try {
@@ -284,11 +377,12 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc
post(new Runnable() {
@Override
public void run() {
- // Update the widget with 0 Layout id, to reset the view to error view.
- updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));
+ switchToErrorView();
}
});
}
+
+ mIsScrollable = checkScrollableRecursively(this);
}
@Override
@@ -296,4 +390,99 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(getClass().getName());
}
+
+ @Override
+ protected void onWindowVisibilityChanged(int visibility) {
+ super.onWindowVisibilityChanged(visibility);
+ maybeRegisterAutoAdvance();
+ }
+
+ private void checkIfAutoAdvance() {
+ boolean isAutoAdvance = false;
+ Advanceable target = getAdvanceable();
+ if (target != null) {
+ isAutoAdvance = true;
+ target.fyiWillBeAdvancedByHostKThx();
+ }
+
+ boolean wasAutoAdvance = sAutoAdvanceWidgetIds.indexOfKey(getAppWidgetId()) >= 0;
+ if (isAutoAdvance != wasAutoAdvance) {
+ if (isAutoAdvance) {
+ sAutoAdvanceWidgetIds.put(getAppWidgetId(), true);
+ } else {
+ sAutoAdvanceWidgetIds.delete(getAppWidgetId());
+ }
+ maybeRegisterAutoAdvance();
+ }
+ }
+
+ private Advanceable getAdvanceable() {
+ AppWidgetProviderInfo info = getAppWidgetInfo();
+ if (info == null || info.autoAdvanceViewId == NO_ID || !mIsAttachedToWindow) {
+ return null;
+ }
+ View v = findViewById(info.autoAdvanceViewId);
+ return (v instanceof Advanceable) ? (Advanceable) v : null;
+ }
+
+ private void maybeRegisterAutoAdvance() {
+ Handler handler = getHandler();
+ boolean shouldRegisterAutoAdvance = getWindowVisibility() == VISIBLE && handler != null
+ && (sAutoAdvanceWidgetIds.indexOfKey(getAppWidgetId()) >= 0);
+ if (shouldRegisterAutoAdvance != mIsAutoAdvanceRegistered) {
+ mIsAutoAdvanceRegistered = shouldRegisterAutoAdvance;
+ if (mAutoAdvanceRunnable == null) {
+ mAutoAdvanceRunnable = new Runnable() {
+ @Override
+ public void run() {
+ runAutoAdvance();
+ }
+ };
+ }
+
+ handler.removeCallbacks(mAutoAdvanceRunnable);
+ scheduleNextAdvance();
+ }
+ }
+
+ private void scheduleNextAdvance() {
+ if (!mIsAutoAdvanceRegistered) {
+ return;
+ }
+ long now = SystemClock.uptimeMillis();
+ long advanceTime = now + (ADVANCE_INTERVAL - (now % ADVANCE_INTERVAL)) +
+ ADVANCE_STAGGER * sAutoAdvanceWidgetIds.indexOfKey(getAppWidgetId());
+ Handler handler = getHandler();
+ if (handler != null) {
+ handler.postAtTime(mAutoAdvanceRunnable, advanceTime);
+ }
+ }
+
+ private void runAutoAdvance() {
+ Advanceable target = getAdvanceable();
+ if (target != null) {
+ target.advance();
+ }
+ scheduleNextAdvance();
+ }
+
+ public void setScaleToFit(float scale) {
+ mScaleToFit = scale;
+ setScaleX(scale);
+ setScaleY(scale);
+ }
+
+ public float getScaleToFit() {
+ return mScaleToFit;
+ }
+
+ public void setTranslationForCentering(float x, float y) {
+ mTranslationForCentering.set(x, y);
+ setTranslationX(x);
+ setTranslationY(y);
+ }
+
+ public PointF getTranslationForCentering() {
+ return mTranslationForCentering;
+ }
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 66d895726b..1e0f28546b 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -18,11 +18,10 @@ package com.android.launcher3;
import android.appwidget.AppWidgetHostView;
import android.content.ComponentName;
-import android.content.ContentValues;
-import android.content.Context;
import android.content.Intent;
+import android.os.Process;
-import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.util.ContentWriter;
/**
* Represents a widget (either instantiated or about to be) in the Launcher.
@@ -66,7 +65,7 @@ public class LauncherAppWidgetInfo extends ItemInfo {
/**
* Indicates that the widget hasn't been instantiated yet.
*/
- static final int NO_ID = -1;
+ public static final int NO_ID = -1;
/**
* Indicates that this is a locally defined widget and hence has no system allocated id.
@@ -77,19 +76,19 @@ public class LauncherAppWidgetInfo extends ItemInfo {
* Identifier for this widget when talking with
* {@link android.appwidget.AppWidgetManager} for updates.
*/
- int appWidgetId = NO_ID;
+ public int appWidgetId = NO_ID;
public ComponentName providerName;
/**
* Indicates the restore status of the widget.
*/
- int restoreStatus;
+ public int restoreStatus;
/**
* Indicates the installation progress of the widget provider
*/
- int installProgress = -1;
+ public int installProgress = -1;
/**
* Optional extras sent during widget bind. See {@link #FLAG_DIRECT_CONFIG}.
@@ -98,7 +97,7 @@ public class LauncherAppWidgetInfo extends ItemInfo {
private boolean mHasNotifiedInitialWidgetSizeChanged;
- LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName) {
+ public LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName) {
if (appWidgetId == CUSTOM_WIDGET_ID) {
itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
} else {
@@ -113,22 +112,26 @@ public class LauncherAppWidgetInfo extends ItemInfo {
spanX = -1;
spanY = -1;
// We only support app widgets on current user.
- user = UserHandleCompat.myUserHandle();
+ user = Process.myUserHandle();
restoreStatus = RESTORE_COMPLETED;
}
+ /** Used for testing **/
+ public LauncherAppWidgetInfo() {
+ itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+ }
+
public boolean isCustomWidget() {
return appWidgetId == CUSTOM_WIDGET_ID;
}
@Override
- void onAddToDatabase(Context context, ContentValues values) {
- super.onAddToDatabase(context, values);
- values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
- values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, providerName.flattenToString());
- values.put(LauncherSettings.Favorites.RESTORED, restoreStatus);
- values.put(LauncherSettings.Favorites.INTENT,
- bindOptions == null ? null : bindOptions.toUri(0));
+ public void onAddToDatabase(ContentWriter writer) {
+ super.onAddToDatabase(writer);
+ writer.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId)
+ .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, providerName.flattenToString())
+ .put(LauncherSettings.Favorites.RESTORED, restoreStatus)
+ .put(LauncherSettings.Favorites.INTENT, bindOptions);
}
/**
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index 1a4153f75d..6cb703b43a 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -1,6 +1,5 @@
package com.android.launcher3;
-import android.annotation.TargetApi;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
@@ -9,8 +8,9 @@ import android.content.pm.PackageManager;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.os.Parcel;
+import android.os.Process;
+import android.os.UserHandle;
/**
* This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords
@@ -29,22 +29,27 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context,
AppWidgetProviderInfo info) {
+ final LauncherAppWidgetProviderInfo launcherInfo;
+ if (info instanceof LauncherAppWidgetProviderInfo) {
+ launcherInfo = (LauncherAppWidgetProviderInfo) info;
+ } else {
- // In lieu of a public super copy constructor, we first write the AppWidgetProviderInfo
- // into a parcel, and then construct a new LauncherAppWidgetProvider info from the
- // associated super parcel constructor. This allows us to copy non-public members without
- // using reflection.
- Parcel p = Parcel.obtain();
- info.writeToParcel(p, 0);
- p.setDataPosition(0);
- LauncherAppWidgetProviderInfo lawpi = new LauncherAppWidgetProviderInfo(p);
- p.recycle();
- return lawpi;
+ // In lieu of a public super copy constructor, we first write the AppWidgetProviderInfo
+ // into a parcel, and then construct a new LauncherAppWidgetProvider info from the
+ // associated super parcel constructor. This allows us to copy non-public members without
+ // using reflection.
+ Parcel p = Parcel.obtain();
+ info.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ launcherInfo = new LauncherAppWidgetProviderInfo(p);
+ p.recycle();
+ }
+ launcherInfo.initSpans(context);
+ return launcherInfo;
}
- public LauncherAppWidgetProviderInfo(Parcel in) {
+ private LauncherAppWidgetProviderInfo(Parcel in) {
super(in);
- initSpans();
}
public LauncherAppWidgetProviderInfo(Context context, CustomAppWidget widget) {
@@ -56,12 +61,11 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
previewImage = widget.getPreviewImage();
initialLayout = widget.getWidgetLayout();
resizeMode = widget.getResizeMode();
- initSpans();
+ initSpans(context);
}
- public void initSpans() {
- LauncherAppState app = LauncherAppState.getInstance();
- InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
+ public void initSpans(Context context) {
+ InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
Point paddingLand = idp.landscapeProfile.getTotalWorkspacePadding();
Point paddingPort = idp.portraitProfile.getTotalWorkspacePadding();
@@ -80,7 +84,7 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
// We want to account for the extra amount of padding that we are adding to the widget
// to ensure that it gets the full amount of space that it has requested.
Rect widgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(
- app.getContext(), provider, null);
+ context, provider, null);
spanX = Math.max(1, (int) Math.ceil(
(minWidth + widgetPadding.left + widgetPadding.right) / smallestCellWidth));
spanY = Math.max(1, (int) Math.ceil(
@@ -92,7 +96,6 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
(minResizeHeight + widgetPadding.top + widgetPadding.bottom) / smallestCellHeight));
}
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
public String getLabel(PackageManager packageManager) {
if (isCustomWidget) {
return Utilities.trim(label);
@@ -100,13 +103,11 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
return super.loadLabel(packageManager);
}
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
public Drawable getIcon(Context context, IconCache cache) {
if (isCustomWidget) {
return cache.getFullResIcon(provider.getPackageName(), icon);
}
- return super.loadIcon(context,
- LauncherAppState.getInstance().getInvariantDeviceProfile().fillResIconDpi);
+ return super.loadIcon(context, LauncherAppState.getIDP(context).fillResIconDpi);
}
public String toString(PackageManager pm) {
@@ -122,4 +123,8 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
(resizeMode & RESIZE_HORIZONTAL) != 0 ? minSpanX : -1,
(resizeMode & RESIZE_VERTICAL) != 0 ? minSpanY : -1);
}
+
+ public UserHandle getUser() {
+ return isCustomWidget ? Process.myUserHandle() : getProfile();
+ }
}
diff --git a/src/com/android/launcher3/LauncherBackupAgent.java b/src/com/android/launcher3/LauncherBackupAgent.java
index b3e73f7689..140794b074 100644
--- a/src/com/android/launcher3/LauncherBackupAgent.java
+++ b/src/com/android/launcher3/LauncherBackupAgent.java
@@ -5,10 +5,18 @@ import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.os.ParcelFileDescriptor;
+import com.android.launcher3.logging.FileLog;
import com.android.launcher3.provider.RestoreDbTask;
public class LauncherBackupAgent extends BackupAgent {
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ // Set the log dir as LauncherAppState is not initialized during restore.
+ FileLog.setDir(getFilesDir());
+ }
+
@Override
public void onRestore(
BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) {
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 6394b9052b..2bac11f975 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -92,7 +92,6 @@ public interface LauncherCallbacks {
/*
* Extensions points for adding / replacing some other aspects of the Launcher experience.
*/
- public UserEventDispatcher getUserEventDispatcher();
public boolean shouldMoveToDefaultScreenOnHomeIntent();
public boolean hasSettings();
public AllAppsSearchBarController getAllAppsSearchBarController();
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 0199d0ca78..2586764239 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -24,41 +24,47 @@ 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.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.database.Cursor;
-import android.graphics.Bitmap;
+import android.content.pm.LauncherActivityInfo;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
-import android.os.Parcelable;
import android.os.Process;
import android.os.SystemClock;
import android.os.Trace;
-import android.provider.BaseColumns;
+import android.os.UserHandle;
+import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.MutableInt;
-import android.util.Pair;
import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.LauncherActivityInfoCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
-import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dynamicui.ExtractionUtils;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.model.AddWorkspaceItemsTask;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.CacheDataUpdatedTask;
+import com.android.launcher3.model.ExtendedModelTask;
import com.android.launcher3.model.GridSizeMigrationTask;
+import com.android.launcher3.model.LoaderCursor;
+import com.android.launcher3.model.ModelWriter;
+import com.android.launcher3.model.PackageInstallStateChangedTask;
+import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.model.PackageUpdatedTask;
+import com.android.launcher3.model.SdCardAvailableReceiver;
+import com.android.launcher3.model.ShortcutsChangedTask;
+import com.android.launcher3.model.UserLockStateChangedTask;
+import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.provider.ImportDataTask;
import com.android.launcher3.provider.LauncherDbUtils;
@@ -66,23 +72,19 @@ import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.CursorIconInfo;
-import com.android.launcher3.util.FlagOp;
-import com.android.launcher3.util.GridOccupancy;
-import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.ManagedProfileHeuristic;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.StringFilter;
+import com.android.launcher3.util.Provider;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.ViewOnDrawExecutor;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.lang.ref.WeakReference;
-import java.net.URISyntaxException;
-import java.security.InvalidParameterException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -90,8 +92,8 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
+import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
/**
@@ -123,12 +125,16 @@ public class LauncherModel extends BroadcastReceiver
}
@Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper());
- // We start off with everything not loaded. After that, we assume that
+ // Indicates whether the current model data is valid or not.
+ // We start off with everything not loaded. After that, we assume that
// our monitoring of the package manager provides all updates and we never
- // need to do a requery. These are only ever touched from the loader thread.
- private boolean mWorkspaceLoaded;
- private boolean mAllAppsLoaded;
- private boolean mDeepShortcutsLoaded;
+ // need to do a requery. This is only ever touched from the loader thread.
+ private boolean mModelLoaded;
+ public boolean isModelLoaded() {
+ synchronized (mLock) {
+ return mModelLoaded && mLoaderTask == null;
+ }
+ }
/**
* Set of runnables to be called on the background thread after the workspace binding
@@ -143,60 +149,30 @@ public class LauncherModel extends BroadcastReceiver
// Entire list of widgets.
private final WidgetsModel mBgWidgetsModel;
- // Maps all launcher activities to the id's of their shortcuts (if they have any).
- private final MultiHashMap mBgDeepShortcutMap = new MultiHashMap<>();
-
private boolean mHasShortcutHostPermission;
// Runnable to check if the shortcuts permission has changed.
private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
@Override
public void run() {
- if (mDeepShortcutsLoaded) {
- boolean hasShortcutHostPermission = mDeepShortcutManager.hasHostPermission();
+ if (mModelLoaded) {
+ boolean hasShortcutHostPermission =
+ DeepShortcutManager.getInstance(mApp.getContext()).hasHostPermission();
if (hasShortcutHostPermission != mHasShortcutHostPermission) {
- mApp.reloadWorkspace();
+ forceReload();
}
}
}
};
- // The lock that must be acquired before referencing any static bg data structures. Unlike
- // other locks, this one can generally be held long-term because we never expect any of these
- // static data structures to be referenced outside of the worker thread except on the first
- // load after configuration change.
- static final Object sBgLock = new Object();
-
- // sBgItemsIdMap maps *all* the ItemInfos (shortcuts, folders, and widgets) created by
- // LauncherModel to their ids
- static final LongArrayMap sBgItemsIdMap = new LongArrayMap<>();
-
- // sBgWorkspaceItems is passed to bindItems, which expects a list of all folders and shortcuts
- // created by LauncherModel that are directly on the home screen (however, no widgets or
- // shortcuts within folders).
- static final ArrayList sBgWorkspaceItems = new ArrayList();
-
- // sBgAppWidgets is all LauncherAppWidgetInfo created by LauncherModel. Passed to bindAppWidget()
- static final ArrayList sBgAppWidgets =
- new ArrayList();
-
- // sBgFolders is all FolderInfos created by LauncherModel. Passed to bindFolders()
- static final LongArrayMap sBgFolders = new LongArrayMap<>();
-
- // sBgWorkspaceScreens is the ordered set of workspace screens.
- static final ArrayList sBgWorkspaceScreens = new ArrayList();
-
- // sBgPinnedShortcutCounts is the ComponentKey representing a pinned shortcut to the number of
- // times it is pinned.
- static final Map sBgPinnedShortcutCounts = new HashMap<>();
-
- // sPendingPackages is a set of packages which could be on sdcard and are not available yet
- static final HashMap> sPendingPackages =
- new HashMap>();
+ /**
+ * All the static data should be accessed on the background thread, A lock should be acquired
+ * on this object when accessing any data from this model.
+ */
+ static final BgDataModel sBgDataModel = new BgDataModel();
// only access in worker thread >
- private IconCache mIconCache;
- private DeepShortcutManager mDeepShortcutManager;
+ private final IconCache mIconCache;
private final LauncherAppsCompat mLauncherApps;
private final UserManagerCompat mUserManager;
@@ -219,32 +195,26 @@ public class LauncherModel extends BroadcastReceiver
ArrayList addedApps);
public void bindAppsUpdated(ArrayList apps);
public void bindShortcutsChanged(ArrayList updated,
- ArrayList removed, UserHandleCompat user);
+ ArrayList removed, UserHandle user);
public void bindWidgetsRestored(ArrayList widgets);
public void bindRestoreItemsChange(HashSet updates);
public void bindWorkspaceComponentsRemoved(
HashSet packageNames, HashSet components,
- UserHandleCompat user);
+ UserHandle user);
public void bindAppInfosRemoved(ArrayList appInfos);
public void notifyWidgetProvidersChanged();
- public void bindWidgetsModel(WidgetsModel model);
+ public void bindAllWidgets(MultiHashMap widgets);
public void onPageBoundSynchronously(int page);
public void executeOnNextDraw(ViewOnDrawExecutor executor);
public void bindDeepShortcutMap(MultiHashMap deepShortcutMap);
}
- public interface ItemInfoFilter {
- public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn);
- }
-
- LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter,
- DeepShortcutManager deepShortcutManager) {
+ LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
Context context = app.getContext();
mApp = app;
mBgAllAppsList = new AllAppsList(iconCache, appFilter);
- mBgWidgetsModel = new WidgetsModel(context, iconCache, appFilter);
+ mBgWidgetsModel = new WidgetsModel(iconCache, appFilter);
mIconCache = iconCache;
- mDeepShortcutManager = deepShortcutManager;
mLauncherApps = LauncherAppsCompat.getInstance(context);
mUserManager = UserManagerCompat.getInstance(context);
@@ -272,314 +242,42 @@ public class LauncherModel extends BroadcastReceiver
}
}
- public void setPackageState(final PackageInstallInfo installInfo) {
- Runnable updateRunnable = new Runnable() {
-
- @Override
- public void run() {
- synchronized (sBgLock) {
- final HashSet updates = new HashSet<>();
-
- if (installInfo.state == PackageInstallerCompat.STATUS_INSTALLED) {
- // Ignore install success events as they are handled by Package add events.
- return;
- }
-
- for (ItemInfo info : sBgItemsIdMap) {
- if (info instanceof ShortcutInfo) {
- ShortcutInfo si = (ShortcutInfo) info;
- ComponentName cn = si.getTargetComponent();
- if (si.isPromise() && (cn != null)
- && installInfo.packageName.equals(cn.getPackageName())) {
- si.setInstallProgress(installInfo.progress);
-
- if (installInfo.state == PackageInstallerCompat.STATUS_FAILED) {
- // Mark this info as broken.
- si.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
- }
- updates.add(si);
- }
- }
- }
-
- for (LauncherAppWidgetInfo widget : sBgAppWidgets) {
- if (widget.providerName.getPackageName().equals(installInfo.packageName)) {
- widget.installProgress = installInfo.progress;
- updates.add(widget);
- }
- }
-
- if (!updates.isEmpty()) {
- // Push changes to the callback.
- Runnable r = new Runnable() {
- public void run() {
- Callbacks callbacks = getCallback();
- if (callbacks != null) {
- callbacks.bindRestoreItemsChange(updates);
- }
- }
- };
- mHandler.post(r);
- }
- }
- }
- };
- runOnWorkerThread(updateRunnable);
+ public void setPackageState(PackageInstallInfo installInfo) {
+ enqueueModelUpdateTask(new PackageInstallStateChangedTask(installInfo));
}
/**
* Updates the icons and label of all pending icons for the provided package name.
*/
public void updateSessionDisplayInfo(final String packageName) {
- Runnable updateRunnable = new Runnable() {
-
- @Override
- public void run() {
- synchronized (sBgLock) {
- ArrayList updates = new ArrayList<>();
- UserHandleCompat user = UserHandleCompat.myUserHandle();
-
- for (ItemInfo info : sBgItemsIdMap) {
- if (info instanceof ShortcutInfo) {
- ShortcutInfo si = (ShortcutInfo) info;
- ComponentName cn = si.getTargetComponent();
- if (si.isPromise() && (cn != null)
- && packageName.equals(cn.getPackageName())) {
- if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
- // For auto install apps update the icon as well as label.
- mIconCache.getTitleAndIcon(si,
- si.promisedIntent, user,
- si.shouldUseLowResIcon());
- } else {
- // Only update the icon for restored apps.
- si.updateIcon(mIconCache);
- }
- updates.add(si);
- }
- }
- }
-
- bindUpdatedShortcuts(updates, user);
- }
- }
- };
- runOnWorkerThread(updateRunnable);
- }
-
- public void addAppsToAllApps(final Context ctx, final ArrayList allAppsApps) {
- final Callbacks callbacks = getCallback();
-
- if (allAppsApps == null) {
- throw new RuntimeException("allAppsApps must not be null");
- }
- if (allAppsApps.isEmpty()) {
- return;
- }
-
- // Process the newly added applications and add them to the database first
- Runnable r = new Runnable() {
- public void run() {
- runOnMainThread(new Runnable() {
- public void run() {
- Callbacks cb = getCallback();
- if (callbacks == cb && cb != null) {
- callbacks.bindAppsAdded(null, null, null, allAppsApps);
- }
- }
- });
- }
- };
- runOnWorkerThread(r);
- }
-
- private static boolean findNextAvailableIconSpaceInScreen(ArrayList occupiedPos,
- int[] xy, int spanX, int spanY) {
- LauncherAppState app = LauncherAppState.getInstance();
- InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
-
- GridOccupancy occupied = new GridOccupancy(profile.numColumns, profile.numRows);
- if (occupiedPos != null) {
- for (ItemInfo r : occupiedPos) {
- occupied.markCells(r, true);
- }
- }
- return occupied.findVacantCell(xy, spanX, spanY);
- }
-
- /**
- * Find a position on the screen for the given size or adds a new screen.
- * @return screenId and the coordinates for the item.
- */
- @Thunk Pair findSpaceForItem(
- Context context,
- ArrayList workspaceScreens,
- ArrayList addedWorkspaceScreensFinal,
- int spanX, int spanY) {
- LongSparseArray> screenItems = new LongSparseArray<>();
-
- // Use sBgItemsIdMap as all the items are already loaded.
- assertWorkspaceLoaded();
- synchronized (sBgLock) {
- for (ItemInfo info : sBgItemsIdMap) {
- if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- ArrayList items = screenItems.get(info.screenId);
- if (items == null) {
- items = new ArrayList<>();
- screenItems.put(info.screenId, items);
- }
- items.add(info);
- }
- }
- }
-
- // Find appropriate space for the item.
- long screenId = 0;
- int[] cordinates = new int[2];
- boolean found = false;
-
- int screenCount = workspaceScreens.size();
- // First check the preferred screen.
- int preferredScreenIndex = workspaceScreens.isEmpty() ? 0 : 1;
- if (preferredScreenIndex < screenCount) {
- screenId = workspaceScreens.get(preferredScreenIndex);
- found = findNextAvailableIconSpaceInScreen(
- screenItems.get(screenId), cordinates, spanX, spanY);
- }
-
- if (!found) {
- // Search on any of the screens starting from the first screen.
- for (int screen = 1; screen < screenCount; screen++) {
- screenId = workspaceScreens.get(screen);
- if (findNextAvailableIconSpaceInScreen(
- screenItems.get(screenId), cordinates, spanX, spanY)) {
- // We found a space for it
- found = true;
- break;
- }
- }
- }
-
- if (!found) {
- // Still no position found. Add a new screen to the end.
- screenId = LauncherSettings.Settings.call(context.getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
- .getLong(LauncherSettings.Settings.EXTRA_VALUE);
-
- // Save the screen id for binding in the workspace
- workspaceScreens.add(screenId);
- addedWorkspaceScreensFinal.add(screenId);
-
- // If we still can't find an empty space, then God help us all!!!
- if (!findNextAvailableIconSpaceInScreen(
- screenItems.get(screenId), cordinates, spanX, spanY)) {
- throw new RuntimeException("Can't find space to add the item");
- }
- }
- return Pair.create(screenId, cordinates);
+ HashSet packages = new HashSet<>();
+ packages.add(packageName);
+ enqueueModelUpdateTask(new CacheDataUpdatedTask(
+ CacheDataUpdatedTask.OP_SESSION_UPDATE, Process.myUserHandle(), packages));
}
/**
* Adds the provided items to the workspace.
*/
- public void addAndBindAddedWorkspaceItems(final Context context,
- final ArrayList extends ItemInfo> workspaceApps) {
- final Callbacks callbacks = getCallback();
- if (workspaceApps.isEmpty()) {
- return;
- }
- // Process the newly added applications and add them to the database first
- Runnable r = new Runnable() {
- public void run() {
- final ArrayList addedShortcutsFinal = new ArrayList();
- final ArrayList addedWorkspaceScreensFinal = new ArrayList();
-
- // Get the list of workspace screens. We need to append to this list and
- // can not use sBgWorkspaceScreens because loadWorkspace() may not have been
- // called.
- ArrayList workspaceScreens = loadWorkspaceScreensDb(context);
- synchronized(sBgLock) {
- for (ItemInfo item : workspaceApps) {
- if (item instanceof ShortcutInfo) {
- // Short-circuit this logic if the icon exists somewhere on the workspace
- if (shortcutExists(context, item.getIntent(), item.user)) {
- continue;
- }
- }
-
- // Find appropriate space for the item.
- Pair coords = findSpaceForItem(context,
- workspaceScreens, addedWorkspaceScreensFinal, 1, 1);
- long screenId = coords.first;
- int[] cordinates = coords.second;
-
- ItemInfo itemInfo;
- if (item instanceof ShortcutInfo || item instanceof FolderInfo) {
- itemInfo = item;
- } else if (item instanceof AppInfo) {
- itemInfo = ((AppInfo) item).makeShortcut();
- } else {
- throw new RuntimeException("Unexpected info type");
- }
-
- // Add the shortcut to the db
- addItemToDatabase(context, itemInfo,
- LauncherSettings.Favorites.CONTAINER_DESKTOP,
- screenId, cordinates[0], cordinates[1]);
- // Save the ShortcutInfo for binding in the workspace
- addedShortcutsFinal.add(itemInfo);
- }
- }
-
- // Update the workspace screens
- updateWorkspaceScreenOrder(context, workspaceScreens);
-
- if (!addedShortcutsFinal.isEmpty()) {
- runOnMainThread(new Runnable() {
- public void run() {
- Callbacks cb = getCallback();
- if (callbacks == cb && cb != null) {
- final ArrayList addAnimated = new ArrayList();
- final ArrayList addNotAnimated = new ArrayList();
- if (!addedShortcutsFinal.isEmpty()) {
- ItemInfo info = addedShortcutsFinal.get(addedShortcutsFinal.size() - 1);
- long lastScreenId = info.screenId;
- for (ItemInfo i : addedShortcutsFinal) {
- if (i.screenId == lastScreenId) {
- addAnimated.add(i);
- } else {
- addNotAnimated.add(i);
- }
- }
- }
- callbacks.bindAppsAdded(addedWorkspaceScreensFinal,
- addNotAnimated, addAnimated, null);
- }
- }
- });
- }
- }
- };
- runOnWorkerThread(r);
+ public void addAndBindAddedWorkspaceItems(List workspaceApps) {
+ addAndBindAddedWorkspaceItems(Provider.of(workspaceApps));
}
/**
- * Adds an item to the DB if it was not created previously, or move it to a new
- *
+ * Adds the provided items to the workspace.
*/
- public static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
- long screenId, int cellX, int cellY) {
- if (item.container == ItemInfo.NO_ID) {
- // From all apps
- addItemToDatabase(context, item, container, screenId, cellX, cellY);
- } else {
- // From somewhere else
- moveItemInDatabase(context, item, container, screenId, cellX, cellY);
- }
+ public void addAndBindAddedWorkspaceItems(
+ Provider> appsProvider) {
+ enqueueModelUpdateTask(new AddWorkspaceItemsTask(appsProvider));
+ }
+
+ public ModelWriter getWriter(boolean hasVerticalHotseat) {
+ return new ModelWriter(mApp.getContext(), sBgDataModel, hasVerticalHotseat);
}
static void checkItemInfoLocked(
final long itemId, final ItemInfo item, StackTraceElement[] stackTrace) {
- ItemInfo modelItem = sBgItemsIdMap.get(itemId);
+ ItemInfo modelItem = sBgDataModel.itemsIdMap.get(itemId);
if (modelItem != null && item != modelItem) {
// check all the data is consistent
if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
@@ -620,7 +318,7 @@ public class LauncherModel extends BroadcastReceiver
final long itemId = item.id;
Runnable r = new Runnable() {
public void run() {
- synchronized (sBgLock) {
+ synchronized (sBgDataModel) {
checkItemInfoLocked(itemId, item, stackTrace);
}
}
@@ -628,439 +326,11 @@ public class LauncherModel extends BroadcastReceiver
runOnWorkerThread(r);
}
- static void updateItemInDatabaseHelper(Context context, final ContentValues values,
- final ItemInfo item, final String callingFunction) {
- final long itemId = item.id;
- final Uri uri = LauncherSettings.Favorites.getContentUri(itemId);
- final ContentResolver cr = context.getContentResolver();
-
- final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
- Runnable r = new Runnable() {
- public void run() {
- cr.update(uri, values, null, null);
- updateItemArrays(item, itemId, stackTrace);
- }
- };
- runOnWorkerThread(r);
- }
-
- static void updateItemsInDatabaseHelper(Context context, final ArrayList valuesList,
- final ArrayList items, final String callingFunction) {
- final ContentResolver cr = context.getContentResolver();
-
- final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
- Runnable r = new Runnable() {
- public void run() {
- ArrayList ops =
- new ArrayList();
- int count = items.size();
- for (int i = 0; i < count; i++) {
- ItemInfo item = items.get(i);
- final long itemId = item.id;
- final Uri uri = LauncherSettings.Favorites.getContentUri(itemId);
- ContentValues values = valuesList.get(i);
-
- ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
- updateItemArrays(item, itemId, stackTrace);
-
- }
- try {
- cr.applyBatch(LauncherProvider.AUTHORITY, ops);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- };
- runOnWorkerThread(r);
- }
-
- static void updateItemArrays(ItemInfo item, long itemId, StackTraceElement[] stackTrace) {
- // Lock on mBgLock *after* the db operation
- synchronized (sBgLock) {
- checkItemInfoLocked(itemId, item, stackTrace);
-
- if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
- item.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- // Item is in a folder, make sure this folder exists
- if (!sBgFolders.containsKey(item.container)) {
- // An items container is being set to a that of an item which is not in
- // the list of Folders.
- String msg = "item: " + item + " container being set to: " +
- item.container + ", not in the list of folders";
- Log.e(TAG, msg);
- }
- }
-
- // Items are added/removed from the corresponding FolderInfo elsewhere, such
- // as in Workspace.onDrop. Here, we just add/remove them from the list of items
- // that are on the desktop, as appropriate
- ItemInfo modelItem = sBgItemsIdMap.get(itemId);
- if (modelItem != null &&
- (modelItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
- modelItem.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT)) {
- switch (modelItem.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
- case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- if (!sBgWorkspaceItems.contains(modelItem)) {
- sBgWorkspaceItems.add(modelItem);
- }
- break;
- default:
- break;
- }
- } else {
- sBgWorkspaceItems.remove(modelItem);
- }
- }
- }
-
- /**
- * Move an item in the DB to a new
- */
- public static void moveItemInDatabase(Context context, final ItemInfo item, final long container,
- final long screenId, final int cellX, final int cellY) {
- item.container = container;
- item.cellX = cellX;
- item.cellY = cellY;
-
- // We store hotseat items in canonical form which is this orientation invariant position
- // in the hotseat
- if (context instanceof Launcher && screenId < 0 &&
- container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- item.screenId = Launcher.getLauncher(context).getHotseat()
- .getOrderInHotseat(cellX, cellY);
- } else {
- item.screenId = screenId;
- }
-
- final ContentValues values = new ContentValues();
- values.put(LauncherSettings.Favorites.CONTAINER, item.container);
- values.put(LauncherSettings.Favorites.CELLX, item.cellX);
- values.put(LauncherSettings.Favorites.CELLY, item.cellY);
- values.put(LauncherSettings.Favorites.RANK, item.rank);
- values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
-
- updateItemInDatabaseHelper(context, values, item, "moveItemInDatabase");
- }
-
- /**
- * Move items in the DB to a new . We assume that the
- * cellX, cellY have already been updated on the ItemInfos.
- */
- public static void moveItemsInDatabase(Context context, final ArrayList items,
- final long container, final int screen) {
-
- ArrayList contentValues = new ArrayList();
- int count = items.size();
-
- for (int i = 0; i < count; i++) {
- ItemInfo item = items.get(i);
- item.container = container;
-
- // We store hotseat items in canonical form which is this orientation invariant position
- // in the hotseat
- if (context instanceof Launcher && screen < 0 &&
- container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- item.screenId = Launcher.getLauncher(context).getHotseat().getOrderInHotseat(item.cellX,
- item.cellY);
- } else {
- item.screenId = screen;
- }
-
- final ContentValues values = new ContentValues();
- values.put(LauncherSettings.Favorites.CONTAINER, item.container);
- values.put(LauncherSettings.Favorites.CELLX, item.cellX);
- values.put(LauncherSettings.Favorites.CELLY, item.cellY);
- values.put(LauncherSettings.Favorites.RANK, item.rank);
- values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
-
- contentValues.add(values);
- }
- updateItemsInDatabaseHelper(context, contentValues, items, "moveItemInDatabase");
- }
-
- /**
- * Move and/or resize item in the DB to a new
- */
- static void modifyItemInDatabase(Context context, final ItemInfo item, final long container,
- final long screenId, final int cellX, final int cellY, final int spanX, final int spanY) {
- item.container = container;
- item.cellX = cellX;
- item.cellY = cellY;
- item.spanX = spanX;
- item.spanY = spanY;
-
- // We store hotseat items in canonical form which is this orientation invariant position
- // in the hotseat
- if (context instanceof Launcher && screenId < 0 &&
- container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- item.screenId = Launcher.getLauncher(context).getHotseat()
- .getOrderInHotseat(cellX, cellY);
- } else {
- item.screenId = screenId;
- }
-
- final ContentValues values = new ContentValues();
- values.put(LauncherSettings.Favorites.CONTAINER, item.container);
- values.put(LauncherSettings.Favorites.CELLX, item.cellX);
- values.put(LauncherSettings.Favorites.CELLY, item.cellY);
- values.put(LauncherSettings.Favorites.RANK, item.rank);
- values.put(LauncherSettings.Favorites.SPANX, item.spanX);
- values.put(LauncherSettings.Favorites.SPANY, item.spanY);
- values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
-
- updateItemInDatabaseHelper(context, values, item, "modifyItemInDatabase");
- }
-
- /**
- * Update an item to the database in a specified container.
- */
- public static void updateItemInDatabase(Context context, final ItemInfo item) {
- final ContentValues values = new ContentValues();
- item.onAddToDatabase(context, values);
- updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase");
- }
-
- private void assertWorkspaceLoaded() {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
- synchronized (mLock) {
- if (!mHasLoaderCompletedOnce ||
- (mLoaderTask != null && mLoaderTask.mIsLoadingAndBindingWorkspace)) {
- throw new RuntimeException("Trying to add shortcut while loader is running");
- }
- }
- }
- }
-
- /**
- * Returns true if the shortcuts already exists on the workspace. This must be called after
- * the workspace has been loaded. We identify a shortcut by its intent.
- */
- @Thunk boolean shortcutExists(Context context, Intent intent, UserHandleCompat user) {
- assertWorkspaceLoaded();
- final String intentWithPkg, intentWithoutPkg;
- if (intent.getComponent() != null) {
- // If component is not null, an intent with null package will produce
- // the same result and should also be a match.
- String packageName = intent.getComponent().getPackageName();
- if (intent.getPackage() != null) {
- intentWithPkg = intent.toUri(0);
- intentWithoutPkg = new Intent(intent).setPackage(null).toUri(0);
- } else {
- intentWithPkg = new Intent(intent).setPackage(packageName).toUri(0);
- intentWithoutPkg = intent.toUri(0);
- }
- } else {
- intentWithPkg = intent.toUri(0);
- intentWithoutPkg = intent.toUri(0);
- }
-
- synchronized (sBgLock) {
- for (ItemInfo item : sBgItemsIdMap) {
- if (item instanceof ShortcutInfo) {
- ShortcutInfo info = (ShortcutInfo) item;
- Intent targetIntent = info.promisedIntent == null
- ? info.intent : info.promisedIntent;
- if (targetIntent != null && info.user.equals(user)) {
- Intent copyIntent = new Intent(targetIntent);
- copyIntent.setSourceBounds(intent.getSourceBounds());
- String s = copyIntent.toUri(0);
- if (intentWithPkg.equals(s) || intentWithoutPkg.equals(s)) {
- return true;
- }
- }
- }
- }
- }
- return false;
- }
-
- /**
- * Add an item to the database in a specified container. Sets the container, screen, cellX and
- * cellY fields of the item. Also assigns an ID to the item.
- */
- public static void addItemToDatabase(Context context, final ItemInfo item, final long container,
- final long screenId, final int cellX, final int cellY) {
- item.container = container;
- item.cellX = cellX;
- item.cellY = cellY;
- // We store hotseat items in canonical form which is this orientation invariant position
- // in the hotseat
- if (context instanceof Launcher && screenId < 0 &&
- container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- item.screenId = Launcher.getLauncher(context).getHotseat()
- .getOrderInHotseat(cellX, cellY);
- } else {
- item.screenId = screenId;
- }
-
- final ContentValues values = new ContentValues();
- final ContentResolver cr = context.getContentResolver();
- item.onAddToDatabase(context, values);
-
- item.id = LauncherSettings.Settings.call(cr, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
- .getLong(LauncherSettings.Settings.EXTRA_VALUE);
-
- values.put(LauncherSettings.Favorites._ID, item.id);
-
- final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
- Runnable r = new Runnable() {
- public void run() {
- cr.insert(LauncherSettings.Favorites.CONTENT_URI, values);
-
- // Lock on mBgLock *after* the db operation
- synchronized (sBgLock) {
- checkItemInfoLocked(item.id, item, stackTrace);
- sBgItemsIdMap.put(item.id, item);
- switch (item.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- sBgFolders.put(item.id, (FolderInfo) item);
- // Fall through
- case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
- case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
- item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- sBgWorkspaceItems.add(item);
- } else {
- if (!sBgFolders.containsKey(item.container)) {
- // Adding an item to a folder that doesn't exist.
- String msg = "adding item: " + item + " to a folder that " +
- " doesn't exist";
- Log.e(TAG, msg);
- }
- }
- if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- incrementPinnedShortcutCount(
- ShortcutKey.fromShortcutInfo((ShortcutInfo) item),
- true /* shouldPin */);
- }
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
- sBgAppWidgets.add((LauncherAppWidgetInfo) item);
- break;
- }
- }
- }
- };
- runOnWorkerThread(r);
- }
-
- private static ArrayList getItemsByPackageName(
- final String pn, final UserHandleCompat user) {
- ItemInfoFilter filter = new ItemInfoFilter() {
- @Override
- public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
- return cn.getPackageName().equals(pn) && info.user.equals(user);
- }
- };
- return filterItemInfos(sBgItemsIdMap, filter);
- }
-
- /**
- * Removes all the items from the database corresponding to the specified package.
- */
- static void deletePackageFromDatabase(Context context, final String pn,
- final UserHandleCompat user) {
- deleteItemsFromDatabase(context, getItemsByPackageName(pn, user));
- }
-
- /**
- * Removes the specified item from the database
- */
- public static void deleteItemFromDatabase(Context context, final ItemInfo item) {
- ArrayList items = new ArrayList();
- items.add(item);
- deleteItemsFromDatabase(context, items);
- }
-
- /**
- * Removes the specified items from the database
- */
- static void deleteItemsFromDatabase(Context context, final ArrayList extends ItemInfo> items) {
- final ContentResolver cr = context.getContentResolver();
- Runnable r = new Runnable() {
- public void run() {
- for (ItemInfo item : items) {
- final Uri uri = LauncherSettings.Favorites.getContentUri(item.id);
- cr.delete(uri, null, null);
-
- // Lock on mBgLock *after* the db operation
- synchronized (sBgLock) {
- switch (item.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- sBgFolders.remove(item.id);
- for (ItemInfo info: sBgItemsIdMap) {
- if (info.container == item.id) {
- // We are deleting a folder which still contains items that
- // think they are contained by that folder.
- String msg = "deleting a folder (" + item + ") which still " +
- "contains items (" + info + ")";
- Log.e(TAG, msg);
- }
- }
- sBgWorkspaceItems.remove(item);
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- decrementPinnedShortcutCount(ShortcutKey.fromShortcutInfo(
- (ShortcutInfo) item));
- // Fall through.
- case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
- sBgWorkspaceItems.remove(item);
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
- sBgAppWidgets.remove((LauncherAppWidgetInfo) item);
- break;
- }
- sBgItemsIdMap.remove(item.id);
- }
- }
- }
- };
- runOnWorkerThread(r);
- }
-
- /**
- * Decrement the count for the given pinned shortcut, unpinning it if the count becomes 0.
- */
- private static void decrementPinnedShortcutCount(final ShortcutKey pinnedShortcut) {
- synchronized (sBgLock) {
- MutableInt count = sBgPinnedShortcutCounts.get(pinnedShortcut);
- if (count == null || --count.value == 0) {
- LauncherAppState.getInstance().getShortcutManager().unpinShortcut(pinnedShortcut);
- }
- }
- }
-
- /**
- * Increment the count for the given shortcut, pinning it if the count becomes 1.
- *
- * As an optimization, the caller can pass shouldPin == false to avoid
- * unnecessary RPC's if the shortcut is already pinned.
- */
- private static void incrementPinnedShortcutCount(ShortcutKey pinnedShortcut, boolean shouldPin) {
- synchronized (sBgLock) {
- MutableInt count = sBgPinnedShortcutCounts.get(pinnedShortcut);
- if (count == null) {
- count = new MutableInt(1);
- sBgPinnedShortcutCounts.put(pinnedShortcut, count);
- } else {
- count.value++;
- }
- if (shouldPin && count.value == 1) {
- LauncherAppState.getInstance().getShortcutManager().pinShortcut(pinnedShortcut);
- }
- }
- }
-
/**
* Update the order of the workspace screens in the database. The array list contains
* a list of screen ids in the order that they should appear.
*/
- public void updateWorkspaceScreenOrder(Context context, final ArrayList screens) {
+ public static void updateWorkspaceScreenOrder(Context context, final ArrayList screens) {
final ArrayList screensCopy = new ArrayList(screens);
final ContentResolver cr = context.getContentResolver();
final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
@@ -1095,38 +365,9 @@ public class LauncherModel extends BroadcastReceiver
throw new RuntimeException(ex);
}
- synchronized (sBgLock) {
- sBgWorkspaceScreens.clear();
- sBgWorkspaceScreens.addAll(screensCopy);
- }
- }
- };
- runOnWorkerThread(r);
- }
-
- /**
- * Remove the specified folder and all its contents from the database.
- */
- public static void deleteFolderAndContentsFromDatabase(Context context, final FolderInfo info) {
- final ContentResolver cr = context.getContentResolver();
-
- Runnable r = new Runnable() {
- public void run() {
- cr.delete(LauncherSettings.Favorites.getContentUri(info.id), null, null);
- // Lock on mBgLock *after* the db operation
- synchronized (sBgLock) {
- sBgItemsIdMap.remove(info.id);
- sBgFolders.remove(info.id);
- sBgWorkspaceItems.remove(info);
- }
-
- cr.delete(LauncherSettings.Favorites.CONTENT_URI,
- LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
- // Lock on mBgLock *after* the db operation
- synchronized (sBgLock) {
- for (ItemInfo childInfo : info.contents) {
- sBgItemsIdMap.remove(childInfo.id);
- }
+ synchronized (sBgDataModel) {
+ sBgDataModel.workspaceScreens.clear();
+ sBgDataModel.workspaceScreens.addAll(screensCopy);
}
}
};
@@ -1146,66 +387,64 @@ public class LauncherModel extends BroadcastReceiver
}
@Override
- public void onPackageChanged(String packageName, UserHandleCompat user) {
+ public void onPackageChanged(String packageName, UserHandle user) {
int op = PackageUpdatedTask.OP_UPDATE;
- enqueueItemUpdatedTask(new PackageUpdatedTask(op, new String[] { packageName },
- user));
+ enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
}
@Override
- public void onPackageRemoved(String packageName, UserHandleCompat user) {
+ public void onPackageRemoved(String packageName, UserHandle user) {
+ onPackagesRemoved(user, packageName);
+ }
+
+ public void onPackagesRemoved(UserHandle user, String... packages) {
int op = PackageUpdatedTask.OP_REMOVE;
- enqueueItemUpdatedTask(new PackageUpdatedTask(op, new String[] { packageName },
- user));
+ enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packages));
}
@Override
- public void onPackageAdded(String packageName, UserHandleCompat user) {
+ public void onPackageAdded(String packageName, UserHandle user) {
int op = PackageUpdatedTask.OP_ADD;
- enqueueItemUpdatedTask(new PackageUpdatedTask(op, new String[] { packageName },
- user));
+ enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
}
@Override
- public void onPackagesAvailable(String[] packageNames, UserHandleCompat user,
+ public void onPackagesAvailable(String[] packageNames, UserHandle user,
boolean replacing) {
- enqueueItemUpdatedTask(
- new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, packageNames, user));
+ enqueueModelUpdateTask(
+ new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, user, packageNames));
}
@Override
- public void onPackagesUnavailable(String[] packageNames, UserHandleCompat user,
+ public void onPackagesUnavailable(String[] packageNames, UserHandle user,
boolean replacing) {
if (!replacing) {
- enqueueItemUpdatedTask(new PackageUpdatedTask(
- PackageUpdatedTask.OP_UNAVAILABLE, packageNames,
- user));
+ enqueueModelUpdateTask(new PackageUpdatedTask(
+ PackageUpdatedTask.OP_UNAVAILABLE, user, packageNames));
}
}
@Override
- public void onPackagesSuspended(String[] packageNames, UserHandleCompat user) {
- enqueueItemUpdatedTask(new PackageUpdatedTask(
- PackageUpdatedTask.OP_SUSPEND, packageNames,
- user));
+ public void onPackagesSuspended(String[] packageNames, UserHandle user) {
+ enqueueModelUpdateTask(new PackageUpdatedTask(
+ PackageUpdatedTask.OP_SUSPEND, user, packageNames));
}
@Override
- public void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user) {
- enqueueItemUpdatedTask(new PackageUpdatedTask(
- PackageUpdatedTask.OP_UNSUSPEND, packageNames,
- user));
+ public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
+ enqueueModelUpdateTask(new PackageUpdatedTask(
+ PackageUpdatedTask.OP_UNSUSPEND, user, packageNames));
}
@Override
public void onShortcutsChanged(String packageName, List shortcuts,
- UserHandleCompat user) {
- enqueueItemUpdatedTask(new ShortcutsChangedTask(packageName, shortcuts, user, true));
+ UserHandle user) {
+ enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, true));
}
public void updatePinnedShortcuts(String packageName, List shortcuts,
- UserHandleCompat user) {
- enqueueItemUpdatedTask(new ShortcutsChangedTask(packageName, shortcuts, user, false));
+ UserHandle user) {
+ enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, false));
}
/**
@@ -1227,20 +466,19 @@ public class LauncherModel extends BroadcastReceiver
} else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) ||
Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
- UserHandleCompat user = UserHandleCompat.fromIntent(intent);
+ UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
if (user != null) {
if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
- enqueueItemUpdatedTask(new PackageUpdatedTask(
- PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE,
- new String[0], user));
+ enqueueModelUpdateTask(new PackageUpdatedTask(
+ PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user));
}
// ACTION_MANAGED_PROFILE_UNAVAILABLE sends the profile back to locked mode, so
// we need to run the state change task again.
if (Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) ||
Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
- enqueueItemUpdatedTask(new UserLockStateChangedTask(user));
+ enqueueModelUpdateTask(new UserLockStateChangedTask(user));
}
}
} else if (Intent.ACTION_WALLPAPER_CHANGED.equals(action)) {
@@ -1248,8 +486,16 @@ public class LauncherModel extends BroadcastReceiver
}
}
- void forceReload() {
- resetLoadedState(true, true);
+ /**
+ * Reloads the workspace items from the DB and re-binds the workspace. This should generally
+ * not be called as DB updates are automatically followed by UI update
+ */
+ public void forceReload() {
+ synchronized (mLock) {
+ // Stop any existing loaders first, so they don't set mModelLoaded to true later
+ stopLoaderLocked();
+ mModelLoaded = false;
+ }
// Do this here because if the launcher activity is running it will be restarted.
// If it's not running startLoaderFromBackground will merely tell it that it needs
@@ -1257,19 +503,6 @@ public class LauncherModel extends BroadcastReceiver
startLoaderFromBackground();
}
- public void resetLoadedState(boolean resetAllAppsLoaded, boolean resetWorkspaceLoaded) {
- synchronized (mLock) {
- // Stop any existing loaders first, so they don't set mAllAppsLoaded or
- // mWorkspaceLoaded to true later
- stopLoaderLocked();
- if (resetAllAppsLoaded) mAllAppsLoaded = false;
- if (resetWorkspaceLoaded) mWorkspaceLoaded = false;
- // Always reset deep shortcuts loaded.
- // TODO: why?
- mDeepShortcutsLoaded = false;
- }
- }
-
/**
* When the launcher is in the background, it's possible for it to miss paired
* configuration changes. So whenever we trigger the loader from the background
@@ -1321,9 +554,8 @@ public class LauncherModel extends BroadcastReceiver
// If there is already one running, tell it to stop.
stopLoaderLocked();
mLoaderTask = new LoaderTask(mApp.getContext(), synchronousBindPage);
- // TODO: mDeepShortcutsLoaded does not need to be true for synchronous bind.
- if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE && mAllAppsLoaded
- && mWorkspaceLoaded && mDeepShortcutsLoaded && !mIsLoaderTaskRunning) {
+ if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
+ && mModelLoaded && !mIsLoaderTaskRunning) {
mLoaderTask.runBindSynchronousPage(synchronousBindPage);
return true;
} else {
@@ -1375,28 +607,6 @@ public class LauncherModel extends BroadcastReceiver
mPageToBindFirst = pageToBindFirst;
}
- private void loadAndBindWorkspace() {
- mIsLoadingAndBindingWorkspace = true;
-
- // Load the workspace
- if (DEBUG_LOADERS) {
- Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
- }
-
- if (!mWorkspaceLoaded) {
- loadWorkspace();
- synchronized (LoaderTask.this) {
- if (mStopped) {
- return;
- }
- mWorkspaceLoaded = true;
- }
- }
-
- // Bind the workspace
- bindWorkspace(mPageToBindFirst);
- }
-
private void waitForIdle() {
// Wait until the either we're stopped or the other threads are done.
// This way we don't start loading all apps until the workspace has settled
@@ -1439,7 +649,7 @@ public class LauncherModel extends BroadcastReceiver
throw new RuntimeException("Should not call runBindSynchronousPage() without " +
"valid page index");
}
- if (!mAllAppsLoaded || !mWorkspaceLoaded) {
+ if (!mModelLoaded) {
// Ensure that we don't try and bind a specified page when the pages have not been
// loaded already (we should load everything asynchronously in that case)
throw new RuntimeException("Expecting AllApps and Workspace to be loaded");
@@ -1471,6 +681,14 @@ public class LauncherModel extends BroadcastReceiver
bindDeepShortcuts();
}
+ private void verifyNotStopped() throws CancellationException {
+ synchronized (LoaderTask.this) {
+ if (mStopped) {
+ throw new CancellationException("Loader stopped");
+ }
+ }
+ }
+
public void run() {
synchronized (mLock) {
if (mStopped) {
@@ -1478,41 +696,72 @@ public class LauncherModel extends BroadcastReceiver
}
mIsLoaderTaskRunning = true;
}
- // Optimize for end-user experience: if the Launcher is up and // running with the
- // All Apps interface in the foreground, load All Apps first. Otherwise, load the
- // workspace first (default).
- keep_running: {
- if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
- loadAndBindWorkspace();
- if (mStopped) {
- break keep_running;
- }
+ try {
+ if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
+ // Set to false in bindWorkspace()
+ mIsLoadingAndBindingWorkspace = true;
+ loadWorkspace();
+ verifyNotStopped();
+ if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace");
+ bindWorkspace(mPageToBindFirst);
+
+ // Take a break
+ if (DEBUG_LOADERS) Log.d(TAG, "step 1 completed, wait for idle");
waitForIdle();
+ verifyNotStopped();
// second step
- if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
- loadAndBindAllApps();
+ if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps");
+ loadAllApps();
+ verifyNotStopped();
+ if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Update icon cache");
+ updateIconCache();
+
+ // Take a break
+ if (DEBUG_LOADERS) Log.d(TAG, "step 2 completed, wait for idle");
waitForIdle();
+ verifyNotStopped();
// third step
- if (DEBUG_LOADERS) Log.d(TAG, "step 3: loading deep shortcuts");
- loadAndBindDeepShortcuts();
- }
+ if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts");
+ loadDeepShortcuts();
- // Clear out this reference, otherwise we end up holding it until all of the
- // callback runnables are done.
- mContext = null;
+ verifyNotStopped();
+ if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts");
+ bindDeepShortcuts();
- synchronized (mLock) {
- // If we are still the last one to be scheduled, remove ourselves.
- if (mLoaderTask == this) {
- mLoaderTask = null;
+ // Take a break
+ if (DEBUG_LOADERS) Log.d(TAG, "step 3 completed, wait for idle");
+ waitForIdle();
+ verifyNotStopped();
+
+ // fourth step
+ if (DEBUG_LOADERS) Log.d(TAG, "step 4.1: loading widgets");
+ refreshAndBindWidgetsAndShortcuts(getCallback(), false /* bindFirst */,
+ null /* packageUser */);
+
+ synchronized (mLock) {
+ // Everything loaded bind the data.
+ mModelLoaded = true;
+ mHasLoaderCompletedOnce = true;
+ }
+ } catch (CancellationException e) {
+ // Loader stopped, ignore
+ } finally {
+ // Clear out this reference, otherwise we end up holding it until all of the
+ // callback runnables are done.
+ mContext = null;
+
+ synchronized (mLock) {
+ // If we are still the last one to be scheduled, remove ourselves.
+ if (mLoaderTask == this) {
+ mLoaderTask = null;
+ }
+ mIsLoaderTaskRunning = false;
}
- mIsLoaderTaskRunning = false;
- mHasLoaderCompletedOnce = true;
}
}
@@ -1553,125 +802,19 @@ public class LauncherModel extends BroadcastReceiver
}
}
- // check & update map of what's occupied; used to discard overlapping/invalid items
- private boolean checkItemPlacement(LongArrayMap occupied, ItemInfo item,
- ArrayList workspaceScreens) {
- LauncherAppState app = LauncherAppState.getInstance();
- InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
-
- long containerIndex = item.screenId;
- if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- // Return early if we detect that an item is under the hotseat button
- if (!FeatureFlags.NO_ALL_APPS_ICON &&
- profile.isAllAppsButtonRank((int) item.screenId)) {
- Log.e(TAG, "Error loading shortcut into hotseat " + item
- + " into position (" + item.screenId + ":" + item.cellX + ","
- + item.cellY + ") occupied by all apps");
- return false;
- }
-
- final GridOccupancy hotseatOccupancy =
- occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
-
- if (item.screenId >= profile.numHotseatIcons) {
- Log.e(TAG, "Error loading shortcut " + item
- + " into hotseat position " + item.screenId
- + ", position out of bounds: (0 to " + (profile.numHotseatIcons - 1)
- + ")");
- return false;
- }
-
- if (hotseatOccupancy != null) {
- if (hotseatOccupancy.cells[(int) item.screenId][0]) {
- Log.e(TAG, "Error loading shortcut into hotseat " + item
- + " into position (" + item.screenId + ":" + item.cellX + ","
- + item.cellY + ") already occupied");
- return false;
- } else {
- hotseatOccupancy.cells[(int) item.screenId][0] = true;
- return true;
- }
- } else {
- final GridOccupancy occupancy = new GridOccupancy(profile.numHotseatIcons, 1);
- occupancy.cells[(int) item.screenId][0] = true;
- occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, occupancy);
- return true;
- }
- } else if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- if (!workspaceScreens.contains((Long) item.screenId)) {
- // The item has an invalid screen id.
- return false;
- }
- } else {
- // Skip further checking if it is not the hotseat or workspace container
- return true;
- }
-
- final int countX = profile.numColumns;
- final int countY = profile.numRows;
- if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
- item.cellX < 0 || item.cellY < 0 ||
- item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
- Log.e(TAG, "Error loading shortcut " + item
- + " into cell (" + containerIndex + "-" + item.screenId + ":"
- + item.cellX + "," + item.cellY
- + ") out of screen bounds ( " + countX + "x" + countY + ")");
- return false;
- }
-
- if (!occupied.containsKey(item.screenId)) {
- GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1);
- if (item.screenId == Workspace.FIRST_SCREEN_ID) {
- // Mark the first row as occupied (if the feature is enabled)
- // in order to account for the QSB.
- screen.markCells(0, 0, countX + 1, 1, FeatureFlags.QSB_ON_FIRST_SCREEN);
- }
- occupied.put(item.screenId, screen);
- }
- final GridOccupancy occupancy = occupied.get(item.screenId);
-
- // Check if any workspace icons overlap with each other
- if (occupancy.isRegionVacant(item.cellX, item.cellY, item.spanX, item.spanY)) {
- occupancy.markCells(item, true);
- return true;
- } else {
- Log.e(TAG, "Error loading shortcut " + item
- + " into cell (" + containerIndex + "-" + item.screenId + ":"
- + item.cellX + "," + item.cellX + "," + item.spanX + "," + item.spanY
- + ") already occupied");
- return false;
- }
- }
-
- /** Clears all the sBg data structures */
- private void clearSBgDataStructures() {
- synchronized (sBgLock) {
- sBgWorkspaceItems.clear();
- sBgAppWidgets.clear();
- sBgFolders.clear();
- sBgItemsIdMap.clear();
- sBgWorkspaceScreens.clear();
- sBgPinnedShortcutCounts.clear();
- }
- }
-
private void loadWorkspace() {
if (LauncherAppState.PROFILE_STARTUP) {
Trace.beginSection("Loading Workspace");
}
- final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
final Context context = mContext;
final ContentResolver contentResolver = context.getContentResolver();
- final PackageManager manager = context.getPackageManager();
- final boolean isSafeMode = manager.isSafeMode();
+ final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
+ final boolean isSafeMode = pmHelper.isSafeMode();
final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+ final DeepShortcutManager shortcutManager = DeepShortcutManager.getInstance(context);
final boolean isSdCardReady = Utilities.isBootCompleted();
-
- LauncherAppState app = LauncherAppState.getInstance();
- InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
- int countX = profile.numColumns;
- int countY = profile.numRows;
+ final MultiHashMap pendingPackages = new MultiHashMap<>();
boolean clearDb = false;
try {
@@ -1690,68 +833,44 @@ public class LauncherModel extends BroadcastReceiver
if (clearDb) {
Log.d(TAG, "loadWorkspace: resetting launcher database");
LauncherSettings.Settings.call(contentResolver,
- LauncherSettings.Settings.METHOD_DELETE_DB);
+ LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
}
Log.d(TAG, "loadWorkspace: loading default favorites");
LauncherSettings.Settings.call(contentResolver,
LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
- synchronized (sBgLock) {
- clearSBgDataStructures();
+ synchronized (sBgDataModel) {
+ sBgDataModel.clear();
+
final HashMap installingPkgs = PackageInstallerCompat
.getInstance(mContext).updateAndGetActiveSessionCache();
- sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
+ sBgDataModel.workspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
- final ArrayList itemsToRemove = new ArrayList<>();
- final ArrayList restoredRows = new ArrayList<>();
Map shortcutKeyToPinnedShortcuts = new HashMap<>();
- final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
- if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
- final Cursor c = contentResolver.query(contentUri, null, null, null, null);
+ final LoaderCursor c = new LoaderCursor(contentResolver.query(
+ LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
- // +1 for the hotseat (it can be larger than the workspace)
- // Load workspace in reverse order to ensure that latest items are loaded first (and
- // before any earlier duplicates)
- final LongArrayMap occupied = new LongArrayMap<>();
HashMap widgetProvidersMap = null;
try {
- final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
- final int intentIndex = c.getColumnIndexOrThrow
- (LauncherSettings.Favorites.INTENT);
- final int containerIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.CONTAINER);
- final int itemTypeIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.ITEM_TYPE);
final int appWidgetIdIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.APPWIDGET_ID);
final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.APPWIDGET_PROVIDER);
- final int screenIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.SCREEN);
- final int cellXIndex = c.getColumnIndexOrThrow
- (LauncherSettings.Favorites.CELLX);
- final int cellYIndex = c.getColumnIndexOrThrow
- (LauncherSettings.Favorites.CELLY);
final int spanXIndex = c.getColumnIndexOrThrow
(LauncherSettings.Favorites.SPANX);
final int spanYIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.SPANY);
final int rankIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.RANK);
- final int restoredIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.RESTORED);
- final int profileIdIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.PROFILE_ID);
final int optionsIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.OPTIONS);
- final CursorIconInfo cursorIconInfo = new CursorIconInfo(mContext, c);
- final LongSparseArray allUsers = new LongSparseArray<>();
+ final LongSparseArray allUsers = c.allUsers;
final LongSparseArray quietMode = new LongSparseArray<>();
final LongSparseArray unlockedUsers = new LongSparseArray<>();
- for (UserHandleCompat user : mUserManager.getUserProfiles()) {
+ for (UserHandle user : mUserManager.getUserProfiles()) {
long serialNo = mUserManager.getSerialNumberForUser(user);
allUsers.put(serialNo, user);
quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
@@ -1761,8 +880,8 @@ public class LauncherModel extends BroadcastReceiver
// We can only query for shortcuts when the user is unlocked.
if (userUnlocked) {
List pinnedShortcuts =
- mDeepShortcutManager.queryForPinnedShortcuts(null, user);
- if (mDeepShortcutManager.wasLastCallSuccess()) {
+ shortcutManager.queryForPinnedShortcuts(null, user);
+ if (shortcutManager.wasLastCallSuccess()) {
for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
shortcut);
@@ -1778,215 +897,174 @@ public class LauncherModel extends BroadcastReceiver
}
ShortcutInfo info;
- String intentDescription;
LauncherAppWidgetInfo appWidgetInfo;
- int container;
- long id;
- long serialNumber;
Intent intent;
- UserHandleCompat user;
- String targetPackage;
+ String targetPkg;
while (!mStopped && c.moveToNext()) {
try {
- int itemType = c.getInt(itemTypeIndex);
- boolean restored = 0 != c.getInt(restoredIndex);
- boolean allowMissingTarget = false;
- container = c.getInt(containerIndex);
+ if (c.user == null) {
+ // User has been deleted, remove the item.
+ c.markDeleted("User has been deleted");
+ continue;
+ }
- switch (itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+ boolean allowMissingTarget = false;
+ switch (c.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+ case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- id = c.getLong(idIndex);
- intentDescription = c.getString(intentIndex);
- serialNumber = c.getInt(profileIdIndex);
- user = allUsers.get(serialNumber);
- int promiseType = c.getInt(restoredIndex);
- int disabledState = 0;
- boolean itemReplaced = false;
- targetPackage = null;
- if (user == null) {
- // User has been deleted remove the item.
- itemsToRemove.add(id);
+ intent = c.parseIntent();
+ if (intent == null) {
+ c.markDeleted("Invalid or null intent");
continue;
}
- try {
- intent = Intent.parseUri(intentDescription, 0);
- ComponentName cn = intent.getComponent();
- if (cn != null && cn.getPackageName() != null) {
- boolean validPkg = launcherApps.isPackageEnabledForProfile(
- cn.getPackageName(), user);
- boolean validComponent = validPkg &&
- launcherApps.isActivityEnabledForProfile(cn, user);
- if (validPkg) {
- targetPackage = cn.getPackageName();
- }
- if (validComponent) {
- if (restored) {
- // no special handling necessary for this item
- restoredRows.add(id);
- restored = false;
- }
- if (quietMode.get(serialNumber)) {
- disabledState = ShortcutInfo.FLAG_DISABLED_QUIET_USER;
- }
- } else if (validPkg) {
- intent = null;
- if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
- // We allow auto install apps to have their intent
- // updated after an install.
- intent = manager.getLaunchIntentForPackage(
- cn.getPackageName());
- if (intent != null) {
- ContentValues values = new ContentValues();
- values.put(LauncherSettings.Favorites.INTENT,
- intent.toUri(0));
- updateItem(id, values);
- }
- }
+ int disabledState = quietMode.get(c.serialNumber) ?
+ ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0;
+ ComponentName cn = intent.getComponent();
+ targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
- if (intent == null) {
- // The app is installed but the component is no
- // longer available.
- FileLog.d(TAG, "Invalid component removed: " + cn);
- itemsToRemove.add(id);
- continue;
+ if (!Process.myUserHandle().equals(c.user)) {
+ if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+ c.markDeleted("Legacy shortcuts are only allowed for default user");
+ continue;
+ } else if (c.restoreFlag != 0) {
+ // Don't restore items for other profiles.
+ c.markDeleted("Restore from managed profile not supported");
+ continue;
+ }
+ }
+ if (TextUtils.isEmpty(targetPkg) &&
+ c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+ c.markDeleted("Only legacy shortcuts can have null package");
+ continue;
+ }
+
+ // If there is no target package, its an implicit intent
+ // (legacy shortcut) which is always valid
+ boolean validTarget = TextUtils.isEmpty(targetPkg) ||
+ launcherApps.isPackageEnabledForProfile(targetPkg, c.user);
+
+ if (cn != null && validTarget) {
+ // If the apk is present and the shortcut points to a specific
+ // component.
+
+ // If the component is already present
+ if (launcherApps.isActivityEnabledForProfile(cn, c.user)) {
+ // no special handling necessary for this item
+ c.markRestored();
+ } else {
+ if (c.hasRestoreFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
+ // We allow auto install apps to have their intent
+ // updated after an install.
+ intent = pmHelper.getAppLaunchIntent(targetPkg, c.user);
+ if (intent != null) {
+ c.restoreFlag = 0;
+ c.updater().put(
+ LauncherSettings.Favorites.INTENT,
+ intent.toUri(0)).commit();
+ cn = intent.getComponent();
} else {
- // no special handling necessary for this item
- restoredRows.add(id);
- restored = false;
- }
- } else if (restored) {
- // Package is not yet available but might be
- // installed later.
- FileLog.d(TAG, "package not yet restored: " + cn);
-
- if ((promiseType & ShortcutInfo.FLAG_RESTORE_STARTED) != 0) {
- // Restore has started once.
- } else if (installingPkgs.containsKey(cn.getPackageName())) {
- // App restore has started. Update the flag
- promiseType |= ShortcutInfo.FLAG_RESTORE_STARTED;
- ContentValues values = new ContentValues();
- values.put(LauncherSettings.Favorites.RESTORED,
- promiseType);
- updateItem(id, values);
- } else if ((promiseType & ShortcutInfo.FLAG_RESTORED_APP_TYPE) != 0) {
- // This is a common app. Try to replace this.
- int appType = CommonAppTypeParser.decodeItemTypeFromFlag(promiseType);
- CommonAppTypeParser parser = new CommonAppTypeParser(id, appType, context);
- if (parser.findDefaultApp()) {
- // Default app found. Replace it.
- intent = parser.parsedIntent;
- cn = intent.getComponent();
- ContentValues values = parser.parsedValues;
- values.put(LauncherSettings.Favorites.RESTORED, 0);
- updateItem(id, values);
- restored = false;
- itemReplaced = true;
-
- } else {
- FileLog.d(TAG, "Unrestored package removed: " + cn);
- itemsToRemove.add(id);
- continue;
- }
- } else {
- FileLog.d(TAG, "Unrestored package removed: " + cn);
- itemsToRemove.add(id);
+ c.markDeleted("Unable to find a launch target");
continue;
}
- } else if (PackageManagerHelper.isAppOnSdcard(
- manager, cn.getPackageName())) {
- // Package is present but not available.
- allowMissingTarget = true;
- disabledState = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
- } else if (!isSdCardReady) {
- // SdCard is not ready yet. Package might get available,
- // once it is ready.
- Log.d(TAG, "Invalid package: " + cn + " (check again later)");
- HashSet 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 {
- // Do not wait for external media load anymore.
- // Log the invalid package, and remove it
- FileLog.d(TAG, "Invalid package removed: " + cn);
- itemsToRemove.add(id);
+ // The app is installed but the component is no
+ // longer available.
+ c.markDeleted("Invalid component removed: " + cn);
continue;
}
- } else if (cn == null) {
- // For shortcuts with no component, keep them as they are
- restoredRows.add(id);
- restored = false;
}
- } catch (URISyntaxException e) {
- FileLog.d(TAG, "Invalid uri: " + intentDescription);
- itemsToRemove.add(id);
- continue;
+ }
+ // else if cn == null => can't infer much, leave it
+ // else if !validPkg => could be restored icon or missing sd-card
+
+ if (!TextUtils.isEmpty(targetPkg) && !validTarget) {
+ // Points to a valid app (superset of cn != null) but the apk
+ // is not available.
+
+ if (c.restoreFlag != 0) {
+ // Package is not yet available but might be
+ // installed later.
+ FileLog.d(TAG, "package not yet restored: " + targetPkg);
+
+ if (c.hasRestoreFlag(ShortcutInfo.FLAG_RESTORE_STARTED)) {
+ // Restore has started once.
+ } else if (installingPkgs.containsKey(targetPkg)) {
+ // App restore has started. Update the flag
+ c.restoreFlag |= ShortcutInfo.FLAG_RESTORE_STARTED;
+ c.updater().commit();
+ } else {
+ c.markDeleted("Unrestored app removed: " + targetPkg);
+ continue;
+ }
+ } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) {
+ // Package is present but not available.
+ disabledState |= ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
+ // Add the icon on the workspace anyway.
+ allowMissingTarget = true;
+ } else if (!isSdCardReady) {
+ // SdCard is not ready yet. Package might get available,
+ // once it is ready.
+ Log.d(TAG, "Missing pkg, will check later: " + targetPkg);
+ pendingPackages.addToList(c.user, targetPkg);
+ // Add the icon on the workspace anyway.
+ allowMissingTarget = true;
+ } else {
+ // Do not wait for external media load anymore.
+ c.markDeleted("Invalid package removed: " + targetPkg);
+ continue;
+ }
}
- boolean useLowResIcon = container >= 0 &&
+ if (validTarget) {
+ // The shortcut points to a valid target (either no target
+ // or something which is ready to be used)
+ c.markRestored();
+ }
+
+ boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() &&
c.getInt(rankIndex) >= FolderIcon.NUM_ITEMS_IN_PREVIEW;
- if (itemReplaced) {
- if (user.equals(UserHandleCompat.myUserHandle())) {
- info = getAppShortcutInfo(intent, user, null,
- cursorIconInfo, false, useLowResIcon);
- } else {
- // Don't replace items for other profiles.
- itemsToRemove.add(id);
- continue;
- }
- } else if (restored) {
- if (user.equals(UserHandleCompat.myUserHandle())) {
- info = getRestoredItemInfo(c, intent,
- promiseType, itemType, cursorIconInfo);
- intent = getRestoredItemIntent(c, context, intent);
- } else {
- // Don't restore items for other profiles.
- itemsToRemove.add(id);
- continue;
- }
- } else if (itemType ==
+ if (c.restoreFlag != 0) {
+ // Already verified above that user is same as default user
+ info = c.getRestoredItemInfo(intent);
+ } else if (c.itemType ==
LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
- info = getAppShortcutInfo(intent, user, c,
- cursorIconInfo, allowMissingTarget, useLowResIcon);
- } else if (itemType ==
+ info = c.getAppShortcutInfo(
+ intent, allowMissingTarget, useLowResIcon);
+ } else if (c.itemType ==
LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- ShortcutKey key = ShortcutKey.fromIntent(intent, user);
- if (unlockedUsers.get(serialNumber)) {
+ ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
+ if (unlockedUsers.get(c.serialNumber)) {
ShortcutInfoCompat pinnedShortcut =
shortcutKeyToPinnedShortcuts.get(key);
if (pinnedShortcut == null) {
// The shortcut is no longer valid.
- itemsToRemove.add(id);
+ c.markDeleted("Pinned shortcut not found");
continue;
}
info = new ShortcutInfo(pinnedShortcut, context);
+ info.iconBitmap = LauncherIcons
+ .createShortcutIcon(pinnedShortcut, context);
+ if (pmHelper.isAppSuspended(
+ pinnedShortcut.getPackage(), info.user)) {
+ info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+ }
intent = info.intent;
} else {
// Create a shortcut info in disabled mode for now.
- info = new ShortcutInfo();
- info.user = user;
- info.itemType = itemType;
- loadInfoFromCursor(info, c, cursorIconInfo);
-
+ info = c.loadSimpleShortcut();
info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
}
- incrementPinnedShortcutCount(key, false /* shouldPin */);
} else { // item type == ITEM_TYPE_SHORTCUT
- info = getShortcutInfo(c, cursorIconInfo);
+ info = c.loadSimpleShortcut();
// Shortcuts are only available on the primary profile
- if (PackageManagerHelper.isAppSuspended(manager, targetPackage)) {
+ if (!TextUtils.isEmpty(targetPkg)
+ && pmHelper.isAppSuspended(targetPkg, c.user)) {
disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
}
@@ -2004,121 +1082,64 @@ public class LauncherModel extends BroadcastReceiver
}
if (info != null) {
- info.id = id;
+ c.applyCommonProperties(info);
+
info.intent = intent;
- info.container = container;
- info.screenId = c.getInt(screenIndex);
- info.cellX = c.getInt(cellXIndex);
- info.cellY = c.getInt(cellYIndex);
info.rank = c.getInt(rankIndex);
info.spanX = 1;
info.spanY = 1;
- info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
- if (info.promisedIntent != null) {
- info.promisedIntent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
- }
info.isDisabled |= disabledState;
if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
}
- // check & update map of what's occupied
- if (!checkItemPlacement(occupied, info, sBgWorkspaceScreens)) {
- itemsToRemove.add(id);
- break;
- }
-
- if (restored) {
- ComponentName cn = info.getTargetComponent();
- if (cn != null) {
- Integer progress = installingPkgs.get(cn.getPackageName());
- if (progress != null) {
- info.setInstallProgress(progress);
- } else {
- info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
- }
+ if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
+ Integer progress = installingPkgs.get(targetPkg);
+ if (progress != null) {
+ info.setInstallProgress(progress);
+ } else {
+ info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
}
}
- switch (container) {
- case LauncherSettings.Favorites.CONTAINER_DESKTOP:
- case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
- sBgWorkspaceItems.add(info);
- break;
- default:
- // Item is in a user folder
- FolderInfo folderInfo =
- findOrMakeFolder(sBgFolders, container);
- folderInfo.add(info, false);
- break;
- }
- sBgItemsIdMap.put(info.id, info);
+ c.checkAndAddItem(info, sBgDataModel);
} else {
throw new RuntimeException("Unexpected null ShortcutInfo");
}
break;
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- id = c.getLong(idIndex);
- FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
+ FolderInfo folderInfo = sBgDataModel.findOrMakeFolder(c.id);
+ c.applyCommonProperties(folderInfo);
// Do not trim the folder label, as is was set by the user.
- folderInfo.title = c.getString(cursorIconInfo.titleIndex);
- folderInfo.id = id;
- folderInfo.container = container;
- folderInfo.screenId = c.getInt(screenIndex);
- folderInfo.cellX = c.getInt(cellXIndex);
- folderInfo.cellY = c.getInt(cellYIndex);
+ folderInfo.title = c.getString(c.titleIndex);
folderInfo.spanX = 1;
folderInfo.spanY = 1;
folderInfo.options = c.getInt(optionsIndex);
- // check & update map of what's occupied
- if (!checkItemPlacement(occupied, folderInfo, sBgWorkspaceScreens)) {
- itemsToRemove.add(id);
- break;
- }
+ // no special handling required for restored folders
+ c.markRestored();
- switch (container) {
- case LauncherSettings.Favorites.CONTAINER_DESKTOP:
- case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
- sBgWorkspaceItems.add(folderInfo);
- break;
- }
-
- if (restored) {
- // no special handling required for restored folders
- restoredRows.add(id);
- }
-
- sBgItemsIdMap.put(folderInfo.id, folderInfo);
- sBgFolders.put(folderInfo.id, folderInfo);
+ c.checkAndAddItem(folderInfo, sBgDataModel);
break;
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
// Read all Launcher-specific widget details
- boolean customWidget = itemType ==
+ boolean customWidget = c.itemType ==
LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
int appWidgetId = c.getInt(appWidgetIdIndex);
- serialNumber = c.getLong(profileIdIndex);
String savedProvider = c.getString(appWidgetProviderIndex);
- id = c.getLong(idIndex);
- user = allUsers.get(serialNumber);
- if (user == null) {
- itemsToRemove.add(id);
- continue;
- }
final ComponentName component =
ComponentName.unflattenFromString(savedProvider);
- final int restoreStatus = c.getInt(restoredIndex);
- final boolean isIdValid = (restoreStatus &
- LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) == 0;
- final boolean wasProviderReady = (restoreStatus &
- LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0;
+ final boolean isIdValid = !c.hasRestoreFlag(
+ LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
+ final boolean wasProviderReady = !c.hasRestoreFlag(
+ LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY);
if (widgetProvidersMap == null) {
widgetProvidersMap = AppWidgetManagerCompat
@@ -2127,14 +1148,14 @@ public class LauncherModel extends BroadcastReceiver
final AppWidgetProviderInfo provider = widgetProvidersMap.get(
new ComponentKey(
ComponentName.unflattenFromString(savedProvider),
- user));
+ c.user));
final boolean isProviderReady = isValidProvider(provider);
if (!isSafeMode && !customWidget &&
wasProviderReady && !isProviderReady) {
- FileLog.d(TAG, "Deleting widget that isn't installed anymore: "
+ c.markDeleted(
+ "Deleting widget that isn't installed anymore: "
+ provider);
- itemsToRemove.add(id);
} else {
if (isProviderReady) {
appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
@@ -2143,7 +1164,7 @@ public class LauncherModel extends BroadcastReceiver
// The provider is available. So the widget is either
// available or not available. We do not need to track
// any future restore updates.
- int status = restoreStatus &
+ int status = c.restoreFlag &
~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
if (!wasProviderReady) {
// If provider was not previously ready, update the
@@ -2159,23 +1180,22 @@ public class LauncherModel extends BroadcastReceiver
}
appWidgetInfo.restoreStatus = status;
} else {
- Log.v(TAG, "Widget restore pending id=" + id
+ Log.v(TAG, "Widget restore pending id=" + c.id
+ " appWidgetId=" + appWidgetId
- + " status =" + restoreStatus);
+ + " status =" + c.restoreFlag);
appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
component);
- appWidgetInfo.restoreStatus = restoreStatus;
+ appWidgetInfo.restoreStatus = c.restoreFlag;
Integer installProgress = installingPkgs.get(component.getPackageName());
- if ((restoreStatus & LauncherAppWidgetInfo.FLAG_RESTORE_STARTED) != 0) {
+ if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) {
// Restore has started once.
} else if (installProgress != null) {
// App restore has started. Update the flag
appWidgetInfo.restoreStatus |=
LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
} else if (!isSafeMode) {
- FileLog.d(TAG, "Unrestored widget removed: " + component);
- itemsToRemove.add(id);
+ c.markDeleted("Unrestored widget removed: " + component);
continue;
}
@@ -2184,52 +1204,34 @@ public class LauncherModel extends BroadcastReceiver
}
if (appWidgetInfo.hasRestoreFlag(
LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) {
- intentDescription = c.getString(intentIndex);
- if (!TextUtils.isEmpty(intentDescription)) {
- appWidgetInfo.bindOptions =
- Intent.parseUri(intentDescription, 0);
- }
+ appWidgetInfo.bindOptions = c.parseIntent();
}
- appWidgetInfo.id = id;
- appWidgetInfo.screenId = c.getInt(screenIndex);
- appWidgetInfo.cellX = c.getInt(cellXIndex);
- appWidgetInfo.cellY = c.getInt(cellYIndex);
+ c.applyCommonProperties(appWidgetInfo);
appWidgetInfo.spanX = c.getInt(spanXIndex);
appWidgetInfo.spanY = c.getInt(spanYIndex);
- appWidgetInfo.user = user;
+ appWidgetInfo.user = c.user;
- if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
- container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- Log.e(TAG, "Widget found where container != " +
+ if (!c.isOnWorkspaceOrHotseat()) {
+ c.markDeleted("Widget found where container != " +
"CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
- itemsToRemove.add(id);
continue;
}
- appWidgetInfo.container = container;
- // check & update map of what's occupied
- if (!checkItemPlacement(occupied, appWidgetInfo, sBgWorkspaceScreens)) {
- itemsToRemove.add(id);
- break;
- }
-
if (!customWidget) {
String providerName =
appWidgetInfo.providerName.flattenToString();
if (!providerName.equals(savedProvider) ||
- (appWidgetInfo.restoreStatus != restoreStatus)) {
- ContentValues values = new ContentValues();
- values.put(
- LauncherSettings.Favorites.APPWIDGET_PROVIDER,
- providerName);
- values.put(LauncherSettings.Favorites.RESTORED,
- appWidgetInfo.restoreStatus);
- updateItem(id, values);
+ (appWidgetInfo.restoreStatus != c.restoreFlag)) {
+ c.updater()
+ .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
+ providerName)
+ .put(LauncherSettings.Favorites.RESTORED,
+ appWidgetInfo.restoreStatus)
+ .commit();
}
}
- sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
- sBgAppWidgets.add(appWidgetInfo);
+ c.checkAndAddItem(appWidgetInfo, sBgDataModel);
}
break;
}
@@ -2243,48 +1245,48 @@ public class LauncherModel extends BroadcastReceiver
// Break early if we've stopped loading
if (mStopped) {
- clearSBgDataStructures();
+ sBgDataModel.clear();
return;
}
- if (itemsToRemove.size() > 0) {
- // Remove dead items
- contentResolver.delete(LauncherSettings.Favorites.CONTENT_URI,
- Utilities.createDbSelectionQuery(
- LauncherSettings.Favorites._ID, itemsToRemove), null);
- if (DEBUG_LOADERS) {
- Log.d(TAG, "Removed = " + Utilities.createDbSelectionQuery(
- LauncherSettings.Favorites._ID, itemsToRemove));
- }
-
+ // Remove dead items
+ if (c.commitDeleted()) {
// Remove any empty folder
ArrayList deletedFolderIds = (ArrayList) LauncherSettings.Settings
.call(contentResolver,
LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
.getSerializable(LauncherSettings.Settings.EXTRA_VALUE);
for (long folderId : deletedFolderIds) {
- sBgWorkspaceItems.remove(sBgFolders.get(folderId));
- sBgFolders.remove(folderId);
- sBgItemsIdMap.remove(folderId);
+ sBgDataModel.workspaceItems.remove(sBgDataModel.folders.get(folderId));
+ sBgDataModel.folders.remove(folderId);
+ sBgDataModel.itemsIdMap.remove(folderId);
}
+
+ // Remove any ghost widgets
+ LauncherSettings.Settings.call(contentResolver,
+ LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS);
}
// Unpin shortcuts that don't exist on the workspace.
+ HashSet pendingShortcuts =
+ InstallShortcutReceiver.getPendingShortcuts(context);
for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
- MutableInt numTimesPinned = sBgPinnedShortcutCounts.get(key);
- if (numTimesPinned == null || numTimesPinned.value == 0) {
+ MutableInt numTimesPinned = sBgDataModel.pinnedShortcutCounts.get(key);
+ if ((numTimesPinned == null || numTimesPinned.value == 0)
+ && !pendingShortcuts.contains(key)) {
// Shortcut is pinned but doesn't exist on the workspace; unpin it.
- mDeepShortcutManager.unpinShortcut(key);
+ shortcutManager.unpinShortcut(key);
}
}
// Sort all the folder items and make sure the first 3 items are high resolution.
- for (FolderInfo folder : sBgFolders) {
+ for (FolderInfo folder : sBgDataModel.folders) {
Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
int pos = 0;
for (ShortcutInfo info : folder.contents) {
- if (info.usingLowResIcon) {
- info.updateIcon(mIconCache, false);
+ if (info.usingLowResIcon &&
+ info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+ mIconCache.getTitleAndIcon(info, false);
}
pos ++;
if (pos >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
@@ -2293,24 +1295,19 @@ public class LauncherModel extends BroadcastReceiver
}
}
- if (restoredRows.size() > 0) {
- // Update restored items that no longer require special handling
- ContentValues values = new ContentValues();
- values.put(LauncherSettings.Favorites.RESTORED, 0);
- contentResolver.update(LauncherSettings.Favorites.CONTENT_URI, values,
- Utilities.createDbSelectionQuery(
- LauncherSettings.Favorites._ID, restoredRows), null);
- }
-
- if (!isSdCardReady && !sPendingPackages.isEmpty()) {
- context.registerReceiver(new AppsAvailabilityCheck(),
+ c.commitRestoredItems();
+ if (!isSdCardReady && !pendingPackages.isEmpty()) {
+ context.registerReceiver(
+ new SdCardAvailableReceiver(
+ LauncherModel.this, mContext, pendingPackages),
new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
- null, sWorker);
+ null,
+ sWorker);
}
// Remove any empty screens
- ArrayList unusedScreens = new ArrayList(sBgWorkspaceScreens);
- for (ItemInfo item: sBgItemsIdMap) {
+ ArrayList unusedScreens = new ArrayList<>(sBgDataModel.workspaceScreens);
+ for (ItemInfo item: sBgDataModel.itemsIdMap) {
long screenId = item.screenId;
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
unusedScreens.contains(screenId)) {
@@ -2320,25 +1317,8 @@ public class LauncherModel extends BroadcastReceiver
// If there are any empty screens remove them, and update.
if (unusedScreens.size() != 0) {
- sBgWorkspaceScreens.removeAll(unusedScreens);
- updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
- }
-
- if (DEBUG_LOADERS) {
- Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
- Log.d(TAG, "workspace layout: ");
- int nScreens = occupied.size();
- for (int y = 0; y < countY; y++) {
- String line = "";
-
- for (int i = 0; i < nScreens; i++) {
- long screenId = occupied.keyAt(i);
- if (screenId > 0) {
- line += " | ";
- }
- }
- Log.d(TAG, "[ " + line + " ]");
- }
+ sBgDataModel.workspaceScreens.removeAll(unusedScreens);
+ updateWorkspaceScreenOrder(context, sBgDataModel.workspaceScreens);
}
}
if (LauncherAppState.PROFILE_STARTUP) {
@@ -2346,17 +1326,6 @@ public class LauncherModel extends BroadcastReceiver
}
}
- /**
- * Partially updates the item without any notification. Must be called on the worker thread.
- */
- private void updateItem(long itemId, ContentValues update) {
- mContext.getContentResolver().update(
- LauncherSettings.Favorites.CONTENT_URI,
- update,
- BaseColumns._ID + "= ?",
- new String[]{Long.toString(itemId)});
- }
-
/** Filters the set of items who are directly or indirectly (via another container) on the
* specified screen. */
private void filterCurrentWorkspaceItems(long currentScreenId,
@@ -2424,8 +1393,7 @@ public class LauncherModel extends BroadcastReceiver
/** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
* right) */
private void sortWorkspaceItemsSpatially(ArrayList workspaceItems) {
- final LauncherAppState app = LauncherAppState.getInstance();
- final InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
+ final InvariantDeviceProfile profile = mApp.getInvariantDeviceProfile();
final int screenCols = profile.numColumns;
final int screenCellCount = profile.numColumns * profile.numRows;
Collections.sort(workspaceItems, new Comparator() {
@@ -2534,10 +1502,10 @@ public class LauncherModel extends BroadcastReceiver
ArrayList appWidgets = new ArrayList<>();
ArrayList orderedScreenIds = new ArrayList<>();
- synchronized (sBgLock) {
- workspaceItems.addAll(sBgWorkspaceItems);
- appWidgets.addAll(sBgAppWidgets);
- orderedScreenIds.addAll(sBgWorkspaceScreens);
+ synchronized (sBgDataModel) {
+ workspaceItems.addAll(sBgDataModel.workspaceItems);
+ appWidgets.addAll(sBgDataModel.appWidgets);
+ orderedScreenIds.addAll(sBgDataModel.workspaceScreens);
}
final int currentScreen;
@@ -2655,34 +1623,11 @@ public class LauncherModel extends BroadcastReceiver
}
}
- private void loadAndBindAllApps() {
- if (DEBUG_LOADERS) {
- Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
- }
- if (!mAllAppsLoaded) {
- loadAllApps();
- synchronized (LoaderTask.this) {
- if (mStopped) {
- return;
- }
- }
- updateIconCache();
- synchronized (LoaderTask.this) {
- if (mStopped) {
- return;
- }
- mAllAppsLoaded = true;
- }
- } else {
- onlyBindAllApps();
- }
- }
-
private void updateIconCache() {
// Ignore packages which have a promise icon.
HashSet packagesToIgnore = new HashSet<>();
- synchronized (sBgLock) {
- for (ItemInfo info : sBgItemsIdMap) {
+ synchronized (sBgDataModel) {
+ for (ItemInfo info : sBgDataModel.itemsIdMap) {
if (info instanceof ShortcutInfo) {
ShortcutInfo si = (ShortcutInfo) info;
if (si.isPromise() && si.getTargetComponent() != null) {
@@ -2728,7 +1673,7 @@ public class LauncherModel extends BroadcastReceiver
}
private void scheduleManagedHeuristicRunnable(final ManagedProfileHeuristic heuristic,
- final UserHandleCompat user, final List apps) {
+ final UserHandle user, final List apps) {
if (heuristic != null) {
// Assume the app lists now is updated.
mIsManagedHeuristicAppsUpdated = false;
@@ -2740,7 +1685,7 @@ public class LauncherModel extends BroadcastReceiver
// list will override everything in processUserApps().
sWorker.post(new Runnable() {
public void run() {
- final List updatedApps =
+ final List updatedApps =
mLauncherApps.getActivityList(null, user);
scheduleManagedHeuristicRunnable(heuristic, user,
updatedApps);
@@ -2778,14 +1723,14 @@ public class LauncherModel extends BroadcastReceiver
return;
}
- final List profiles = mUserManager.getUserProfiles();
+ final List profiles = mUserManager.getUserProfiles();
// Clear the list of apps
mBgAllAppsList.clear();
- for (UserHandleCompat user : profiles) {
+ for (UserHandle user : profiles) {
// Query for the set of apps
final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
- final List apps = mLauncherApps.getActivityList(null, user);
+ final List apps = mLauncherApps.getActivityList(null, user);
if (DEBUG_LOADERS) {
Log.d(TAG, "getActivityList took "
+ (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
@@ -2799,9 +1744,9 @@ public class LauncherModel extends BroadcastReceiver
boolean quietMode = mUserManager.isQuietModeEnabled(user);
// Create the ApplicationInfos
for (int i = 0; i < apps.size(); i++) {
- LauncherActivityInfoCompat app = apps.get(i);
+ LauncherActivityInfo app = apps.get(i);
// This builds the icon bitmaps.
- mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, quietMode));
+ mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
}
final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user);
if (heuristic != null) {
@@ -2837,72 +1782,27 @@ public class LauncherModel extends BroadcastReceiver
}
}
- private void loadAndBindDeepShortcuts() {
- if (DEBUG_LOADERS) {
- Log.d(TAG, "loadAndBindDeepShortcuts mDeepShortcutsLoaded=" + mDeepShortcutsLoaded);
- }
- if (!mDeepShortcutsLoaded) {
- mBgDeepShortcutMap.clear();
- mHasShortcutHostPermission = mDeepShortcutManager.hasHostPermission();
+ private void loadDeepShortcuts() {
+ if (!mModelLoaded) {
+ sBgDataModel.deepShortcutMap.clear();
+ DeepShortcutManager shortcutManager = DeepShortcutManager.getInstance(mContext);
+ mHasShortcutHostPermission = shortcutManager.hasHostPermission();
if (mHasShortcutHostPermission) {
- for (UserHandleCompat user : mUserManager.getUserProfiles()) {
+ for (UserHandle user : mUserManager.getUserProfiles()) {
if (mUserManager.isUserUnlocked(user)) {
- List shortcuts = mDeepShortcutManager
- .queryForAllShortcuts(user);
- updateDeepShortcutMap(null, user, shortcuts);
+ List shortcuts =
+ shortcutManager.queryForAllShortcuts(user);
+ sBgDataModel.updateDeepShortcutMap(null, user, shortcuts);
}
}
}
- synchronized (LoaderTask.this) {
- if (mStopped) {
- return;
- }
- mDeepShortcutsLoaded = true;
- }
- }
- bindDeepShortcuts();
- }
-
- public void dumpState() {
- synchronized (sBgLock) {
- Log.d(TAG, "mLoaderTask.mContext=" + mContext);
- Log.d(TAG, "mLoaderTask.mStopped=" + mStopped);
- Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
- Log.d(TAG, "mItems size=" + sBgWorkspaceItems.size());
- }
- }
- }
-
- /**
- * Clear all the shortcuts for the given package, and re-add the new shortcuts.
- */
- private void updateDeepShortcutMap(
- String packageName, UserHandleCompat user, List shortcuts) {
- if (packageName != null) {
- Iterator keysIter = mBgDeepShortcutMap.keySet().iterator();
- while (keysIter.hasNext()) {
- ComponentKey next = keysIter.next();
- if (next.componentName.getPackageName().equals(packageName)
- && next.user.equals(user)) {
- keysIter.remove();
- }
- }
- }
-
- // Now add the new shortcuts to the map.
- for (ShortcutInfoCompat shortcut : shortcuts) {
- boolean shouldShowInContainer = shortcut.isEnabled()
- && (shortcut.isDeclaredInManifest() || shortcut.isDynamic());
- if (shouldShowInContainer) {
- ComponentKey targetComponent
- = new ComponentKey(shortcut.getActivity(), shortcut.getUserHandle());
- mBgDeepShortcutMap.addToList(targetComponent, shortcut.getId());
}
}
}
public void bindDeepShortcuts() {
- final MultiHashMap shortcutMapCopy = mBgDeepShortcutMap.clone();
+ final MultiHashMap shortcutMapCopy =
+ sBgDataModel.deepShortcutMap.clone();
Runnable r = new Runnable() {
@Override
public void run() {
@@ -2921,7 +1821,7 @@ public class LauncherModel extends BroadcastReceiver
* use partial updates similar to {@link UserManagerCompat}
*/
public void refreshShortcutsIfRequired() {
- if (Utilities.isNycMR1OrAbove()) {
+ if (Utilities.ATLEAST_NOUGAT_MR1) {
sWorker.removeCallbacks(mShortcutPermissionCheckRunnable);
sWorker.post(mShortcutPermissionCheckRunnable);
}
@@ -2930,934 +1830,155 @@ public class LauncherModel extends BroadcastReceiver
/**
* Called when the icons for packages have been updated in the icon cache.
*/
- public void onPackageIconsUpdated(HashSet updatedPackages, UserHandleCompat user) {
- final Callbacks callbacks = getCallback();
- final ArrayList updatedApps = new ArrayList<>();
- final ArrayList updatedShortcuts = new ArrayList<>();
-
+ public void onPackageIconsUpdated(HashSet updatedPackages, UserHandle user) {
// If any package icon has changed (app was updated while launcher was dead),
// update the corresponding shortcuts.
- synchronized (sBgLock) {
- for (ItemInfo info : sBgItemsIdMap) {
- if (info instanceof ShortcutInfo && user.equals(info.user)
- && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
- ShortcutInfo si = (ShortcutInfo) info;
- ComponentName cn = si.getTargetComponent();
- if (cn != null && updatedPackages.contains(cn.getPackageName())) {
- si.updateIcon(mIconCache);
- updatedShortcuts.add(si);
- }
- }
- }
- mBgAllAppsList.updateIconsAndLabels(updatedPackages, user, updatedApps);
- }
-
- bindUpdatedShortcuts(updatedShortcuts, user);
-
- if (!updatedApps.isEmpty()) {
- mHandler.post(new Runnable() {
-
- public void run() {
- Callbacks cb = getCallback();
- if (cb != null && callbacks == cb) {
- cb.bindAppsUpdated(updatedApps);
- }
- }
- });
- }
+ enqueueModelUpdateTask(new CacheDataUpdatedTask(
+ CacheDataUpdatedTask.OP_CACHE_UPDATE, user, updatedPackages));
}
- private void bindUpdatedShortcuts(
- ArrayList updatedShortcuts, UserHandleCompat user) {
- bindUpdatedShortcuts(updatedShortcuts, new ArrayList(), user);
- }
-
- private void bindUpdatedShortcuts(
- final ArrayList updatedShortcuts,
- final ArrayList removedShortcuts,
- final UserHandleCompat user) {
- if (!updatedShortcuts.isEmpty() || !removedShortcuts.isEmpty()) {
- final Callbacks callbacks = getCallback();
- mHandler.post(new Runnable() {
-
- public void run() {
- Callbacks cb = getCallback();
- if (cb != null && callbacks == cb) {
- cb.bindShortcutsChanged(updatedShortcuts, removedShortcuts, user);
- }
- }
- });
- }
- }
-
- void enqueueItemUpdatedTask(Runnable task) {
- sWorker.post(task);
- }
-
- @Thunk class AppsAvailabilityCheck extends BroadcastReceiver {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- synchronized (sBgLock) {
- final LauncherAppsCompat launcherApps = LauncherAppsCompat
- .getInstance(mApp.getContext());
- final PackageManager manager = context.getPackageManager();
- final ArrayList packagesRemoved = new ArrayList();
- final ArrayList packagesUnavailable = new ArrayList();
- for (Entry> entry : sPendingPackages.entrySet()) {
- UserHandleCompat user = entry.getKey();
- packagesRemoved.clear();
- packagesUnavailable.clear();
- for (String pkg : entry.getValue()) {
- if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
- if (PackageManagerHelper.isAppOnSdcard(manager, pkg)) {
- packagesUnavailable.add(pkg);
- } else {
- packagesRemoved.add(pkg);
- }
- }
- }
- if (!packagesRemoved.isEmpty()) {
- enqueueItemUpdatedTask(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE,
- packagesRemoved.toArray(new String[packagesRemoved.size()]), user));
- }
- if (!packagesUnavailable.isEmpty()) {
- enqueueItemUpdatedTask(new PackageUpdatedTask(PackageUpdatedTask.OP_UNAVAILABLE,
- packagesUnavailable.toArray(new String[packagesUnavailable.size()]), user));
- }
- }
- sPendingPackages.clear();
- }
- }
- }
-
- private class PackageUpdatedTask implements Runnable {
- int mOp;
- String[] mPackages;
- UserHandleCompat mUser;
-
- public static final int OP_NONE = 0;
- public static final int OP_ADD = 1;
- public static final int OP_UPDATE = 2;
- public static final int OP_REMOVE = 3; // uninstlled
- public static final int OP_UNAVAILABLE = 4; // external media unmounted
- public static final int OP_SUSPEND = 5; // package suspended
- public static final int OP_UNSUSPEND = 6; // package unsuspended
- public static final int OP_USER_AVAILABILITY_CHANGE = 7; // user available/unavailable
-
- public PackageUpdatedTask(int op, String[] packages, UserHandleCompat user) {
- mOp = op;
- mPackages = packages;
- mUser = user;
- }
-
- public void run() {
- if (!mHasLoaderCompletedOnce) {
- // Loader has not yet run.
- return;
- }
- mIsManagedHeuristicAppsUpdated = true;
- final Context context = mApp.getContext();
-
- final String[] packages = mPackages;
- final int N = packages.length;
- FlagOp flagOp = FlagOp.NO_OP;
- StringFilter pkgFilter = StringFilter.of(new HashSet<>(Arrays.asList(packages)));
- switch (mOp) {
- case OP_ADD: {
- for (int i=0; i added = null;
- ArrayList