summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/launcher3/AppFilter.java5
-rw-r--r--src/com/android/launcher3/AppWidgetsRestoredReceiver.java3
-rw-r--r--src/com/android/launcher3/BaseRecyclerView.java16
-rw-r--r--src/com/android/launcher3/BubbleTextView.java25
-rw-r--r--src/com/android/launcher3/CellLayout.java2
-rw-r--r--src/com/android/launcher3/DeviceProfile.java8
-rw-r--r--src/com/android/launcher3/FastBitmapDrawable.java2
-rw-r--r--src/com/android/launcher3/Launcher.java37
-rw-r--r--src/com/android/launcher3/LauncherAppState.java4
-rw-r--r--src/com/android/launcher3/LauncherModel.java880
-rw-r--r--src/com/android/launcher3/PinchAnimationManager.java9
-rw-r--r--src/com/android/launcher3/Utilities.java8
-rw-r--r--src/com/android/launcher3/Workspace.java161
-rw-r--r--src/com/android/launcher3/WorkspaceStateTransitionAnimation.java21
-rw-r--r--src/com/android/launcher3/allapps/AllAppsContainerView.java15
-rw-r--r--src/com/android/launcher3/allapps/AllAppsGridAdapter.java146
-rw-r--r--src/com/android/launcher3/allapps/AllAppsRecyclerView.java4
-rw-r--r--src/com/android/launcher3/allapps/AllAppsTransitionController.java6
-rw-r--r--src/com/android/launcher3/anim/AnimationLayerSet.java9
-rw-r--r--src/com/android/launcher3/anim/SpringAnimationHandler.java181
-rw-r--r--src/com/android/launcher3/badge/BadgeRenderer.java14
-rw-r--r--src/com/android/launcher3/compat/LauncherAppsCompat.java4
-rw-r--r--src/com/android/launcher3/compat/WallpaperManagerCompatVL.java103
-rw-r--r--src/com/android/launcher3/dragndrop/DragLayer.java16
-rw-r--r--src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java535
-rw-r--r--src/com/android/launcher3/dynamicui/ColorExtractionService.java105
-rw-r--r--src/com/android/launcher3/dynamicui/ExtractionUtils.java10
-rw-r--r--src/com/android/launcher3/dynamicui/WallpaperColorInfo.java9
-rw-r--r--src/com/android/launcher3/folder/FolderIcon.java4
-rw-r--r--src/com/android/launcher3/graphics/IconPalette.java84
-rw-r--r--src/com/android/launcher3/graphics/IconShapeOverride.java14
-rw-r--r--src/com/android/launcher3/graphics/LauncherIcons.java5
-rw-r--r--src/com/android/launcher3/graphics/ShadowDrawable.java33
-rw-r--r--src/com/android/launcher3/graphics/ShadowGenerator.java4
-rw-r--r--src/com/android/launcher3/graphics/TintedDrawableSpan.java1
-rw-r--r--src/com/android/launcher3/model/BgDataModel.java10
-rw-r--r--src/com/android/launcher3/model/ExtendedModelTask.java11
-rw-r--r--src/com/android/launcher3/model/LoaderResults.java17
-rw-r--r--src/com/android/launcher3/model/LoaderTask.java839
-rw-r--r--src/com/android/launcher3/model/PackageUpdatedTask.java7
-rw-r--r--src/com/android/launcher3/model/SdCardAvailableReceiver.java7
-rw-r--r--src/com/android/launcher3/model/WidgetsModel.java41
-rw-r--r--src/com/android/launcher3/notification/NotificationFooterLayout.java1
-rw-r--r--src/com/android/launcher3/notification/NotificationItemView.java3
-rw-r--r--src/com/android/launcher3/notification/NotificationMainView.java6
-rw-r--r--src/com/android/launcher3/pageindicators/CaretDrawable.java9
-rw-r--r--src/com/android/launcher3/popup/PopupContainerWithArrow.java23
-rw-r--r--src/com/android/launcher3/provider/ImportDataTask.java7
-rw-r--r--src/com/android/launcher3/qsb/QsbBlockerView.java93
-rw-r--r--src/com/android/launcher3/shortcuts/ShortcutCache.java12
-rw-r--r--src/com/android/launcher3/util/MultiStateAlphaController.java119
-rw-r--r--src/com/android/launcher3/util/Themes.java8
52 files changed, 2083 insertions, 1613 deletions
diff --git a/src/com/android/launcher3/AppFilter.java b/src/com/android/launcher3/AppFilter.java
index db8f5dd0e..923835a67 100644
--- a/src/com/android/launcher3/AppFilter.java
+++ b/src/com/android/launcher3/AppFilter.java
@@ -1,9 +1,14 @@
package com.android.launcher3;
import android.content.ComponentName;
+import android.content.Context;
public class AppFilter {
+ public static AppFilter newInstance(Context context) {
+ return Utilities.getOverrideObject(AppFilter.class, context, R.string.app_filter_class);
+ }
+
public boolean shouldShowApp(ComponentName app) {
return true;
}
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index 84a8bce6e..70be7dae4 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -12,6 +12,7 @@ import android.os.Handler;
import android.util.Log;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.util.ContentWriter;
public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
@@ -52,7 +53,7 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]);
final int state;
- if (LauncherModel.isValidProvider(provider)) {
+ if (LoaderTask.isValidProvider(provider)) {
// This will ensure that we show 'Click to setup' UI if required.
state = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
} else {
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index 1e6d89485..514cc0751 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -48,6 +48,8 @@ public abstract class BaseRecyclerView extends RecyclerView
private int mDownY;
private int mLastY;
+ private boolean mScrollBarVisible = true;
+
public BaseRecyclerView(Context context) {
this(context, null);
}
@@ -199,8 +201,18 @@ public abstract class BaseRecyclerView extends RecyclerView
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
- onUpdateScrollbar(0);
- mScrollbar.draw(canvas);
+ if (mScrollBarVisible) {
+ onUpdateScrollbar(0);
+ mScrollbar.draw(canvas);
+ }
+ }
+
+ /**
+ * Sets the scrollbar visibility. The call does not refresh the UI, its the responsibility
+ * of the caller to call {@link #invalidate()}.
+ */
+ public void setScrollBarVisible(boolean visible) {
+ mScrollBarVisible = visible;
}
/**
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 1f7eba812..27e190e5d 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -65,8 +65,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
private static final float AMBIENT_SHADOW_RADIUS = 2.5f;
private static final float KEY_SHADOW_RADIUS = 1f;
private static final float KEY_SHADOW_OFFSET = 0.5f;
- private static final int AMBIENT_SHADOW_COLOR = 0x33000000;
- private static final int KEY_SHADOW_COLOR = 0x66000000;
private static final int DISPLAY_WORKSPACE = 0;
private static final int DISPLAY_ALL_APPS = 1;
@@ -82,6 +80,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
private final CheckLongPressHelper mLongPressHelper;
private final HolographicOutlineHelper mOutlineHelper;
private final StylusEventHelper mStylusEventHelper;
+ private final int mAmbientShadowColor;
+ private final int mKeyShadowColor;
private boolean mBackgroundSizeChanged;
@@ -99,7 +99,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
private BadgeInfo mBadgeInfo;
private BadgeRenderer mBadgeRenderer;
- private IconPalette mIconPalette;
+ private IconPalette mBadgePalette;
private float mBadgeScale;
private boolean mForceHideBadge;
private Point mTempSpaceForBadgeOffset = new Point();
@@ -147,6 +147,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
mDeferShadowGenerationOnTouch =
a.getBoolean(R.styleable.BubbleTextView_deferShadowGeneration, false);
+ mAmbientShadowColor = a.getColor(R.styleable.BubbleTextView_ambientShadowColor, 0x33000000);
+ mKeyShadowColor = a.getColor(R.styleable.BubbleTextView_keyShadowColor, 0x66000000);
int display = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
int defaultIconSize = grid.iconSizePx;
@@ -174,7 +176,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
// Set shadow layer as the larger shadow to that the textView does not clip the shadow.
float density = getResources().getDisplayMetrics().density;
- setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, AMBIENT_SHADOW_COLOR);
+ setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, mAmbientShadowColor);
} else {
mBackground = null;
}
@@ -435,14 +437,14 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
// We enhance the shadow by drawing the shadow twice
float density = getResources().getDisplayMetrics().density;
- getPaint().setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, AMBIENT_SHADOW_COLOR);
+ getPaint().setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, mAmbientShadowColor);
super.draw(canvas);
canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
getScrollX() + getWidth(),
getScrollY() + getHeight(), Region.Op.INTERSECT);
getPaint().setShadowLayer(
- density * KEY_SHADOW_RADIUS, 0.0f, density * KEY_SHADOW_OFFSET, KEY_SHADOW_COLOR);
+ density * KEY_SHADOW_RADIUS, 0.0f, density * KEY_SHADOW_OFFSET, mKeyShadowColor);
super.draw(canvas);
canvas.restore();
@@ -460,7 +462,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
final int scrollX = getScrollX();
final int scrollY = getScrollY();
canvas.translate(scrollX, scrollY);
- mBadgeRenderer.draw(canvas, mBadgeInfo, mTempIconBounds, mBadgeScale,
+ mBadgeRenderer.draw(canvas, mBadgePalette, mBadgeInfo, mTempIconBounds, mBadgeScale,
mTempSpaceForBadgeOffset);
canvas.translate(-scrollX, -scrollY);
}
@@ -594,7 +596,10 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
float newBadgeScale = isBadged ? 1f : 0;
mBadgeRenderer = mLauncher.getDeviceProfile().mBadgeRenderer;
if (wasBadged || isBadged) {
- mIconPalette = ((FastBitmapDrawable) mIcon).getIconPalette();
+ mBadgePalette = IconPalette.getBadgePalette(getResources());
+ if (mBadgePalette == null) {
+ mBadgePalette = ((FastBitmapDrawable) mIcon).getIconPalette();
+ }
// Animate when a badge is first added or when it is removed.
if (animate && (wasBadged ^ isBadged) && isShown()) {
ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, newBadgeScale).start();
@@ -606,6 +611,10 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
}
}
+ public IconPalette getBadgePalette() {
+ return mBadgePalette;
+ }
+
/**
* Sets the icon for this view based on the layout direction.
*/
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 78030ce2a..c2c5c27db 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -236,7 +236,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
for (int i = 0; i < mDragOutlines.length; i++) {
mDragOutlines[i] = new Rect(-1, -1, -1, -1);
}
- mDragOutlinePaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
+ mDragOutlinePaint.setColor(Themes.getAttrColor(context, R.attr.workspaceTextColor));
// When dragging things around the home screens, we show a green outline of
// where the item will land. The outlines gradually fade out, leaving a trail
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 508fc3406..9bb56d603 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -532,14 +532,6 @@ public class DeviceProfile {
workspacePadding.bottom);
workspace.setPageSpacing(getWorkspacePageSpacing());
- // Only display when enabled
- if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
- View qsbContainer = launcher.getQsbContainer();
- lp = (FrameLayout.LayoutParams) qsbContainer.getLayoutParams();
- lp.topMargin = mInsets.top + workspacePadding.top;
- qsbContainer.setLayoutParams(lp);
- }
-
// Layout the hotseat
Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat);
lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams();
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index a096a1ddf..199baaf58 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -124,7 +124,7 @@ public class FastBitmapDrawable extends Drawable {
public IconPalette getIconPalette() {
if (mIconPalette == null) {
mIconPalette = IconPalette.fromDominantColor(Utilities
- .findDominantColorByHue(mBitmap, 20));
+ .findDominantColorByHue(mBitmap, 20), true /* desaturateBackground */);
}
return mIconPalette;
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0075887ee..dd0d2b804 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -127,6 +127,7 @@ 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.Themes;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -224,7 +225,6 @@ public class Launcher extends BaseActivity
private View mLauncherView;
@Thunk DragLayer mDragLayer;
private DragController mDragController;
- private View mQsbContainer;
public View mWeightWatcher;
@@ -272,7 +272,6 @@ public class Launcher extends BaseActivity
private Handler mHandler = new Handler();
private boolean mIsResumeFromActionScreenOff;
private boolean mHasFocus = false;
- private boolean mAttached = false;
private ObjectAnimator mScrimAnimator;
@@ -369,7 +368,7 @@ public class Launcher extends BaseActivity
WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
wallpaperColorInfo.setOnThemeChangeListener(this);
- overrideTheme(wallpaperColorInfo.isDark());
+ overrideTheme(wallpaperColorInfo.isDark(), wallpaperColorInfo.supportsDarkText());
super.onCreate(savedInstanceState);
@@ -467,6 +466,13 @@ public class Launcher extends BaseActivity
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
+
+ // Listen for broadcasts screen off
+ registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+
+ if (Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText)) {
+ activateLightSystemBars(true, true, true);
+ }
}
@Override
@@ -474,9 +480,11 @@ public class Launcher extends BaseActivity
recreate();
}
- protected void overrideTheme(boolean isDark) {
+ protected void overrideTheme(boolean isDark, boolean supportsDarkText) {
if (isDark) {
setTheme(R.style.LauncherThemeDark);
+ } else if (supportsDarkText) {
+ setTheme(R.style.LauncherThemeDarkText);
}
}
@@ -1278,9 +1286,7 @@ public class Launcher extends BaseActivity
private void setupViews() {
mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
mFocusHandler = mDragLayer.getFocusIndicatorHelper();
- mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
- mQsbContainer = mDragLayer.findViewById(mDeviceProfile.isVerticalBarLayout()
- ? R.id.workspace_blocked_row : R.id.qsb_container);
+ mWorkspace = mDragLayer.findViewById(R.id.workspace);
mWorkspace.initParentViews(mDragLayer);
mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
@@ -1587,13 +1593,7 @@ public class Launcher extends BaseActivity
public void onAttachedToWindow() {
super.onAttachedToWindow();
- // Listen for broadcasts related to user-presence
- final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- registerReceiver(mReceiver, filter);
FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
- mAttached = true;
-
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onAttachedToWindow();
}
@@ -1602,10 +1602,6 @@ public class Launcher extends BaseActivity
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
- if (mAttached) {
- unregisterReceiver(mReceiver);
- mAttached = false;
- }
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onDetachedFromWindow();
@@ -1667,10 +1663,6 @@ public class Launcher extends BaseActivity
return mWorkspace;
}
- public View getQsbContainer() {
- return mQsbContainer;
- }
-
public Hotseat getHotseat() {
return mHotseat;
}
@@ -1846,6 +1838,7 @@ public class Launcher extends BaseActivity
public void onDestroy() {
super.onDestroy();
+ unregisterReceiver(mReceiver);
mWorkspace.removeCallbacks(mBuildLayersRunnable);
mWorkspace.removeFolderListeners();
@@ -3970,7 +3963,7 @@ public class Launcher extends BaseActivity
* refreshes the widgets and shortcuts associated with the given package/user
*/
public void refreshAndBindWidgetsForPackageUser(@Nullable PackageUserKey packageUser) {
- mModel.refreshAndBindWidgetsAndShortcuts(this, mWidgetsView.isEmpty(), packageUser);
+ mModel.refreshAndBindWidgetsAndShortcuts(packageUser);
}
public void lockScreenOrientation() {
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 27ccabea9..cf20febd5 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -93,9 +93,7 @@ public class LauncherAppState {
mInvariantDeviceProfile = new InvariantDeviceProfile(mContext);
mIconCache = new IconCache(mContext, mInvariantDeviceProfile);
mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
-
- mModel = new LauncherModel(this, mIconCache,
- Utilities.getOverrideObject(AppFilter.class, mContext, R.string.app_filter_class));
+ mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
LauncherAppsCompat.getInstance(mContext).addOnAppsChangedCallback(mModel);
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index ee06d9e58..48c5c562c 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -16,7 +16,6 @@
package com.android.launcher3;
-import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProviderOperation;
@@ -24,62 +23,40 @@ import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.PackageInstaller;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
-import android.os.SystemClock;
-import android.os.Trace;
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.LauncherAppsCompat;
-import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dynamicui.ExtractionUtils;
-import com.android.launcher3.folder.Folder;
-import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.folder.FolderIconPreviewVerifier;
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.LoaderResults;
+import com.android.launcher3.model.LoaderTask;
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;
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.LooperIdleLock;
-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.Provider;
@@ -90,12 +67,9 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
@@ -106,7 +80,7 @@ import java.util.concurrent.Executor;
*/
public class LauncherModel extends BroadcastReceiver
implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
- static final boolean DEBUG_LOADERS = false;
+ static final boolean DEBUG_TASKS = false;
private static final boolean DEBUG_RECEIVER = false;
static final String TAG = "Launcher.Model";
@@ -114,9 +88,9 @@ public class LauncherModel extends BroadcastReceiver
private final MainThreadExecutor mUiExecutor = new MainThreadExecutor();
@Thunk final LauncherAppState mApp;
@Thunk final Object mLock = new Object();
- @Thunk LoaderTask mLoaderTask;
+ @Thunk
+ LoaderTask mLoaderTask;
@Thunk boolean mIsLoaderTaskRunning;
- @Thunk boolean mHasLoaderCompletedOnce;
@Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
static {
@@ -139,10 +113,13 @@ public class LauncherModel extends BroadcastReceiver
// < only access in worker thread >
private final AllAppsList mBgAllAppsList;
- // Entire list of widgets.
- private final WidgetsModel mBgWidgetsModel;
- private boolean mHasShortcutHostPermission;
+ /**
+ * 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();
+
// Runnable to check if the shortcuts permission has changed.
private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
@Override
@@ -150,26 +127,13 @@ public class LauncherModel extends BroadcastReceiver
if (mModelLoaded) {
boolean hasShortcutHostPermission =
DeepShortcutManager.getInstance(mApp.getContext()).hasHostPermission();
- if (hasShortcutHostPermission != mHasShortcutHostPermission) {
+ if (hasShortcutHostPermission != sBgDataModel.hasShortcutHostPermission) {
forceReload();
}
}
}
};
- /**
- * 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 final IconCache mIconCache;
-
- private final LauncherAppsCompat mLauncherApps;
- private final UserManagerCompat mUserManager;
-
public interface Callbacks {
public boolean setLoadOnResume();
public int getCurrentWorkspaceScreen();
@@ -204,14 +168,8 @@ public class LauncherModel extends BroadcastReceiver
}
LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
- Context context = app.getContext();
mApp = app;
mBgAllAppsList = new AllAppsList(iconCache, appFilter);
- mBgWidgetsModel = new WidgetsModel(iconCache, appFilter);
- mIconCache = iconCache;
-
- mLauncherApps = LauncherAppsCompat.getInstance(context);
- mUserManager = UserManagerCompat.getInstance(context);
}
/** Runs the specified runnable immediately if called from the worker thread, otherwise it is
@@ -539,9 +497,10 @@ public class LauncherModel extends BroadcastReceiver
// issues that arise from that.
loaderResults.bindAllApps();
loaderResults.bindDeepShortcuts();
+ loaderResults.bindWidgets();
return true;
} else {
- mLoaderTask = new LoaderTask(mApp.getContext(), loaderResults);
+ mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, loaderResults);
sWorker.post(mLoaderTask);
}
}
@@ -588,763 +547,42 @@ public class LauncherModel extends BroadcastReceiver
});
}
- /**
- * Runnable for the thread that loads the contents of the launcher:
- * - workspace icons
- * - widgets
- * - all apps icons
- * - deep shortcuts within apps
- */
- private class LoaderTask implements Runnable {
- private Context mContext;
- private final LoaderResults mResults;
-
- private boolean mStopped;
-
- LoaderTask(Context context, LoaderResults results) {
- mContext = context;
- mResults = results;
- }
+ public class LoaderTransaction implements AutoCloseable {
- 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
- // down.
- synchronized (LoaderTask.this) {
- LooperIdleLock idleLock = new LooperIdleLock(this, Looper.getMainLooper());
- // Just in case mFlushingWorkerThread changes but we aren't woken up,
- // wait no longer than 1sec at a time
- while (!mStopped && idleLock.awaitLocked(1000));
- }
- }
-
- private void verifyNotStopped() throws CancellationException {
- synchronized (LoaderTask.this) {
- if (mStopped) {
- throw new CancellationException("Loader stopped");
- }
- }
- }
+ private final LoaderTask mTask;
- public void run() {
+ private LoaderTransaction(LoaderTask task) throws CancellationException {
synchronized (mLock) {
- if (mStopped) {
- return;
+ if (mLoaderTask != task) {
+ throw new CancellationException("Loader already stopped");
}
+ mTask = task;
mIsLoaderTaskRunning = true;
- }
-
- try {
- long now = 0;
- if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
- loadWorkspace();
-
- verifyNotStopped();
- if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace");
- mResults.bindWorkspace();
-
- // Take a break
- if (DEBUG_LOADERS) {
- Log.d(TAG, "step 1 completed, wait for idle");
- now = SystemClock.uptimeMillis();
- }
- waitForIdle();
- if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms");
- verifyNotStopped();
-
- // second step
- if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps");
- loadAllApps();
-
- if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Binding all apps");
- verifyNotStopped();
- mResults.bindAllApps();
-
- verifyNotStopped();
- if (DEBUG_LOADERS) Log.d(TAG, "step 2.3: Update icon cache");
- updateIconCache();
-
- // Take a break
- if (DEBUG_LOADERS) {
- Log.d(TAG, "step 2 completed, wait for idle");
- now = SystemClock.uptimeMillis();
- }
- waitForIdle();
- if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms");
- verifyNotStopped();
-
- // third step
- if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts");
- loadDeepShortcuts();
-
- verifyNotStopped();
- if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts");
- mResults.bindDeepShortcuts();
-
- // 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;
- }
+ mModelLoaded = false;
}
}
- public void stopLocked() {
- synchronized (LoaderTask.this) {
- mStopped = true;
- this.notify();
- }
- }
-
- private void loadWorkspace() {
- if (LauncherAppState.PROFILE_STARTUP) {
- Trace.beginSection("Loading Workspace");
- }
-
- final Context context = mContext;
- final ContentResolver contentResolver = context.getContentResolver();
- 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();
- final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>();
-
- boolean clearDb = false;
- try {
- ImportDataTask.performImportIfPossible(context);
- } catch (Exception e) {
- // Migration failed. Clear workspace.
- clearDb = true;
- }
-
- if (!clearDb && GridSizeMigrationTask.ENABLED &&
- !GridSizeMigrationTask.migrateGridIfNeeded(mContext)) {
- // Migration failed. Clear workspace.
- clearDb = true;
- }
-
- if (clearDb) {
- Log.d(TAG, "loadWorkspace: resetting launcher database");
- LauncherSettings.Settings.call(contentResolver,
- LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
- }
-
- Log.d(TAG, "loadWorkspace: loading default favorites");
- LauncherSettings.Settings.call(contentResolver,
- LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
-
- synchronized (sBgDataModel) {
- sBgDataModel.clear();
-
- final HashMap<String, Integer> installingPkgs = PackageInstallerCompat
- .getInstance(mContext).updateAndGetActiveSessionCache();
- sBgDataModel.workspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
-
- Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
- final LoaderCursor c = new LoaderCursor(contentResolver.query(
- LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
-
- HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
-
- try {
- final int appWidgetIdIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.APPWIDGET_ID);
- final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.APPWIDGET_PROVIDER);
- 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 optionsIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.OPTIONS);
-
- final LongSparseArray<UserHandle> allUsers = c.allUsers;
- final LongSparseArray<Boolean> quietMode = new LongSparseArray<>();
- final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
- for (UserHandle user : mUserManager.getUserProfiles()) {
- long serialNo = mUserManager.getSerialNumberForUser(user);
- allUsers.put(serialNo, user);
- quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
-
- boolean userUnlocked = mUserManager.isUserUnlocked(user);
-
- // We can only query for shortcuts when the user is unlocked.
- if (userUnlocked) {
- List<ShortcutInfoCompat> pinnedShortcuts =
- shortcutManager.queryForPinnedShortcuts(null, user);
- if (shortcutManager.wasLastCallSuccess()) {
- for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
- shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
- shortcut);
- }
- } else {
- // Shortcut manager can fail due to some race condition when the
- // lock state changes too frequently. For the purpose of the loading
- // shortcuts, consider the user is still locked.
- userUnlocked = false;
- }
- }
- unlockedUsers.put(serialNo, userUnlocked);
- }
-
- ShortcutInfo info;
- LauncherAppWidgetInfo appWidgetInfo;
- Intent intent;
- String targetPkg;
-
- FolderIconPreviewVerifier verifier =
- new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
- while (!mStopped && c.moveToNext()) {
- try {
- if (c.user == null) {
- // User has been deleted, remove the item.
- c.markDeleted("User has been deleted");
- continue;
- }
-
- 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:
- intent = c.parseIntent();
- if (intent == null) {
- c.markDeleted("Invalid or null intent");
- continue;
- }
-
- int disabledState = quietMode.get(c.serialNumber) ?
- ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0;
- ComponentName cn = intent.getComponent();
- targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
-
- 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_AUTOINSTALL_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 {
- c.markDeleted("Unable to find a launch target");
- continue;
- }
- } else {
- // The app is installed but the component is no
- // longer available.
- c.markDeleted("Invalid component removed: " + cn);
- 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;
- }
- }
-
- 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() &&
- !verifier.isItemInPreview(c.getInt(rankIndex));
-
- 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 = c.getAppShortcutInfo(
- intent, allowMissingTarget, useLowResIcon);
- } else if (c.itemType ==
- LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-
- 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.
- 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 = c.loadSimpleShortcut();
- info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
- }
- } else { // item type == ITEM_TYPE_SHORTCUT
- info = c.loadSimpleShortcut();
-
- // Shortcuts are only available on the primary profile
- if (!TextUtils.isEmpty(targetPkg)
- && pmHelper.isAppSuspended(targetPkg, c.user)) {
- disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
- }
-
- // App shortcuts that used to be automatically added to Launcher
- // didn't always have the correct intent flags set, so do that
- // here
- if (intent.getAction() != null &&
- intent.getCategories() != null &&
- intent.getAction().equals(Intent.ACTION_MAIN) &&
- intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
- intent.addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- }
- }
-
- if (info != null) {
- c.applyCommonProperties(info);
-
- info.intent = intent;
- info.rank = c.getInt(rankIndex);
- info.spanX = 1;
- info.spanY = 1;
- info.isDisabled |= disabledState;
- if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
- info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
- }
-
- 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;
- }
- }
-
- c.checkAndAddItem(info, sBgDataModel);
- } else {
- throw new RuntimeException("Unexpected null ShortcutInfo");
- }
- break;
-
- case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- 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(c.titleIndex);
- folderInfo.spanX = 1;
- folderInfo.spanY = 1;
- folderInfo.options = c.getInt(optionsIndex);
-
- // no special handling required for restored folders
- c.markRestored();
-
- 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 = c.itemType ==
- LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
-
- int appWidgetId = c.getInt(appWidgetIdIndex);
- String savedProvider = c.getString(appWidgetProviderIndex);
-
- final ComponentName component =
- ComponentName.unflattenFromString(savedProvider);
-
- 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
- .getInstance(mContext).getAllProvidersMap();
- }
- final AppWidgetProviderInfo provider = widgetProvidersMap.get(
- new ComponentKey(
- ComponentName.unflattenFromString(savedProvider),
- c.user));
-
- final boolean isProviderReady = isValidProvider(provider);
- if (!isSafeMode && !customWidget &&
- wasProviderReady && !isProviderReady) {
- c.markDeleted(
- "Deleting widget that isn't installed anymore: "
- + provider);
- } else {
- if (isProviderReady) {
- appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
- provider.provider);
-
- // 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 = c.restoreFlag &
- ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
- if (!wasProviderReady) {
- // If provider was not previously ready, update the
- // status and UI flag.
-
- // Id would be valid only if the widget restore broadcast was received.
- if (isIdValid) {
- status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
- } else {
- status &= ~LauncherAppWidgetInfo
- .FLAG_PROVIDER_NOT_READY;
- }
- }
- appWidgetInfo.restoreStatus = status;
- } else {
- Log.v(TAG, "Widget restore pending id=" + c.id
- + " appWidgetId=" + appWidgetId
- + " status =" + c.restoreFlag);
- appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
- component);
- appWidgetInfo.restoreStatus = c.restoreFlag;
- Integer installProgress = installingPkgs.get(component.getPackageName());
-
- 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) {
- c.markDeleted("Unrestored widget removed: " + component);
- continue;
- }
-
- appWidgetInfo.installProgress =
- installProgress == null ? 0 : installProgress;
- }
- if (appWidgetInfo.hasRestoreFlag(
- LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) {
- appWidgetInfo.bindOptions = c.parseIntent();
- }
-
- c.applyCommonProperties(appWidgetInfo);
- appWidgetInfo.spanX = c.getInt(spanXIndex);
- appWidgetInfo.spanY = c.getInt(spanYIndex);
- appWidgetInfo.user = c.user;
-
- if (!c.isOnWorkspaceOrHotseat()) {
- c.markDeleted("Widget found where container != " +
- "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
- continue;
- }
-
- if (!customWidget) {
- String providerName =
- appWidgetInfo.providerName.flattenToString();
- if (!providerName.equals(savedProvider) ||
- (appWidgetInfo.restoreStatus != c.restoreFlag)) {
- c.updater()
- .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
- providerName)
- .put(LauncherSettings.Favorites.RESTORED,
- appWidgetInfo.restoreStatus)
- .commit();
- }
- }
-
- if (appWidgetInfo.restoreStatus !=
- LauncherAppWidgetInfo.RESTORE_COMPLETED) {
- String pkg = appWidgetInfo.providerName.getPackageName();
- appWidgetInfo.pendingItemInfo = new PackageItemInfo(pkg);
- appWidgetInfo.pendingItemInfo.user = appWidgetInfo.user;
- mIconCache.getTitleAndIconForApp(
- appWidgetInfo.pendingItemInfo, false);
- }
-
- c.checkAndAddItem(appWidgetInfo, sBgDataModel);
- }
- break;
- }
- } catch (Exception e) {
- Log.e(TAG, "Desktop items loading interrupted", e);
- }
- }
- } finally {
- Utilities.closeSilently(c);
- }
-
- // Break early if we've stopped loading
- if (mStopped) {
- sBgDataModel.clear();
- return;
- }
-
- // Remove dead items
- if (c.commitDeleted()) {
- // Remove any empty folder
- ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings
- .call(contentResolver,
- LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
- .getSerializable(LauncherSettings.Settings.EXTRA_VALUE);
- for (long folderId : deletedFolderIds) {
- 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<ShortcutKey> pendingShortcuts =
- InstallShortcutReceiver.getPendingShortcuts(context);
- for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
- 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.
- shortcutManager.unpinShortcut(key);
- }
- }
-
- FolderIconPreviewVerifier verifier =
- new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
- // Sort the folder items and make sure all items in the preview are high resolution.
- for (FolderInfo folder : sBgDataModel.folders) {
- Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
- verifier.setFolderInfo(folder);
-
- int numItemsInPreview = 0;
- for (ShortcutInfo info : folder.contents) {
- if (info.usingLowResIcon
- && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
- && verifier.isItemInPreview(info.rank)) {
- mIconCache.getTitleAndIcon(info, false);
- numItemsInPreview++;
- }
-
- if (numItemsInPreview >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
- break;
- }
- }
- }
-
- c.commitRestoredItems();
- if (!isSdCardReady && !pendingPackages.isEmpty()) {
- context.registerReceiver(
- new SdCardAvailableReceiver(
- LauncherModel.this, mContext, pendingPackages),
- new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
- null,
- sWorker);
- }
-
- // Remove any empty screens
- ArrayList<Long> unusedScreens = new ArrayList<>(sBgDataModel.workspaceScreens);
- for (ItemInfo item: sBgDataModel.itemsIdMap) {
- long screenId = item.screenId;
- if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
- unusedScreens.contains(screenId)) {
- unusedScreens.remove(screenId);
- }
- }
-
- // If there are any empty screens remove them, and update.
- if (unusedScreens.size() != 0) {
- sBgDataModel.workspaceScreens.removeAll(unusedScreens);
- updateWorkspaceScreenOrder(context, sBgDataModel.workspaceScreens);
- }
- }
- if (LauncherAppState.PROFILE_STARTUP) {
- Trace.endSection();
- }
- }
-
- private void updateIconCache() {
- // Ignore packages which have a promise icon.
- HashSet<String> packagesToIgnore = new HashSet<>();
- synchronized (sBgDataModel) {
- for (ItemInfo info : sBgDataModel.itemsIdMap) {
- if (info instanceof ShortcutInfo) {
- ShortcutInfo si = (ShortcutInfo) info;
- if (si.isPromise() && si.getTargetComponent() != null) {
- packagesToIgnore.add(si.getTargetComponent().getPackageName());
- }
- } else if (info instanceof LauncherAppWidgetInfo) {
- LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info;
- if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
- packagesToIgnore.add(lawi.providerName.getPackageName());
- }
- }
- }
+ public void commit() {
+ synchronized (mLock) {
+ // Everything loaded bind the data.
+ mModelLoaded = true;
}
- mIconCache.updateDbIcons(packagesToIgnore);
}
- private void loadAllApps() {
- final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-
- final List<UserHandle> profiles = mUserManager.getUserProfiles();
-
- // Clear the list of apps
- mBgAllAppsList.clear();
- for (UserHandle user : profiles) {
- // Query for the set of apps
- final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
- final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
- if (DEBUG_LOADERS) {
- Log.d(TAG, "getActivityList took "
- + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
- Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
- }
- // Fail if we don't have any apps
- // TODO: Fix this. Only fail for the current user.
- if (apps == null || apps.isEmpty()) {
- return;
- }
- boolean quietMode = mUserManager.isQuietModeEnabled(user);
- // Create the ApplicationInfos
- for (int i = 0; i < apps.size(); i++) {
- LauncherActivityInfo app = apps.get(i);
- // This builds the icon bitmaps.
- mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
- }
-
- ManagedProfileHeuristic.onAllAppsLoaded(mContext, apps, user);
- }
-
- if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
- // get all active sessions and add them to the all apps list
- PackageInstallerCompat installer = PackageInstallerCompat.getInstance(mContext);
- for (PackageInstaller.SessionInfo info : installer.getAllVerifiedSessions()) {
- mBgAllAppsList.addPromiseApp(mContext,
- PackageInstallInfo.fromInstallingState(info));
+ @Override
+ public void close() {
+ synchronized (mLock) {
+ // If we are still the last one to be scheduled, remove ourselves.
+ if (mLoaderTask == mTask) {
+ mLoaderTask = null;
}
- }
-
- mBgAllAppsList.added = new ArrayList<>();
- if (DEBUG_LOADERS) {
- Log.d(TAG, "All apps loaded in in "
- + (SystemClock.uptimeMillis() - loadTime) + "ms");
+ mIsLoaderTaskRunning = false;
}
}
+ }
- private void loadDeepShortcuts() {
- sBgDataModel.deepShortcutMap.clear();
- DeepShortcutManager shortcutManager = DeepShortcutManager.getInstance(mContext);
- mHasShortcutHostPermission = shortcutManager.hasHostPermission();
- if (mHasShortcutHostPermission) {
- for (UserHandle user : mUserManager.getUserProfiles()) {
- if (mUserManager.isUserUnlocked(user)) {
- List<ShortcutInfoCompat> shortcuts =
- shortcutManager.queryForAllShortcuts(user);
- sBgDataModel.updateDeepShortcutMap(null, user, shortcuts);
- }
- }
- }
- }
+ public LoaderTransaction beginLoader(LoaderTask task) throws CancellationException {
+ return new LoaderTransaction(task);
}
/**
@@ -1370,12 +608,6 @@ public class LauncherModel extends BroadcastReceiver
}
public void enqueueModelUpdateTask(BaseModelUpdateTask task) {
- if (!mModelLoaded && mLoaderTask == null) {
- if (DEBUG_LOADERS) {
- Log.d(TAG, "enqueueModelUpdateTask Ignoring task since loader is pending=" + task);
- }
- return;
- }
task.init(this);
runOnWorkerThread(task);
}
@@ -1404,8 +636,11 @@ public class LauncherModel extends BroadcastReceiver
}
@Override
- public void run() {
- if (!mModel.mHasLoaderCompletedOnce) {
+ public final void run() {
+ if (!mModel.mModelLoaded) {
+ if (DEBUG_TASKS) {
+ Log.d(TAG, "Ignoring model task since loader is pending=" + this);
+ }
// Loader has not yet run.
return;
}
@@ -1465,43 +700,16 @@ public class LauncherModel extends BroadcastReceiver
});
}
- private void bindWidgetsModel(final Callbacks callbacks) {
- final MultiHashMap<PackageItemInfo, WidgetItem> widgets
- = mBgWidgetsModel.getWidgetsMap().clone();
- mUiExecutor.execute(new Runnable() {
- @Override
- public void run() {
- Callbacks cb = getCallback();
- if (callbacks == cb && cb != null) {
- callbacks.bindAllWidgets(widgets);
- }
- }
- });
- }
-
- public void refreshAndBindWidgetsAndShortcuts(final Callbacks callbacks,
- final boolean bindFirst, @Nullable final PackageUserKey packageUser) {
- runOnWorkerThread(new Runnable() {
+ public void refreshAndBindWidgetsAndShortcuts(@Nullable final PackageUserKey packageUser) {
+ enqueueModelUpdateTask(new ExtendedModelTask() {
@Override
- public void run() {
- if (bindFirst && !mBgWidgetsModel.isEmpty()) {
- bindWidgetsModel(callbacks);
- }
- ArrayList<WidgetItem> widgets = mBgWidgetsModel.update(
- mApp.getContext(), packageUser);
- bindWidgetsModel(callbacks);
-
- // update the Widget entries inside DB on the worker thread.
- mApp.getWidgetCache().removeObsoletePreviews(widgets, packageUser);
+ public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ dataModel.widgetsModel.update(app, packageUser);
+ bindUpdatedWidgets(dataModel);
}
});
}
- static boolean isValidProvider(AppWidgetProviderInfo provider) {
- return (provider != null) && (provider.provider != null)
- && (provider.provider.getPackageName() != null);
- }
-
public void dumpState(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
writer.println(prefix + "All apps list: size=" + mBgAllAppsList.data.size());
diff --git a/src/com/android/launcher3/PinchAnimationManager.java b/src/com/android/launcher3/PinchAnimationManager.java
index f8196e5f6..c3d3bb3df 100644
--- a/src/com/android/launcher3/PinchAnimationManager.java
+++ b/src/com/android/launcher3/PinchAnimationManager.java
@@ -56,11 +56,10 @@ public class PinchAnimationManager {
private static final LinearInterpolator INTERPOLATOR = new LinearInterpolator();
private static final int INDEX_HOTSEAT = 0;
- private static final int INDEX_QSB = 1;
- private static final int INDEX_OVERVIEW_PANEL_BUTTONS = 2;
- private static final int INDEX_SCRIM = 3;
+ private static final int INDEX_OVERVIEW_PANEL_BUTTONS = 1;
+ private static final int INDEX_SCRIM = 2;
- private final Animator[] mAnimators = new Animator[4];
+ private final Animator[] mAnimators = new Animator[3];
private Launcher mLauncher;
private Workspace mWorkspace;
@@ -196,8 +195,6 @@ public class PinchAnimationManager {
private void animateHotseatAndQsb(boolean show) {
startAnimator(INDEX_HOTSEAT,
mWorkspace.createHotseatAlphaAnimator(show ? 1 : 0), THRESHOLD_ANIM_DURATION);
- startAnimator(INDEX_QSB, mWorkspace.mQsbAlphaController.animateAlphaAtIndex(
- show ? 1 : 0, Workspace.QSB_ALPHA_INDEX_STATE_CHANGE), THRESHOLD_ANIM_DURATION);
}
private void animateOverviewPanelButtons(boolean show) {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 61c970789..c2987f881 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -110,6 +110,9 @@ public final class Utilities {
// An intent extra to indicate the horizontal scroll of the wallpaper.
public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
+ public static final int COLOR_EXTRACTION_JOB_ID = 1;
+ public static final int WALLPAPER_COMPAT_JOB_ID = 2;
+
// These values are same as that in {@link AsyncTask}.
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
@@ -549,6 +552,11 @@ public final class Utilities {
LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE);
}
+ public static SharedPreferences getDevicePrefs(Context context) {
+ return context.getSharedPreferences(
+ LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
+ }
+
public static boolean isPowerSaverOn(Context context) {
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
return powerManager.isPowerSaveMode();
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index ea4a662d3..3fe78757b 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -53,7 +53,6 @@ import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.TextView;
import android.widget.Toast;
-
import com.android.launcher3.Launcher.CustomContentCallbacks;
import com.android.launcher3.Launcher.LauncherOverlay;
import com.android.launcher3.UninstallDropTarget.DropTargetSource;
@@ -80,14 +79,12 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LongArrayMap;
-import com.android.launcher3.util.MultiStateAlphaController;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.VerticalFlingDetector;
import com.android.launcher3.util.WallpaperOffsetInterpolator;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
-
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
@@ -111,7 +108,7 @@ public class Workspace extends PagedView
* {@link #isFinishedSwitchingState()} ()} to return true. */
private static final float FINISHED_SWITCHING_STATE_TRANSITION_PROGRESS = 0.5f;
- private static boolean ENFORCE_DRAG_EVENT_ORDER = false;
+ private static final boolean ENFORCE_DRAG_EVENT_ORDER = false;
private static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
private static final int FADE_EMPTY_SCREEN_DURATION = 150;
@@ -137,8 +134,8 @@ public class Workspace extends PagedView
private ShortcutAndWidgetContainer mDragSourceInternal;
- @Thunk LongArrayMap<CellLayout> mWorkspaceScreens = new LongArrayMap<>();
- @Thunk ArrayList<Long> mScreenOrder = new ArrayList<Long>();
+ @Thunk final LongArrayMap<CellLayout> mWorkspaceScreens = new LongArrayMap<>();
+ @Thunk final ArrayList<Long> mScreenOrder = new ArrayList<>();
@Thunk Runnable mRemoveEmptyScreenRunnable;
@Thunk boolean mDeferRemoveExtraEmptyScreen = false;
@@ -174,7 +171,7 @@ public class Workspace extends PagedView
*/
private CellLayout mDropToLayout = null;
- @Thunk Launcher mLauncher;
+ @Thunk final Launcher mLauncher;
@Thunk DragController mDragController;
// These are temporary variables to prevent having to allocate a new object just to
@@ -183,10 +180,10 @@ public class Workspace extends PagedView
private final int[] mTempXY = new int[2];
@Thunk float[] mDragViewVisualCenter = new float[2];
- private float[] mTempTouchCoordinates = new float[2];
+ private final float[] mTempTouchCoordinates = new float[2];
private SpringLoadedDragController mSpringLoadedDragController;
- private float mOverviewModeShrinkFactor;
+ private final float mOverviewModeShrinkFactor;
// State variable that indicates whether the pages are small (ie when you're
// in all apps or customize mode)
@@ -226,21 +223,13 @@ public class Workspace extends PagedView
/**
* These values correspond to {@link Direction#X} & {@link Direction#Y}
*/
- private float[] mPageAlpha = new float[] {1, 1};
+ private final float[] mPageAlpha = new float[] {1, 1};
/**
* Hotseat alpha can be changed when moving horizontally, vertically, changing states.
* The values correspond to {@link Direction#X}, {@link Direction#Y} &
* {@link #HOTSEAT_STATE_ALPHA_INDEX} respectively.
*/
- private float[] mHotseatAlpha = new float[] {1, 1, 1};
-
- public static final int QSB_ALPHA_INDEX_STATE_CHANGE = 0;
- public static final int QSB_ALPHA_INDEX_Y_TRANSLATION = 1;
- public static final int QSB_ALPHA_INDEX_PAGE_SCROLL = 2;
- public static final int QSB_ALPHA_INDEX_OVERLAY_SCROLL = 3;
-
-
- MultiStateAlphaController mQsbAlphaController;
+ private final float[] mHotseatAlpha = new float[] {1, 1, 1};
@ViewDebug.ExportedProperty(category = "launcher")
private State mState = State.NORMAL;
@@ -252,7 +241,7 @@ public class Workspace extends PagedView
private boolean mStripScreensOnPageStopMoving = false;
private DragPreviewProvider mOutlineProvider = null;
- private boolean mWorkspaceFadeInAdjacentScreens;
+ private final boolean mWorkspaceFadeInAdjacentScreens;
final WallpaperOffsetInterpolator mWallpaperOffset;
private boolean mUnlockWallpaperFromDefaultPageOnLayout;
@@ -297,7 +286,7 @@ public class Workspace extends PagedView
@Thunk int mLastReorderY = -1;
private SparseArray<Parcelable> mSavedStates;
- private final ArrayList<Integer> mRestoredPages = new ArrayList<Integer>();
+ private final ArrayList<Integer> mRestoredPages = new ArrayList<>();
private float mCurrentScale;
private float mTransitionProgress;
@@ -320,10 +309,9 @@ public class Workspace extends PagedView
private boolean mIgnoreQsbScroll;
// Handles workspace state transitions
- private WorkspaceStateTransitionAnimation mStateTransitionAnimation;
+ private final WorkspaceStateTransitionAnimation mStateTransitionAnimation;
private AccessibilityDelegate mPagesAccessibilityDelegate;
- private OnStateChangeListener mOnStateChangeListener;
/**
* Used to inflate the Workspace from XML.
@@ -378,10 +366,6 @@ public class Workspace extends PagedView
}
}
- public void setOnStateChangeListener(OnStateChangeListener listener) {
- mOnStateChangeListener = listener;
- }
-
/**
* Estimates the size of an item using spans: hSpan, vSpan.
*
@@ -536,7 +520,6 @@ public class Workspace extends PagedView
public void initParentViews(View parent) {
super.initParentViews(parent);
mPageIndicator.setAccessibilityDelegate(new OverviewAccessibilityDelegate());
- mQsbAlphaController = new MultiStateAlphaController(mLauncher.getQsbContainer(), 4);
}
private int getDefaultPage() {
@@ -576,11 +559,6 @@ public class Workspace extends PagedView
return mTouchState != TOUCH_STATE_REST;
}
- private int getEmbeddedQsbId() {
- return mLauncher.getDeviceProfile().isVerticalBarLayout()
- ? R.id.qsb_container : R.id.workspace_blocked_row;
- }
-
/**
* Initializes and binds the first page
* @param qsb an existing qsb to recycle or null.
@@ -622,41 +600,17 @@ public class Workspace extends PagedView
if (qsb == null) {
// In transposed layout, we add the QSB in the Grid. As workspace does not touch the
// edges, we do not need a full width QSB.
- qsb = LayoutInflater.from(getContext()).inflate(
- mLauncher.getDeviceProfile().isVerticalBarLayout()
- ? R.layout.qsb_container : R.layout.qsb_blocker_view,
- firstPage, false);
+ qsb = LayoutInflater.from(getContext())
+ .inflate(R.layout.qsb_container,firstPage, false);
}
CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(), 1);
lp.canReorder = false;
- if (!firstPage.addViewToCellLayout(qsb, 0, getEmbeddedQsbId(), lp, true)) {
+ if (!firstPage.addViewToCellLayout(qsb, 0, R.id.qsb_container, lp, true)) {
Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
}
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- // Update the QSB to match the cell height. This is treating the QSB essentially as a child
- // of workspace despite that it's not a true child.
- // Note that it relies on the strict ordering of measuring the workspace before the QSB
- // at the dragLayer level.
- // Only measure the QSB when the view is enabled
- if (FeatureFlags.QSB_ON_FIRST_SCREEN && getChildCount() > 0) {
- CellLayout firstPage = (CellLayout) getChildAt(0);
- int cellHeight = firstPage.getCellHeight();
-
- View qsbContainer = mLauncher.getQsbContainer();
- ViewGroup.LayoutParams lp = qsbContainer.getLayoutParams();
- if (cellHeight > 0 && lp.height != cellHeight) {
- lp.height = cellHeight;
- qsbContainer.setLayoutParams(lp);
- }
- }
- }
-
public void removeAllWorkspaceScreens() {
// Disable all layout transitions before removing all pages to ensure that we don't get the
// transition animations competing with us changing the scroll when we add pages or the
@@ -670,7 +624,7 @@ public class Workspace extends PagedView
}
// Recycle the QSB widget
- View qsb = findViewById(getEmbeddedQsbId());
+ View qsb = findViewById(R.id.qsb_container);
if (qsb != null) {
((ViewGroup) qsb.getParent()).removeView(qsb);
}
@@ -853,7 +807,7 @@ public class Workspace extends PagedView
mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
// Update the model if we have changed any screens
- mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
+ LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
}
}
@@ -955,7 +909,6 @@ public class Workspace extends PagedView
return -1;
}
- int index = getPageIndexForScreenId(EXTRA_EMPTY_SCREEN_ID);
CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
@@ -967,7 +920,7 @@ public class Workspace extends PagedView
mScreenOrder.add(newId);
// Update the model for the new screen
- mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
+ LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
return newId;
}
@@ -1012,7 +965,7 @@ public class Workspace extends PagedView
}
int currentPage = getNextPage();
- ArrayList<Long> removeScreens = new ArrayList<Long>();
+ ArrayList<Long> removeScreens = new ArrayList<>();
int total = mWorkspaceScreens.size();
for (int i = 0; i < total; i++) {
long id = mWorkspaceScreens.keyAt(i);
@@ -1056,7 +1009,7 @@ public class Workspace extends PagedView
if (!removeScreens.isEmpty()) {
// Update the model if we have changed any screens
- mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
+ LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
}
if (pageShift >= 0) {
@@ -1081,7 +1034,7 @@ public class Workspace extends PagedView
/**
* Adds the specified child in the specified screen based on the {@param info}
- * See {@link #addInScreen}.
+ * See {@link #addInScreen(View, long, long, int, int, int, int)}.
*/
public void addInScreen(View child, ItemInfo info) {
addInScreen(child, info.container, info.screenId, info.cellX, info.cellY,
@@ -1406,17 +1359,9 @@ public class Workspace extends PagedView
super.scrollTo(x, y);
}
- private void onWorkspaceOverallScrollChanged() {
- if (!mIgnoreQsbScroll) {
- mLauncher.getQsbContainer().setTranslationX(
- mOverlayTranslation + mFirstPageScrollX - getScrollX());
- }
- }
-
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
- onWorkspaceOverallScrollChanged();
// Update the page indicator progress.
boolean isTransitioning = mIsSwitchingState
@@ -1497,9 +1442,6 @@ public class Workspace extends PagedView
// device I've tried, translating the launcher causes things to get quite laggy.
setWorkspaceTranslationAndAlpha(Direction.X, transX, alpha);
setHotseatTranslationAndAlpha(Direction.X, transX, alpha);
- onWorkspaceOverallScrollChanged();
-
- mQsbAlphaController.setAlphaAtIndex(alpha, QSB_ALPHA_INDEX_OVERLAY_SCROLL);
}
/**
@@ -1509,9 +1451,6 @@ public class Workspace extends PagedView
*/
public void setWorkspaceYTranslationAndAlpha(float translation, float alpha) {
setWorkspaceTranslationAndAlpha(Direction.Y, translation, alpha);
-
- mLauncher.getQsbContainer().setTranslationY(translation);
- mQsbAlphaController.setAlphaAtIndex(alpha, QSB_ALPHA_INDEX_Y_TRANSLATION);
}
/**
@@ -1707,10 +1646,6 @@ public class Workspace extends PagedView
float scrollProgress = getScrollProgress(screenCenter, child, i);
float alpha = 1 - Math.abs(scrollProgress);
child.getShortcutsAndWidgets().setAlpha(alpha);
-
- if (isQsbContainerPage(i)) {
- mQsbAlphaController.setAlphaAtIndex(alpha, QSB_ALPHA_INDEX_PAGE_SCROLL);
- }
}
}
}
@@ -1805,7 +1740,6 @@ public class Workspace extends PagedView
}
super.onLayout(changed, left, top, right, bottom);
mFirstPageScrollX = getScrollForPage(0);
- onWorkspaceOverallScrollChanged();
final LayoutTransition transition = getLayoutTransition();
// If the transition is running defer updating max scroll, as some empty pages could
@@ -1827,7 +1761,6 @@ public class Workspace extends PagedView
mIgnoreQsbScroll = false;
transition.removeTransitionListener(this);
mFirstPageScrollX = getScrollForPage(0);
- onWorkspaceOverallScrollChanged();
}
}
});
@@ -1991,7 +1924,7 @@ public class Workspace extends PagedView
mScreenOrder.add(getIdForScreen(cl));
}
mLauncher.getUserEventDispatcher().logOverviewReorder();
- mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
+ LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
// Re-enable auto layout transitions for page deletion.
enableLayoutTransitions();
@@ -2070,10 +2003,6 @@ public class Workspace extends PagedView
mLauncher.notifyWidgetProvidersChanged();
}
- if (mOnStateChangeListener != null) {
- mOnStateChangeListener.prepareStateChange(toState, animated ? workspaceAnim : null);
- }
-
onPrepareStateTransition(mState.hasMultipleVisiblePages);
StateTransitionListener listener = new StateTransitionListener();
@@ -2322,8 +2251,8 @@ public class Workspace extends PagedView
mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter);
}
- int spanX = 1;
- int spanY = 1;
+ int spanX;
+ int spanY;
if (mDragInfo != null) {
final CellLayout.CellInfo dragCellInfo = mDragInfo;
spanX = dragCellInfo.spanX;
@@ -3155,11 +3084,11 @@ public class Workspace extends PagedView
}
class FolderCreationAlarmListener implements OnAlarmListener {
- CellLayout layout;
- int cellX;
- int cellY;
+ final CellLayout layout;
+ final int cellX;
+ final int cellY;
- FolderIcon.PreviewBackground bg = new FolderIcon.PreviewBackground();
+ final FolderIcon.PreviewBackground bg = new FolderIcon.PreviewBackground();
public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
this.layout = layout;
@@ -3182,10 +3111,10 @@ public class Workspace extends PagedView
}
class ReorderAlarmListener implements OnAlarmListener {
- float[] dragViewCenter;
- int minSpanX, minSpanY, spanX, spanY;
- DragObject dragObject;
- View child;
+ final float[] dragViewCenter;
+ final int minSpanX, minSpanY, spanX, spanY;
+ final DragObject dragObject;
+ final View child;
public ReorderAlarmListener(float[] dragViewCenter, int minSpanX, int minSpanY, int spanX,
int spanY, DragObject dragObject, View child) {
@@ -3277,7 +3206,7 @@ public class Workspace extends PagedView
boolean findNearestVacantCell = true;
if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
- mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
+ mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY,
cellLayout, mTargetCell);
float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
mDragViewVisualCenter[1], mTargetCell);
@@ -3343,7 +3272,7 @@ public class Workspace extends PagedView
animationStyle, finalView, true);
} else {
// This is for other drag/drop cases, like dragging from All Apps
- View view = null;
+ View view;
switch (info.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
@@ -3367,7 +3296,7 @@ public class Workspace extends PagedView
// First we find the cell nearest to point at which the item is
// dropped, without any consideration to whether there is an item there.
if (touchXY != null) {
- mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
+ mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY,
cellLayout, mTargetCell);
float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
mDragViewVisualCenter[1], mTargetCell);
@@ -3616,7 +3545,7 @@ public class Workspace extends PagedView
} else if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw new RuntimeException("Invalid state: cellLayout == null in "
+ "Workspace#onDropCompleted. Please file a bug. ");
- };
+ }
}
if ((d.cancelled || (beingCalledAfterUninstall && !mUninstallSuccessful))
&& mDragInfo.cell != null) {
@@ -3768,7 +3697,7 @@ public class Workspace extends PagedView
* Returns a list of all the CellLayouts in the workspace.
*/
ArrayList<CellLayout> getWorkspaceAndHotseatCellLayouts() {
- ArrayList<CellLayout> layouts = new ArrayList<CellLayout>();
+ ArrayList<CellLayout> layouts = new ArrayList<>();
int screenCount = getChildCount();
for (int screen = 0; screen < screenCount; screen++) {
layouts.add(((CellLayout) getChildAt(screen)));
@@ -3910,7 +3839,7 @@ public class Workspace extends PagedView
* @param view view for the shortcut
* @return true if done, false to continue the map
*/
- public boolean evaluate(ItemInfo info, View view);
+ boolean evaluate(ItemInfo info, View view);
}
/**
@@ -3952,7 +3881,7 @@ public class Workspace extends PagedView
void updateShortcuts(ArrayList<ShortcutInfo> shortcuts) {
int total = shortcuts.size();
- final HashSet<ShortcutInfo> updates = new HashSet<ShortcutInfo>(total);
+ final HashSet<ShortcutInfo> updates = new HashSet<>(total);
final HashSet<Long> folderIds = new HashSet<>();
for (int i = 0; i < total; i++) {
@@ -4230,20 +4159,6 @@ public class Workspace extends PagedView
}
}
- public interface OnStateChangeListener {
-
- /**
- * Called when the workspace state is changing.
- * @param toState final state
- * @param targetAnim animation which will be played during the transition or null.
- */
- void prepareStateChange(State toState, AnimatorSet targetAnim);
- }
-
- public static final boolean isQsbContainerPage(int pageNo) {
- return pageNo == 0;
- }
-
private class StateTransitionListener extends AnimatorListenerAdapter
implements AnimatorUpdateListener {
@Override
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 482a2c93b..32deaf286 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -355,27 +355,10 @@ public class WorkspaceStateTransitionAnimation {
cl.setBackgroundAlpha(finalBackgroundAlpha);
cl.setShortcutAndWidgetAlpha(finalAlpha);
}
-
- if (Workspace.isQsbContainerPage(i) &&
- states.stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
- if (animated) {
- Animator anim = mWorkspace.mQsbAlphaController
- .animateAlphaAtIndex(finalAlpha, Workspace.QSB_ALPHA_INDEX_PAGE_SCROLL);
- anim.setDuration(duration);
- anim.setInterpolator(mZoomInInterpolator);
- mStateAnimator.play(anim);
- } else {
- mWorkspace.mQsbAlphaController.setAlphaAtIndex(
- finalAlpha, Workspace.QSB_ALPHA_INDEX_PAGE_SCROLL);
- }
- }
}
final ViewGroup overviewPanel = mLauncher.getOverviewPanel();
- Animator qsbAlphaAnimation = mWorkspace.mQsbAlphaController
- .animateAlphaAtIndex(finalQsbAlpha, Workspace.QSB_ALPHA_INDEX_STATE_CHANGE);
-
if (animated) {
Animator scale = LauncherAnimUtils.ofPropertyValuesHolder(mWorkspace,
new PropertyListBuilder().scale(mNewScale)
@@ -393,7 +376,6 @@ public class WorkspaceStateTransitionAnimation {
// For animation optimization, we may need to provide the Launcher transition
// with a set of views on which to force build and manage layers in certain scenarios.
layerViews.addView(overviewPanel);
- layerViews.addView(mLauncher.getQsbContainer());
layerViews.addView(mLauncher.getHotseat());
layerViews.addView(mWorkspace.getPageIndicator());
@@ -407,11 +389,9 @@ public class WorkspaceStateTransitionAnimation {
overviewPanelAlpha.setDuration(duration);
hotseatAlpha.setDuration(duration);
- qsbAlphaAnimation.setDuration(duration);
mStateAnimator.play(overviewPanelAlpha);
mStateAnimator.play(hotseatAlpha);
- mStateAnimator.play(qsbAlphaAnimation);
mStateAnimator.addListener(new AnimatorListenerAdapter() {
boolean canceled = false;
@Override
@@ -439,7 +419,6 @@ public class WorkspaceStateTransitionAnimation {
AlphaUpdateListener.updateVisibility(overviewPanel, accessibilityEnabled);
mWorkspace.getPageIndicator().setShouldAutoHide(!states.stateIsSpringLoaded);
- qsbAlphaAnimation.end();
mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha).end();
mWorkspace.updateCustomContentVisibility();
mWorkspace.setScaleX(mNewScale);
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index a399d748a..c3df07360 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -20,7 +20,6 @@ import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.InsetDrawable;
-import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Selection;
@@ -92,9 +91,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
mLauncher = Launcher.getLauncher(context);
mApps = new AlphabeticalAppsList(context);
- mSpringAnimationHandler = new SpringAnimationHandler(SpringAnimationHandler.Y_DIRECTION);
- mAdapter = new AllAppsGridAdapter(mLauncher, mApps, mLauncher, this,
- mSpringAnimationHandler);
+ mAdapter = new AllAppsGridAdapter(mLauncher, mApps, mLauncher, this);
+ mSpringAnimationHandler = mAdapter.getSpringAnimationHandler();
mApps.setAdapter(mAdapter);
mLayoutManager = mAdapter.getLayoutManager();
mSearchQueryBuilder = new SpannableStringBuilder();
@@ -426,8 +424,11 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- if (mScrollState == RecyclerView.SCROLL_STATE_DRAGGING) {
- mSpringAnimationHandler.skipToEnd();
+ if (mScrollState == RecyclerView.SCROLL_STATE_DRAGGING
+ || (dx == 0 && dy == 0)) {
+ if (mSpringAnimationHandler.isRunning()){
+ mSpringAnimationHandler.skipToEnd();
+ }
return;
}
@@ -436,7 +437,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
// We only show the spring animation when at the top or bottom, so we wait until the
// first or last row is visible to ensure that all animations run in sync.
- if (first == 0 || last >= mAdapter.getItemCount() - mAdapter.getNumAppsPerRow()) {
+ if ((first == 0 && dy < 0) || (last == mAdapter.getItemCount() - 1 && dy > 0)) {
mSpringAnimationHandler.animateToFinalPosition(0);
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index d3d23ca24..9c7372f2c 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -19,6 +19,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Point;
+import android.support.animation.DynamicAnimation;
import android.support.animation.SpringAnimation;
import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
@@ -38,6 +39,7 @@ import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.config.FeatureFlags;
@@ -96,11 +98,6 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
- /**
- * Springs used for items where isViewType(viewType, VIEW_TYPE_MASK_HAS_SPRINGS) is true.
- */
- private SpringAnimation spring;
-
public ViewHolder(View v) {
super(v);
}
@@ -213,11 +210,10 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
// The intent to send off to the market app, updated each time the search query changes.
private Intent mMarketSearchIntent;
- private SpringAnimationHandler mSpringAnimationHandler;
+ private SpringAnimationHandler<ViewHolder> mSpringAnimationHandler;
public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps, View.OnClickListener
- iconClickListener, View.OnLongClickListener iconLongClickListener,
- SpringAnimationHandler springAnimationHandler) {
+ iconClickListener, View.OnLongClickListener iconLongClickListener) {
Resources res = launcher.getResources();
mLauncher = launcher;
mApps = apps;
@@ -228,7 +224,14 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
mLayoutInflater = LayoutInflater.from(launcher);
mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener;
- mSpringAnimationHandler = springAnimationHandler;
+ if (FeatureFlags.LAUNCHER3_PHYSICS) {
+ mSpringAnimationHandler = new SpringAnimationHandler<>(
+ SpringAnimationHandler.Y_DIRECTION, new AllAppsSpringAnimationFactory());
+ }
+ }
+
+ public SpringAnimationHandler getSpringAnimationHandler() {
+ return mSpringAnimationHandler;
}
public static boolean isDividerViewType(int viewType) {
@@ -292,8 +295,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
R.layout.all_apps_icon, parent, false);
icon.setOnClickListener(mIconClickListener);
icon.setOnLongClickListener(mIconLongClickListener);
- icon.setLongPressTimeout(ViewConfiguration.get(parent.getContext())
- .getLongPressTimeout());
+ icon.setLongPressTimeout(ViewConfiguration.getLongPressTimeout());
icon.setOnFocusChangeListener(mIconFocusListener);
// Ensure the all apps icon height matches the workspace icons
@@ -386,8 +388,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
public void onViewAttachedToWindow(ViewHolder holder) {
int type = holder.getItemViewType();
if (FeatureFlags.LAUNCHER3_PHYSICS && isViewType(type, VIEW_TYPE_MASK_HAS_SPRINGS)) {
- holder.spring = mSpringAnimationHandler.add(holder.itemView,
- holder.getAdapterPosition(), mApps, mAppsPerRow, holder.spring);
+ mSpringAnimationHandler.add(holder.itemView, holder);
}
}
@@ -395,7 +396,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
public void onViewDetachedFromWindow(ViewHolder holder) {
int type = holder.getItemViewType();
if (FeatureFlags.LAUNCHER3_PHYSICS && isViewType(type, VIEW_TYPE_MASK_HAS_SPRINGS)) {
- holder.spring = mSpringAnimationHandler.remove(holder.spring);
+ mSpringAnimationHandler.remove(holder.itemView);
}
}
@@ -415,4 +416,121 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
return item.viewType;
}
+
+ /**
+ * Helper class to set the SpringAnimation values for an item in the adapter.
+ */
+ private class AllAppsSpringAnimationFactory
+ implements SpringAnimationHandler.AnimationFactory<ViewHolder> {
+ private static final float DEFAULT_MAX_VALUE_PX = 100;
+ private static final float DEFAULT_MIN_VALUE_PX = -DEFAULT_MAX_VALUE_PX;
+
+ // Damping ratio range is [0, 1]
+ private static final float SPRING_DAMPING_RATIO = 0.55f;
+
+ // Stiffness is a non-negative number.
+ private static final float MIN_SPRING_STIFFNESS = 580f;
+ private static final float MAX_SPRING_STIFFNESS = 900f;
+
+ // The amount by which each adjacent rows' stiffness will differ.
+ private static final float ROW_STIFFNESS_COEFFICIENT = 50f;
+
+ @Override
+ public SpringAnimation initialize(ViewHolder vh) {
+ return SpringAnimationHandler.forView(vh.itemView, DynamicAnimation.TRANSLATION_Y, 0);
+ }
+
+ /**
+ * @param spring A new or recycled SpringAnimation.
+ * @param vh The ViewHolder that {@param spring} is related to.
+ */
+ @Override
+ public void update(SpringAnimation spring, ViewHolder vh) {
+ int numPredictedApps = Math.min(mAppsPerRow, mApps.getPredictedApps().size());
+ int appPosition = getAppPosition(vh.getAdapterPosition(), numPredictedApps,
+ mAppsPerRow);
+
+ int col = appPosition % mAppsPerRow;
+ int row = appPosition / mAppsPerRow;
+
+ int numTotalRows = mApps.getNumAppRows() - 1; // zero-based count
+ if (row > (numTotalRows / 2)) {
+ // Mirror the rows so that the top row acts the same as the bottom row.
+ row = Math.abs(numTotalRows - row);
+ }
+
+ // We manipulate the stiffness, min, and max values based on the items distance to the
+ // first row and the items distance to the center column to create the ^-shaped motion
+ // effect.
+ float rowFactor = (1 + row) * 0.5f;
+ float colFactor = getColumnFactor(col, mAppsPerRow);
+
+ float minValue = DEFAULT_MIN_VALUE_PX * (rowFactor + colFactor);
+ float maxValue = DEFAULT_MAX_VALUE_PX * (rowFactor + colFactor);
+
+ float stiffness = Utilities.boundToRange(
+ MAX_SPRING_STIFFNESS - (row * ROW_STIFFNESS_COEFFICIENT),
+ MIN_SPRING_STIFFNESS,
+ MAX_SPRING_STIFFNESS);
+
+ spring.setMinValue(minValue)
+ .setMaxValue(maxValue)
+ .getSpring()
+ .setStiffness(stiffness)
+ .setDampingRatio(SPRING_DAMPING_RATIO);
+ }
+
+ /**
+ * @return The app position is the position of the app in the Adapter if we ignored all
+ * other view types.
+ *
+ * The first app is at position 0, and the first app each following row is at a
+ * position that is a multiple of {@param appsPerRow}.
+ *
+ * ie. If there are 5 apps per row, and there are two rows of apps:
+ * 0 1 2 3 4
+ * 5 6 7 8 9
+ */
+ private int getAppPosition(int position, int numPredictedApps, int appsPerRow) {
+ int appPosition = position;
+ int numDividerViews = 1 + (numPredictedApps == 0 ? 0 : 1);
+
+ int allAppsStartAt = numDividerViews + numPredictedApps;
+ if (numDividerViews == 1 || position < allAppsStartAt) {
+ appPosition -= 1;
+ } else {
+ // We cannot assume that the predicted row will always be full.
+ int numPredictedAppsOffset = appsPerRow - numPredictedApps;
+ appPosition = position + numPredictedAppsOffset - numDividerViews;
+ }
+
+ return appPosition;
+ }
+
+ /**
+ * Increase the column factor as the distance increases between the column and the center
+ * column(s).
+ */
+ private float getColumnFactor(int col, int numCols) {
+ float centerColumn = numCols / 2;
+ int distanceToCenter = (int) Math.abs(col - centerColumn);
+
+ boolean evenNumberOfColumns = numCols % 2 == 0;
+ if (evenNumberOfColumns && col < centerColumn) {
+ distanceToCenter -= 1;
+ }
+
+ float factor = 0;
+ while (distanceToCenter > 0) {
+ if (distanceToCenter == 1) {
+ factor += 0.2f;
+ } else {
+ factor += 0.1f;
+ }
+ --distanceToCenter;
+ }
+
+ return factor;
+ }
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index d76abccd3..b2a74ff15 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -97,6 +97,10 @@ public class AllAppsRecyclerView extends BaseRecyclerView {
mFastScrollHelper = new AllAppsFastScrollHelper(this, apps);
}
+ public AlphabeticalAppsList getApps() {
+ return mApps;
+ }
+
/**
* Sets the number of apps per row in this recycler view.
*/
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index ba20135be..d79b0d19d 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -71,6 +71,7 @@ public class AllAppsTransitionController implements TouchController, VerticalPul
private final VerticalPullDetector mDetector;
private final ArgbEvaluator mEvaluator;
private final boolean mIsDarkTheme;
+ private final boolean mIsWorkspaceDarkText;
// Animation in this class is controlled by a single variable {@link mProgress}.
// Visually, it represents top y coordinate of the all apps container if multiplied with
@@ -112,7 +113,8 @@ public class AllAppsTransitionController implements TouchController, VerticalPul
mEvaluator = new ArgbEvaluator();
mAllAppsBackgroundColor = Themes.getAttrColor(l, android.R.attr.colorPrimary);
- mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isPrimaryColorDark);
+ mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
+ mIsWorkspaceDarkText = Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText);
}
@Override
@@ -281,7 +283,7 @@ public class AllAppsTransitionController implements TouchController, VerticalPul
private void updateLightStatusBar(float shift) {
// Do not modify status bar in dark theme or on landscape as all apps is not full bleed.
- if (mIsDarkTheme || (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS
+ if (mIsDarkTheme || mIsWorkspaceDarkText || (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS
&& mLauncher.getDeviceProfile().isVerticalBarLayout())) {
return;
}
diff --git a/src/com/android/launcher3/anim/AnimationLayerSet.java b/src/com/android/launcher3/anim/AnimationLayerSet.java
index 14bcd1718..f0b34587a 100644
--- a/src/com/android/launcher3/anim/AnimationLayerSet.java
+++ b/src/com/android/launcher3/anim/AnimationLayerSet.java
@@ -18,9 +18,8 @@ package com.android.launcher3.anim;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.util.ArrayMap;
import android.view.View;
-
-import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -29,14 +28,14 @@ import java.util.Map;
*/
public class AnimationLayerSet extends AnimatorListenerAdapter {
- private final HashMap<View, Integer> mViewsToLayerTypeMap;
+ private final ArrayMap<View, Integer> mViewsToLayerTypeMap;
public AnimationLayerSet() {
- mViewsToLayerTypeMap = new HashMap<>();
+ mViewsToLayerTypeMap = new ArrayMap<>();
}
public AnimationLayerSet(View v) {
- mViewsToLayerTypeMap = new HashMap<>(1);
+ mViewsToLayerTypeMap = new ArrayMap<>(1);
addView(v);
}
diff --git a/src/com/android/launcher3/anim/SpringAnimationHandler.java b/src/com/android/launcher3/anim/SpringAnimationHandler.java
index 488657c36..038f82682 100644
--- a/src/com/android/launcher3/anim/SpringAnimationHandler.java
+++ b/src/com/android/launcher3/anim/SpringAnimationHandler.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.anim;
-import android.support.animation.DynamicAnimation;
+import android.support.animation.FloatPropertyCompat;
import android.support.animation.SpringAnimation;
import android.support.animation.SpringForce;
import android.support.annotation.IntDef;
@@ -24,8 +24,7 @@ import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AlphabeticalAppsList;
+import com.android.launcher3.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -35,77 +34,67 @@ import java.util.ArrayList;
* Handler class that manages springs for a set of views that should all move based on the same
* {@link MotionEvent}s.
*
- * Supports using physics for X or Y translations.
+ * Supports setting either X or Y velocity on the list of springs added to this handler.
*/
-public class SpringAnimationHandler {
+public class SpringAnimationHandler<T> {
private static final String TAG = "SpringAnimationHandler";
private static final boolean DEBUG = false;
- private static final float DEFAULT_MAX_VALUE = 100;
- private static final float DEFAULT_MIN_VALUE = -DEFAULT_MAX_VALUE;
-
- private static final float SPRING_DAMPING_RATIO = 0.55f;
- private static final float MIN_SPRING_STIFFNESS = 580f;
- private static final float MAX_SPRING_STIFFNESS = 900f;
+ private static final float VELOCITY_DAMPING_FACTOR = 0.175f;
@Retention(RetentionPolicy.SOURCE)
@IntDef({Y_DIRECTION, X_DIRECTION})
public @interface Direction {}
public static final int Y_DIRECTION = 0;
public static final int X_DIRECTION = 1;
- private int mDirection;
+ private int mVelocityDirection;
private VelocityTracker mVelocityTracker;
private float mCurrentVelocity = 0;
private boolean mShouldComputeVelocity = false;
+ private AnimationFactory<T> mAnimationFactory;
+
private ArrayList<SpringAnimation> mAnimations = new ArrayList<>();
- public SpringAnimationHandler(@Direction int direction) {
- mDirection = direction;
- mVelocityTracker = VelocityTracker.obtain();
+ /**
+ * @param direction Either {@link #X_DIRECTION} or {@link #Y_DIRECTION}.
+ * Determines which direction we use to calculate and set the velocity.
+ * @param factory The AnimationFactory is responsible for initializing and updating the
+ * SpringAnimations added to this class.
+ */
+ public SpringAnimationHandler(@Direction int direction, AnimationFactory<T> factory) {
+ mVelocityDirection = direction;
+ mAnimationFactory = factory;
}
- public SpringAnimation add(View view, int position, AlphabeticalAppsList apps, int appsPerRow,
- SpringAnimation recycle) {
- int numPredictedApps = Math.min(appsPerRow, apps.getPredictedApps().size());
- int appPosition = getAppPosition(position, numPredictedApps, appsPerRow);
-
- int col = appPosition % appsPerRow;
- int row = appPosition / appsPerRow;
-
- int numTotalRows = apps.getNumAppRows() - 1; // zero offset
- if (row > (numTotalRows / 2)) {
- // Mirror the rows so that the top row acts the same as the bottom row.
- row = Math.abs(numTotalRows - row);
+ /**
+ * Adds a new or recycled animation to the list of springs handled by this class.
+ *
+ * @param view The view the spring is attached to.
+ * @param object Used to initialize and update the spring.
+ */
+ public void add(View view, T object) {
+ SpringAnimation spring = (SpringAnimation) view.getTag(R.id.spring_animation_tag);
+ if (spring == null) {
+ spring = mAnimationFactory.initialize(object);
+ view.setTag(R.id.spring_animation_tag, spring);
}
-
- // We manipulate the stiffness, min, and max values based on the items distance to the first
- // row and the items distance to the center column to create the ^-shaped motion effect.
- float rowFactor = (1 + row) * 0.5f;
- float colFactor = getColumnFactor(col, appsPerRow);
-
- float minValue = DEFAULT_MIN_VALUE * (rowFactor + colFactor);
- float maxValue = DEFAULT_MAX_VALUE * (rowFactor + colFactor);
-
- float stiffness = Utilities.boundToRange(MAX_SPRING_STIFFNESS - (row * 50f),
- MIN_SPRING_STIFFNESS, MAX_SPRING_STIFFNESS);
-
- SpringAnimation animation = (recycle != null ? recycle : createSpringAnimation(view))
- .setStartVelocity(mCurrentVelocity)
- .setMinValue(minValue)
- .setMaxValue(maxValue);
- animation.getSpring().setStiffness(stiffness);
-
- mAnimations.add(animation);
- return animation;
+ mAnimationFactory.update(spring, object);
+ spring.setStartVelocity(mCurrentVelocity);
+ mAnimations.add(spring);
}
- public SpringAnimation remove(SpringAnimation animation) {
- animation.skipToEnd();
+ /**
+ * Stops and removes the spring attached to {@param view}.
+ */
+ public void remove(View view) {
+ SpringAnimation animation = (SpringAnimation) view.getTag(R.id.spring_animation_tag);
+ if (animation.canSkipToEnd()) {
+ animation.skipToEnd();
+ }
mAnimations.remove(animation);
- return animation;
}
public void addMovement(MotionEvent event) {
@@ -138,13 +127,20 @@ public class SpringAnimationHandler {
reset();
}
+ public boolean isRunning() {
+ // All the animations run at the same time so we can just check the first one.
+ return !mAnimations.isEmpty() && mAnimations.get(0).isRunning();
+ }
+
public void skipToEnd() {
if (DEBUG) Log.d(TAG, "setStartVelocity#skipToEnd");
if (DEBUG) Log.v(TAG, "setStartVelocity#skipToEnd", new Exception());
int size = mAnimations.size();
for (int i = 0; i < size; ++i) {
- mAnimations.get(i).skipToEnd();
+ if (mAnimations.get(i).canSkipToEnd()) {
+ mAnimations.get(i).skipToEnd();
+ }
}
}
@@ -164,84 +160,55 @@ public class SpringAnimationHandler {
}
private void computeVelocity() {
- getVelocityTracker().computeCurrentVelocity(175);
+ getVelocityTracker().computeCurrentVelocity(1000 /* millis */);
mCurrentVelocity = isVerticalDirection()
? getVelocityTracker().getYVelocity()
: getVelocityTracker().getXVelocity();
+ mCurrentVelocity *= VELOCITY_DAMPING_FACTOR;
mShouldComputeVelocity = false;
if (DEBUG) Log.d(TAG, "computeVelocity=" + mCurrentVelocity);
}
private boolean isVerticalDirection() {
- return mDirection == Y_DIRECTION;
+ return mVelocityDirection == Y_DIRECTION;
}
- private SpringAnimation createSpringAnimation(View view) {
- DynamicAnimation.ViewProperty property = isVerticalDirection()
- ? DynamicAnimation.TRANSLATION_Y
- : DynamicAnimation.TRANSLATION_X;
-
- return new SpringAnimation(view, property, 0)
- .setStartValue(1f)
- .setSpring(new SpringForce(0)
- .setDampingRatio(SPRING_DAMPING_RATIO));
+ private VelocityTracker getVelocityTracker() {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ return mVelocityTracker;
}
/**
- * @return The app position is the position of the app in the Adapter if we ignored all other
- * view types.
+ * This interface is used to initialize and update the SpringAnimations added to the
+ * {@link SpringAnimationHandler}.
*
- * ie. The first predicted app is at position 0, and the first app of all apps is
- * at {@param appsPerRow}.
+ * @param <T> The object that each SpringAnimation is attached to.
*/
- private int getAppPosition(int position, int numPredictedApps, int appsPerRow) {
- int appPosition = position;
- int numDividerViews = 1 + (numPredictedApps == 0 ? 0 : 1);
-
- int allAppsStartAt = numDividerViews + numPredictedApps;
- if (numDividerViews == 1 || position < allAppsStartAt) {
- appPosition -= 1;
- } else {
- // We cannot assume that the predicted row will always be full.
- int numPredictedAppsOffset = appsPerRow - numPredictedApps;
- appPosition = position + numPredictedAppsOffset - numDividerViews;
- }
+ public interface AnimationFactory<T> {
+
+ /**
+ * Initializes a new Spring for {@param object}.
+ */
+ SpringAnimation initialize(T object);
- return appPosition;
+ /**
+ * Updates the value of {@param spring} based on {@param object}.
+ */
+ void update(SpringAnimation spring, T object);
}
/**
- * Increase the column factor as the distance increases between the column and the center
- * column(s).
+ * Helper method to create a new SpringAnimation for {@param view}.
*/
- private float getColumnFactor(int col, int numCols) {
- float centerColumn = numCols / 2;
- int distanceToCenter = (int) Math.abs(col - centerColumn);
-
- boolean evenNumberOfColumns = numCols % 2 == 0;
- if (evenNumberOfColumns && col < centerColumn) {
- distanceToCenter -= 1;
- }
-
- float factor = 0;
- while (distanceToCenter > 0) {
- if (distanceToCenter == 1) {
- factor += 0.2f;
- } else {
- factor += 0.1f;
- }
- --distanceToCenter;
- }
-
- return factor;
+ public static SpringAnimation forView(View view, FloatPropertyCompat property, float finalPos) {
+ SpringAnimation spring = new SpringAnimation(view, property, finalPos);
+ spring.setStartValue(1f);
+ spring.setSpring(new SpringForce(finalPos));
+ return spring;
}
- private VelocityTracker getVelocityTracker() {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- return mVelocityTracker;
- }
}
diff --git a/src/com/android/launcher3/badge/BadgeRenderer.java b/src/com/android/launcher3/badge/BadgeRenderer.java
index adde4a2fc..ba1977af4 100644
--- a/src/com/android/launcher3/badge/BadgeRenderer.java
+++ b/src/com/android/launcher3/badge/BadgeRenderer.java
@@ -63,7 +63,6 @@ public class BadgeRenderer {
private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG
| Paint.FILTER_BITMAP_FLAG);
private final SparseArray<Bitmap> mBackgroundsWithShadow;
- private final IconPalette mIconPalette;
public BadgeRenderer(Context context, int iconSizePx) {
mContext = context;
@@ -83,25 +82,24 @@ public class BadgeRenderer {
mTextHeight = tempTextHeight.height();
mBackgroundsWithShadow = new SparseArray<>(3);
-
- mIconPalette = IconPalette.fromDominantColor(context.getColor(R.color.badge_color));
}
/**
* Draw a circle in the top right corner of the given bounds, and draw
* {@link BadgeInfo#getNotificationCount()} on top of the circle.
+ * @param palette The colors (based on the icon) to use for the badge.
* @param badgeInfo Contains data to draw on the badge. Could be null if we are animating out.
* @param iconBounds The bounds of the icon being badged.
* @param badgeScale The progress of the animation, from 0 to 1.
* @param spaceForOffset How much space is available to offset the badge up and to the right.
*/
- public void draw(Canvas canvas, @Nullable BadgeInfo badgeInfo,
+ public void draw(Canvas canvas, IconPalette palette, @Nullable BadgeInfo badgeInfo,
Rect iconBounds, float badgeScale, Point spaceForOffset) {
- mTextPaint.setColor(mIconPalette.textColor);
+ mTextPaint.setColor(palette.textColor);
IconDrawer iconDrawer = badgeInfo != null && badgeInfo.isIconLarge()
? mLargeIconDrawer : mSmallIconDrawer;
Shader icon = badgeInfo == null ? null : badgeInfo.getNotificationIconForBadge(
- mContext, mIconPalette.backgroundColor, mSize, iconDrawer.mPadding);
+ mContext, palette.backgroundColor, mSize, iconDrawer.mPadding);
String notificationCount = badgeInfo == null ? "0"
: String.valueOf(badgeInfo.getNotificationCount());
int numChars = notificationCount.length();
@@ -127,7 +125,7 @@ public class BadgeRenderer {
canvas.translate(badgeCenterX + offsetX, badgeCenterY - offsetY);
canvas.scale(badgeScale, badgeScale);
// Prepare the background and shadow and possible stacking effect.
- mBackgroundPaint.setColorFilter(mIconPalette.backgroundColorMatrixFilter);
+ mBackgroundPaint.setColorFilter(palette.backgroundColorMatrixFilter);
int backgroundWithShadowSize = backgroundWithShadow.getHeight(); // Same as width.
boolean shouldStack = !isDot && badgeInfo != null
&& badgeInfo.getNotificationKeys().size() > 1;
@@ -149,7 +147,7 @@ public class BadgeRenderer {
-backgroundWithShadowSize / 2, mBackgroundPaint);
iconDrawer.drawIcon(icon, canvas);
} else if (isDot) {
- mBackgroundPaint.setColorFilter(mIconPalette.saturatedBackgroundColorMatrixFilter);
+ mBackgroundPaint.setColorFilter(palette.saturatedBackgroundColorMatrixFilter);
canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
-backgroundWithShadowSize / 2, mBackgroundPaint);
}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index 26f4ae721..75a2a5d18 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -25,11 +25,9 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.UserHandle;
import android.support.annotation.Nullable;
-
import com.android.launcher3.Utilities;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.util.PackageUserKey;
-
import java.util.List;
public abstract class LauncherAppsCompat {
@@ -50,7 +48,7 @@ public abstract class LauncherAppsCompat {
}
private static LauncherAppsCompat sInstance;
- private static Object sInstanceLock = new Object();
+ private static final Object sInstanceLock = new Object();
public static LauncherAppsCompat getInstance(Context context) {
synchronized (sInstanceLock) {
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
index 34316644a..8bdcedbbc 100644
--- a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
@@ -17,31 +17,36 @@ package com.android.launcher3.compat;
import static android.app.WallpaperManager.FLAG_SYSTEM;
-import android.app.IntentService;
+import static com.android.launcher3.Utilities.getDevicePrefs;
+
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
-import android.os.ResultReceiver;
import android.support.annotation.Nullable;
import android.support.v7.graphics.Palette;
import android.util.Log;
import android.util.Pair;
import android.util.SparseIntArray;
-import com.android.launcher3.LauncherFiles;
import com.android.launcher3.Utilities;
import java.io.IOException;
@@ -53,7 +58,8 @@ public class WallpaperManagerCompatVL extends WallpaperManagerCompat {
private static final String VERSION_PREFIX = "1,";
private static final String KEY_COLORS = "wallpaper_parsed_colors";
- private static final String EXTRA_RECEIVER = "receiver";
+ private static final String ACTION_EXTRACTION_COMPLETE =
+ "com.android.launcher3.compat.WallpaperManagerCompatVL.EXTRACTION_COMPLETE";
private final ArrayList<OnColorsChangedListenerCompat> mListeners = new ArrayList<>();
@@ -63,7 +69,7 @@ public class WallpaperManagerCompatVL extends WallpaperManagerCompat {
WallpaperManagerCompatVL(Context context) {
mContext = context;
- String colors = prefs(mContext).getString(KEY_COLORS, "");
+ String colors = getDevicePrefs(mContext).getString(KEY_COLORS, "");
int wallpaperId = -1;
if (colors.startsWith(VERSION_PREFIX)) {
Pair<Integer, WallpaperColorsCompat> storedValue = parseValue(colors);
@@ -80,6 +86,28 @@ public class WallpaperManagerCompatVL extends WallpaperManagerCompat {
reloadColors();
}
}, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
+
+ // Register a receiver for results
+ String permission = null;
+ // Find a permission which only we can use.
+ try {
+ for (PermissionInfo info : context.getPackageManager().getPackageInfo(
+ context.getPackageName(),
+ PackageManager.GET_PERMISSIONS).permissions) {
+ if ((info.protectionLevel & PermissionInfo.PROTECTION_SIGNATURE) != 0) {
+ permission = info.name;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Something went wrong. ignore
+ Log.d(TAG, "Unable to get permission info", e);
+ }
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ handleResult(intent.getStringExtra(KEY_COLORS));
+ }
+ }, new IntentFilter(ACTION_EXTRACTION_COMPLETE), permission, new Handler());
}
@Nullable
@@ -94,30 +122,20 @@ public class WallpaperManagerCompatVL extends WallpaperManagerCompat {
}
private void reloadColors() {
- ResultReceiver receiver = new ResultReceiver(new Handler()) {
-
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- handleResult(resultData.getString(KEY_COLORS));
- }
- };
- mContext.startService(new Intent(mContext, ColorExtractionService.class)
- .putExtra(EXTRA_RECEIVER, receiver));
+ JobInfo job = new JobInfo.Builder(Utilities.WALLPAPER_COMPAT_JOB_ID,
+ new ComponentName(mContext, ColorExtractionService.class))
+ .setMinimumLatency(0).build();
+ ((JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE)).schedule(job);
}
private void handleResult(String result) {
- prefs(mContext).edit().putString(KEY_COLORS, result).apply();
+ getDevicePrefs(mContext).edit().putString(KEY_COLORS, result).apply();
mColorsCompat = parseValue(result).second;
for (OnColorsChangedListenerCompat listener : mListeners) {
listener.onColorsChanged(mColorsCompat, FLAG_SYSTEM);
}
}
- private static SharedPreferences prefs(Context context) {
- return context.getSharedPreferences(
- LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
- }
-
private static final int getWallpaperId(Context context) {
if (!Utilities.ATLEAST_NOUGAT) {
return -1;
@@ -146,18 +164,43 @@ public class WallpaperManagerCompatVL extends WallpaperManagerCompat {
/**
* Intent service to handle color extraction
*/
- public static class ColorExtractionService extends IntentService {
+ public static class ColorExtractionService extends JobService implements Runnable {
private static final int MAX_WALLPAPER_EXTRACTION_AREA = 112 * 112;
- public ColorExtractionService() {
- super("ColorExtractionService");
+ private HandlerThread mWorkerThread;
+ private Handler mWorkerHandler;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mWorkerThread = new HandlerThread("ColorExtractionService");
+ mWorkerThread.start();
+ mWorkerHandler = new Handler(mWorkerThread.getLooper());
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mWorkerThread.quit();
+ }
+
+ @Override
+ public boolean onStartJob(final JobParameters jobParameters) {
+ mWorkerHandler.post(this);
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters jobParameters) {
+ mWorkerHandler.removeCallbacksAndMessages(null);
+ return true;
}
/**
* Extracts the wallpaper colors and sends the result back through the receiver.
*/
@Override
- protected void onHandleIntent(@Nullable Intent intent) {
+ public void run() {
int wallpaperId = getWallpaperId(this);
Bitmap bitmap = null;
@@ -228,10 +271,10 @@ public class WallpaperManagerCompatVL extends WallpaperManagerCompat {
value = builder.toString();
}
- ResultReceiver receiver = intent.getParcelableExtra(EXTRA_RECEIVER);
- Bundle result = new Bundle();
- result.putString(KEY_COLORS, value);
- receiver.send(0, result);
+ // Send the result
+ sendBroadcast(new Intent(ACTION_EXTRACTION_COMPLETE)
+ .setPackage(getPackageName())
+ .putExtra(KEY_COLORS, value));
}
}
}
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 7178c5e8d..be5f01adb 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -27,6 +27,7 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.Region;
+import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -54,10 +55,12 @@ import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dynamicui.WallpaperColorInfo;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.widget.WidgetsBottomSheet;
@@ -72,9 +75,6 @@ public class DragLayer extends InsettableFrameLayout {
public static final int ANIMATION_END_DISAPPEAR = 0;
public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
- // Scrim color without any alpha component.
- private static final int SCRIM_COLOR = Color.BLACK & 0x00FFFFFF;
-
private final int[] mTmpXY = new int[2];
@Thunk DragController mDragController;
@@ -107,6 +107,7 @@ public class DragLayer extends InsettableFrameLayout {
// Related to adjacent page hints
private final Rect mScrollChildPosition = new Rect();
private final ViewGroupFocusHelper mFocusIndicatorHelper;
+ private final WallpaperColorInfo mWallpaperColorInfo;
// Related to pinch-to-go-to-overview gesture.
private PinchToOverviewListener mPinchListener = null;
@@ -130,6 +131,7 @@ public class DragLayer extends InsettableFrameLayout {
mIsRtl = Utilities.isRtl(getResources());
mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
+ mWallpaperColorInfo = WallpaperColorInfo.getInstance(getContext());
}
public void setup(Launcher launcher, DragController dragController,
@@ -451,7 +453,8 @@ public class DragLayer extends InsettableFrameLayout {
@Override
public void setInsets(Rect insets) {
super.setInsets(insets);
- setBackgroundResource(insets.top == 0 ? 0 : R.drawable.workspace_bg);
+ setBackground(insets.top == 0 ? null
+ : Themes.getAttrDrawable(getContext(), R.attr.workspaceStatusBarScrim));
}
@Override
@@ -876,7 +879,10 @@ public class DragLayer extends InsettableFrameLayout {
getDescendantRectRelativeToSelf(currCellLayout, mHighlightRect);
canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE);
}
- canvas.drawColor((alpha << 24) | SCRIM_COLOR);
+ // for super light wallpaper it needs to be darken for contrast to workspace
+ // for dark wallpapers the text is white so darkening works as well
+ int color = ColorUtils.compositeColors(0x66000000, mWallpaperColorInfo.getMainColor());
+ canvas.drawColor(ColorUtils.setAlphaComponent(color, alpha));
canvas.restore();
}
diff --git a/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java b/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java
index 5a0e78b12..21d5b2744 100644
--- a/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java
+++ b/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java
@@ -23,6 +23,7 @@ import android.support.annotation.Nullable;
import android.support.v4.graphics.ColorUtils;
import android.util.Log;
import android.util.Pair;
+import android.util.Range;
import android.util.SparseIntArray;
import com.android.launcher3.R;
@@ -51,16 +52,18 @@ public class ColorExtractionAlgorithm {
private static final float FIT_WEIGHT_S = 1.0f;
private static final float FIT_WEIGHT_L = 10.0f;
+ // When extracting the main color, only consider colors
+ // present in at least MIN_COLOR_OCCURRENCE of the image
private static final float MIN_COLOR_OCCURRENCE = 0.1f;
- private static final float MIN_LUMINOSITY = 0.5f;
- public ColorExtractionAlgorithm() {
- }
+ // Temporary variable to avoid allocations
+ private final float[] mTmpHSL = new float[3];
public @Nullable Pair<Integer, Integer> extractInto(WallpaperColorsCompat wallpaperColors) {
if (wallpaperColors == null) {
return null;
}
+
SparseIntArray colorsArray = wallpaperColors.getColors();
if (colorsArray.size() == 0) {
return null;
@@ -71,13 +74,12 @@ public class ColorExtractionAlgorithm {
// and replaces the original palette
List<Pair<Integer, Integer>> colors = new ArrayList<>(colorsArray.size());
- for (int i = colorsArray.size() - 1; i >= 0; i --) {
+ for (int i = colorsArray.size() - 1; i >= 0; i--) {
colors.add(Pair.create(colorsArray.keyAt(i), colorsArray.valueAt(i)));
}
// First find the most representative color in the image
populationSort(colors);
-
// Calculate total
int total = 0;
for (Pair<Integer, Integer> weightedColor : colors) {
@@ -96,53 +98,80 @@ public class ColorExtractionAlgorithm {
int colorValue = weightedColor.first;
ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue),
Color.blue(colorValue), hsl);
- if (hsl[2] > MIN_LUMINOSITY) {
+
+ // Stop when we find a color that meets our criteria
+ if (!isBlacklisted(hsl)) {
bestColor = weightedColor;
+ break;
}
}
- // Fallback to first color
+ // Fail if not found
if (bestColor == null) {
- bestColor = colors.get(0);
+ return null;
}
int colorValue = bestColor.first;
ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue), Color.blue(colorValue),
hsl);
- hsl[0] /= 360.0f; // normalize
- // TODO, we're finding a tonal palette for a hue, not all components
+ // The Android HSL definition requires the hue to go from 0 to 360 but
+ // the Material Tonal Palette defines hues from 0 to 1.
+ hsl[0] /= 360f;
+
+ // Find the palette that contains the closest color
TonalPalette palette = findTonalPalette(hsl[0]);
- // Fall back to population sort if we couldn't find a tonal palette
if (palette == null) {
Log.w(TAG, "Could not find a tonal palette!");
return null;
}
+ // Figure out what's the main color index in the optimal palette
int fitIndex = bestFit(palette, hsl[0], hsl[1], hsl[2]);
if (fitIndex == -1) {
Log.w(TAG, "Could not find best fit!");
return null;
}
+
+ // Generate the 10 colors palette by offsetting each one of them
float[] h = fit(palette.h, hsl[0], fitIndex,
Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY);
float[] s = fit(palette.s, hsl[1], fitIndex, 0.0f, 1.0f);
float[] l = fit(palette.l, hsl[2], fitIndex, 0.0f, 1.0f);
+ final int textInversionIndex = h.length - 3;
- hsl[0] = fract(h[0]) * 360.0f;
- hsl[1] = s[0];
- hsl[2] = l[0];
- int mainColor = ColorUtils.HSLToColor(hsl);
+ // best fit + a 2 colors offset
+ int primaryIndex = fitIndex;
+ int secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2);
+ int mainColor = getColorInt(primaryIndex, h, s, l);
+ int secondaryColor = getColorInt(secondaryIndex, h, s, l);
- hsl[0] = fract(h[1]) * 360.0f;
- hsl[1] = s[1];
- hsl[2] = l[1];
- int secondaryColor = ColorUtils.HSLToColor(hsl);
return new Pair<>(mainColor, secondaryColor);
}
+ private int getColorInt(int fitIndex, float[] h, float[] s, float[] l) {
+ mTmpHSL[0] = fract(h[fitIndex]) * 360.0f;
+ mTmpHSL[1] = s[fitIndex];
+ mTmpHSL[2] = l[fitIndex];
+ return ColorUtils.HSLToColor(mTmpHSL);
+ }
+
+ /**
+ * Checks if a given color exists in the blacklist
+ * @param hsl float array with 3 components (H 0..360, S 0..1 and L 0..1)
+ * @return true if color should be avoided
+ */
+ private boolean isBlacklisted(float[] hsl) {
+ for (ColorRange badRange: BLACKLISTED_COLORS) {
+ if (badRange.containsColor(hsl[0], hsl[1], hsl[2])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private static void populationSort(@NonNull List<Pair<Integer, Integer>> wallpaperColors) {
Collections.sort(wallpaperColors, new Comparator<Pair<Integer, Integer>>() {
@Override
@@ -160,7 +189,7 @@ public class ColorExtractionAlgorithm {
* @param index which index to calculate the delta against
* @param min minimum accepted value (clamp)
* @param max maximum accepted value (clamp)
- * @return
+ * @return new shifted palette
*/
private static float[] fit(float[] data, float v, int index, float min, float max) {
float[] fitData = new float[data.length];
@@ -272,44 +301,460 @@ public class ColorExtractionAlgorithm {
// Data definition of Material Design tonal palettes
// When the sort type is set to TONAL, these palettes are used to find
- // a best fist. Each palette is defined as 10 HSL colors
+ // a best fit. Each palette is defined as 22 HSL colors
private static final TonalPalette[] TONAL_PALETTES = {
- // Orange
new TonalPalette(
- new float[] { 0.028f, 0.042f, 0.053f, 0.061f, 0.078f, 0.1f, 0.111f, 0.111f, 0.111f, 0.111f },
- new float[] { 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f },
- new float[] { 0.5f, 0.53f, 0.54f, 0.55f, 0.535f, 0.52f, 0.5f, 0.63f, 0.75f, 0.85f }
+ new float[]{0.991f, 0.9833333333333333f, 0f, 0f, 0f, 0.01134380453752181f,
+ 0.015625000000000003f, 0.024193548387096798f, 0.027397260273972573f,
+ 0.017543859649122865f},
+ new float[]{1f, 1f, 1f, 1f, 0.8434782608695652f, 1f, 1f, 1f, 1f, 1f},
+ new float[]{0.2f, 0.27450980392156865f, 0.34901960784313724f,
+ 0.4235294117647059f, 0.5490196078431373f, 0.6254901960784314f,
+ 0.6862745098039216f, 0.7568627450980392f, 0.8568627450980393f,
+ 0.9254901960784314f}
),
- // Yellow
new TonalPalette(
- new float[] { 0.111f, 0.111f, 0.125f, 0.133f, 0.139f, 0.147f, 0.156f, 0.156f, 0.156f, 0.156f },
- new float[] { 1f, 0.942f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f },
- new float[] { 0.43f, 0.484f, 0.535f, 0.555f, 0.57f, 0.575f, 0.595f, 0.715f, 0.78f, 0.885f }
+ new float[]{0.6385767790262171f, 0.6301169590643275f, 0.6223958333333334f,
+ 0.6151079136690647f, 0.6065400843881856f, 0.5986964618249534f,
+ 0.5910746812386157f, 0.5833333333333334f, 0.5748031496062993f,
+ 0.5582010582010583f},
+ new float[]{1f, 1f, 0.9014084507042253f, 0.8128654970760234f,
+ 0.7979797979797981f, 0.7816593886462883f, 0.778723404255319f,
+ 1f, 1f, 1f},
+ new float[]{0.17450980392156862f, 0.2235294117647059f, 0.2784313725490196f,
+ 0.3352941176470588f, 0.388235294117647f, 0.44901960784313727f,
+ 0.5392156862745098f, 0.6509803921568628f, 0.7509803921568627f,
+ 0.8764705882352941f}
),
- // Green
new TonalPalette(
- new float[] { 0.325f, 0.336f, 0.353f, 0.353f, 0.356f, 0.356f, 0.356f, 0.356f, 0.356f, 0.356f },
- new float[] { 1f, 1f, 0.852f, 0.754f, 0.639f, 0.667f, 0.379f, 0.542f, 1f, 1f },
- new float[] { 0.06f, 0.1f, 0.151f, 0.194f, 0.25f, 0.312f, 0.486f, 0.651f, 0.825f, 0.885f }
+ new float[]{0.5669934640522876f, 0.5748031496062993f,
+ 0.5595238095238095f, 0.5473118279569893f, 0.5393258426966292f,
+ 0.5315955766192734f, 0.524031007751938f, 0.5154711673699016f,
+ 0.508080808080808f, 0.5f},
+ new float[]{1f, 1f, 1f, 1f, 1f, 1f, 0.8847736625514403f, 1f, 1f, 1f},
+ new float[]{0.2f, 0.24901960784313726f, 0.27450980392156865f,
+ 0.30392156862745096f, 0.34901960784313724f, 0.4137254901960784f,
+ 0.47647058823529415f, 0.5352941176470588f, 0.6764705882352942f, 0.8f}
),
- // Blue
new TonalPalette(
- new float[] { 0.631f, 0.603f, 0.592f, 0.586f, 0.572f, 0.544f, 0.519f, 0.519f, 0.519f, 0.519f },
- new float[] { 0.852f, 1f, 0.887f, 0.852f, 0.871f, 0.907f, 0.949f, 0.934f, 0.903f, 0.815f },
- new float[] { 0.34f, 0.38f, 0.482f, 0.497f, 0.536f, 0.571f, 0.608f, 0.696f, 0.794f, 0.892f }
+ new float[]{0.5082304526748972f, 0.5069444444444444f, 0.5f, 0.5f,
+ 0.5f, 0.48724954462659376f, 0.4800347222222222f,
+ 0.4755134281200632f, 0.4724409448818897f, 0.4671052631578947f},
+ new float[]{1f, 0.8888888888888887f, 0.9242424242424242f, 1f, 1f,
+ 0.8133333333333332f, 0.7868852459016393f, 1f, 1f, 1f},
+ new float[]{0.1588235294117647f, 0.21176470588235297f,
+ 0.25882352941176473f, 0.3f, 0.34901960784313724f,
+ 0.44117647058823534f, 0.5215686274509804f, 0.5862745098039216f,
+ 0.7509803921568627f, 0.8509803921568627f}
),
- // Purple
new TonalPalette(
- new float[] { 0.839f, 0.831f, 0.825f, 0.819f, 0.803f, 0.803f, 0.772f, 0.772f, 0.772f, 0.772f },
- new float[] { 1f, 1f, 1f, 1f, 1f, 1f, 0.769f, 0.701f, 0.612f, 0.403f },
- new float[] { 0.125f, 0.15f, 0.2f, 0.245f, 0.31f, 0.36f, 0.567f, 0.666f, 0.743f, 0.833f }
+ new float[]{0.3333333333333333f, 0.3333333333333333f,
+ 0.34006734006734f, 0.34006734006734f, 0.34006734006734f,
+ 0.34259259259259256f, 0.3475783475783476f, 0.34767025089605735f,
+ 0.3467741935483871f, 0.3703703703703704f},
+ new float[]{0.6703296703296703f, 0.728813559322034f,
+ 0.5657142857142856f, 0.5076923076923077f, 0.3944223107569721f,
+ 0.6206896551724138f, 0.8931297709923666f, 1f, 1f, 1f},
+ new float[]{0.1784313725490196f, 0.23137254901960785f,
+ 0.3431372549019608f, 0.38235294117647056f, 0.49215686274509807f,
+ 0.6588235294117647f, 0.7431372549019608f, 0.8176470588235294f,
+ 0.8784313725490196f, 0.9294117647058824f}
+ ),
+ new TonalPalette(
+ new float[]{0.162280701754386f, 0.15032679738562088f,
+ 0.15879265091863518f, 0.16236559139784948f, 0.17443868739205526f,
+ 0.17824074074074076f, 0.18674698795180725f,
+ 0.18692449355432778f, 0.1946778711484594f, 0.18604651162790695f},
+ new float[]{1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
+ new float[]{0.14901960784313725f, 0.2f, 0.24901960784313726f,
+ 0.30392156862745096f, 0.3784313725490196f, 0.4235294117647059f,
+ 0.48823529411764705f, 0.6450980392156863f, 0.7666666666666666f,
+ 0.8313725490196078f}
+ ),
+ new TonalPalette(
+ new float[]{0.10619469026548674f, 0.11924686192468618f,
+ 0.13046448087431692f, 0.14248366013071895f, 0.1506024096385542f,
+ 0.16220238095238093f, 0.16666666666666666f,
+ 0.16666666666666666f, 0.162280701754386f, 0.15686274509803924f},
+ new float[]{1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
+ new float[]{0.44313725490196076f, 0.46862745098039216f,
+ 0.47843137254901963f, 0.5f, 0.5117647058823529f,
+ 0.5607843137254902f, 0.6509803921568628f, 0.7509803921568627f,
+ 0.8509803921568627f, 0.9f}
+ ),
+ new TonalPalette(
+ new float[]{0.03561253561253561f, 0.05098039215686275f,
+ 0.07516339869281045f, 0.09477124183006536f, 0.1150326797385621f,
+ 0.134640522875817f, 0.14640522875816991f, 0.1582397003745319f,
+ 0.15773809523809523f, 0.15359477124183002f},
+ new float[]{1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
+ new float[]{0.4588235294117647f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
+ 0.5f, 0.6509803921568628f, 0.7803921568627451f, 0.9f}
+ ),
+ new TonalPalette(
+ new float[]{0.9596491228070175f, 0.9593837535014005f,
+ 0.9514767932489452f, 0.943859649122807f, 0.9396825396825397f,
+ 0.9395424836601307f, 0.9393939393939394f, 0.9362745098039216f,
+ 0.9754098360655739f, 0.9824561403508771f},
+ new float[]{0.84070796460177f, 0.8206896551724138f,
+ 0.7979797979797981f, 0.7661290322580644f, 0.9051724137931036f,
+ 1f, 1f, 1f, 1f, 1f},
+ new float[]{0.22156862745098038f, 0.2843137254901961f,
+ 0.388235294117647f, 0.48627450980392156f, 0.5450980392156863f,
+ 0.6f, 0.6764705882352942f, 0.8f, 0.8803921568627451f,
+ 0.9254901960784314f}
+ ),
+ new TonalPalette(
+ new float[]{0.841025641025641f, 0.8333333333333334f,
+ 0.8285256410256411f, 0.821522309711286f, 0.8083333333333333f,
+ 0.8046594982078853f, 0.8005822416302766f, 0.7842377260981912f,
+ 0.7771084337349398f, 0.7747747747747749f},
+ new float[]{1f, 1f, 1f, 1f, 1f, 1f, 1f,
+ 0.737142857142857f, 0.6434108527131781f, 0.46835443037974644f},
+ new float[]{0.12745098039215685f, 0.15490196078431373f,
+ 0.20392156862745098f, 0.24901960784313726f, 0.3137254901960784f,
+ 0.36470588235294116f, 0.44901960784313727f,
+ 0.6568627450980392f, 0.7470588235294118f, 0.8450980392156863f}
+ ),
+ new TonalPalette(
+ new float[]{0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f},
+ new float[]{0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f},
+ new float[]{0.14901960784313725f, 0.2f, 0.2980392156862745f, 0.4f,
+ 0.4980392156862745f, 0.6196078431372549f, 0.7176470588235294f,
+ 0.8196078431372549f, 0.9176470588235294f, 0.9490196078431372f}
+ ),
+ new TonalPalette(
+ new float[]{0.955952380952381f, 0.9681069958847737f,
+ 0.9760479041916167f, 0.9873563218390804f, 0f, 0f,
+ 0.009057971014492771f, 0.026748971193415648f,
+ 0.041666666666666616f, 0.05303030303030304f},
+ new float[]{1f, 0.8350515463917526f, 0.6929460580912863f,
+ 0.6387665198237885f, 0.6914893617021276f, 0.7583892617449666f,
+ 0.8070175438596495f, 0.9310344827586209f, 1f, 1f},
+ new float[]{0.27450980392156865f, 0.3803921568627451f,
+ 0.4725490196078432f, 0.5549019607843138f, 0.6313725490196078f,
+ 0.707843137254902f, 0.7764705882352941f, 0.8294117647058823f,
+ 0.9058823529411765f, 0.9568627450980391f}
+ ),
+ new TonalPalette(
+ new float[]{0.7514619883040936f, 0.7679738562091503f,
+ 0.7802083333333333f, 0.7844311377245509f, 0.796875f,
+ 0.8165618448637316f, 0.8487179487179487f, 0.8582375478927203f,
+ 0.8562091503267975f, 0.8666666666666667f},
+ new float[]{1f, 1f, 0.8163265306122449f, 0.6653386454183268f,
+ 0.7547169811320753f, 0.929824561403509f, 0.9558823529411766f,
+ 0.9560439560439562f, 1f, 1f},
+ new float[]{0.2235294117647059f, 0.3f, 0.38431372549019605f,
+ 0.492156862745098f, 0.5843137254901961f, 0.6647058823529411f,
+ 0.7333333333333334f, 0.8215686274509804f, 0.9f,
+ 0.9411764705882353f}
+ ),
+ new TonalPalette(
+ new float[]{0.6666666666666666f, 0.6666666666666666f,
+ 0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
+ 0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
+ 0.6666666666666666f, 0.6666666666666666f},
+ new float[]{0.24590163934426232f, 0.17880794701986752f,
+ 0.14606741573033713f, 0.13761467889908252f, 0.14893617021276592f,
+ 0.16756756756756758f, 0.20312500000000017f,
+ 0.26086956521739135f, 0.29999999999999966f, 0.5000000000000004f},
+ new float[]{0.2392156862745098f, 0.296078431372549f,
+ 0.34901960784313724f, 0.4274509803921569f, 0.5392156862745098f,
+ 0.6372549019607843f, 0.7490196078431373f, 0.8196078431372549f,
+ 0.8823529411764706f, 0.9372549019607843f}
+ ),
+ new TonalPalette(
+ new float[]{0.9678571428571429f, 0.9944812362030905f, 0f, 0f,
+ 0.0047348484848484815f, 0.00316455696202532f, 0f,
+ 0.9980392156862745f, 0.9814814814814816f, 0.9722222222222221f},
+ new float[]{1f, 0.7023255813953488f, 0.6638655462184874f,
+ 0.6521739130434782f, 0.7719298245614035f, 0.8315789473684211f,
+ 0.6867469879518071f, 0.7264957264957265f, 0.8181818181818182f,
+ 0.8181818181818189f},
+ new float[]{0.27450980392156865f, 0.4215686274509804f,
+ 0.4666666666666667f, 0.503921568627451f, 0.5529411764705883f,
+ 0.6274509803921569f, 0.6745098039215687f, 0.7705882352941176f,
+ 0.892156862745098f, 0.9568627450980391f}
+ ),
+ new TonalPalette(
+ new float[]{0.9052287581699346f, 0.9112021857923498f, 0.9270152505446624f,
+ 0.9343137254901961f, 0.9391534391534391f, 0.9437984496124031f,
+ 0.943661971830986f, 0.9438943894389439f, 0.9426229508196722f,
+ 0.9444444444444444f},
+ new float[]{1f, 0.8133333333333332f, 0.7927461139896375f, 0.7798165137614679f,
+ 0.7777777777777779f, 0.8190476190476191f, 0.8255813953488372f,
+ 0.8211382113821142f, 0.8133333333333336f, 0.8000000000000006f},
+ new float[]{0.2f, 0.29411764705882354f, 0.3784313725490196f,
+ 0.42745098039215684f, 0.4764705882352941f, 0.5882352941176471f,
+ 0.6627450980392157f, 0.7588235294117647f, 0.8529411764705882f,
+ 0.9411764705882353f}
),
- // Red
new TonalPalette(
- new float[] { 0.964f, 0.975f, 0.975f, 0.975f, 0.972f, 0.992f, 1.003f, 1.011f, 1.011f, 1.011f },
- new float[] { 0.869f, 0.802f, 0.739f, 0.903f, 1f, 1f, 1f, 1f, 1f, 1f },
- new float[] { 0.241f, 0.316f, 0.46f, 0.586f, 0.655f, 0.7f, 0.75f, 0.8f, 0.84f, 0.88f }
+ new float[]{0.6884057971014492f, 0.6974789915966387f, 0.7079889807162534f,
+ 0.7154471544715447f, 0.7217741935483872f, 0.7274143302180687f,
+ 0.7272727272727273f, 0.7258064516129031f, 0.7252252252252251f,
+ 0.7333333333333333f},
+ new float[]{0.8214285714285715f, 0.6878612716763006f, 0.6080402010050251f,
+ 0.5774647887323943f, 0.5391304347826086f, 0.46724890829694316f,
+ 0.4680851063829788f, 0.462686567164179f, 0.45679012345678977f,
+ 0.4545454545454551f},
+ new float[]{0.2196078431372549f, 0.33921568627450976f, 0.39019607843137255f,
+ 0.4176470588235294f, 0.45098039215686275f,
+ 0.5509803921568628f, 0.6313725490196078f, 0.7372549019607844f,
+ 0.8411764705882353f, 0.9352941176470588f}
+ ),
+ new TonalPalette(
+ new float[]{0.6470588235294118f, 0.6516666666666667f, 0.6464174454828661f,
+ 0.6441441441441442f, 0.6432748538011696f, 0.6416666666666667f,
+ 0.6402439024390243f, 0.6412429378531074f, 0.6435185185185186f,
+ 0.6428571428571429f},
+ new float[]{0.8095238095238095f, 0.6578947368421053f, 0.5721925133689839f,
+ 0.5362318840579711f, 0.5f, 0.4424778761061947f, 0.44086021505376327f,
+ 0.44360902255639095f,
+ 0.4499999999999997f, 0.4375000000000006f},
+ new float[]{0.16470588235294117f, 0.2980392156862745f, 0.36666666666666664f,
+ 0.40588235294117647f, 0.44705882352941173f,
+ 0.5568627450980392f, 0.6352941176470588f, 0.7392156862745098f,
+ 0.8431372549019608f, 0.9372549019607843f}
+ ),
+ new TonalPalette(
+ new float[]{0.46732026143790845f, 0.4718614718614719f, 0.4793650793650794f,
+ 0.48071625344352614f, 0.4829683698296837f, 0.484375f,
+ 0.4841269841269842f, 0.48444444444444457f, 0.48518518518518516f,
+ 0.4907407407407408f},
+ new float[]{1f, 1f, 1f, 1f, 1f, 0.6274509803921569f, 0.41832669322709176f,
+ 0.41899441340782106f, 0.4128440366972478f,
+ 0.4090909090909088f},
+ new float[]{0.1f, 0.15098039215686274f, 0.20588235294117646f,
+ 0.2372549019607843f, 0.26862745098039215f, 0.4f, 0.5078431372549019f,
+ 0.6490196078431372f, 0.7862745098039216f, 0.9137254901960784f}
+ ),
+ new TonalPalette(
+ new float[]{0.5444444444444444f, 0.5555555555555556f, 0.5555555555555556f,
+ 0.553763440860215f, 0.5526315789473684f, 0.5555555555555556f,
+ 0.5555555555555555f, 0.5555555555555556f, 0.5512820512820514f,
+ 0.5666666666666667f},
+ new float[]{0.24590163934426232f, 0.19148936170212766f, 0.1791044776119403f,
+ 0.18343195266272191f, 0.18446601941747576f,
+ 0.1538461538461539f, 0.15625000000000003f, 0.15328467153284678f,
+ 0.15662650602409653f, 0.151515151515151f},
+ new float[]{0.1196078431372549f, 0.1843137254901961f, 0.2627450980392157f,
+ 0.33137254901960783f, 0.403921568627451f, 0.5411764705882354f,
+ 0.6235294117647059f, 0.7313725490196079f, 0.8372549019607843f,
+ 0.9352941176470588f}
+ ),
+ new TonalPalette(
+ new float[]{0.022222222222222223f, 0.02469135802469136f, 0.031249999999999997f,
+ 0.03947368421052631f, 0.04166666666666668f,
+ 0.043650793650793655f, 0.04411764705882352f, 0.04166666666666652f,
+ 0.04444444444444459f, 0.05555555555555529f},
+ new float[]{0.33333333333333337f, 0.2783505154639175f, 0.2580645161290323f,
+ 0.25675675675675674f, 0.2528735632183908f, 0.17500000000000002f,
+ 0.15315315315315312f, 0.15189873417721522f,
+ 0.15789473684210534f, 0.15789473684210542f},
+ new float[]{0.08823529411764705f, 0.19019607843137254f, 0.2431372549019608f,
+ 0.2901960784313725f, 0.3411764705882353f, 0.47058823529411764f,
+ 0.5647058823529412f, 0.6901960784313725f, 0.8137254901960784f,
+ 0.9254901960784314f}
+ ),
+ new TonalPalette(
+ new float[]{0.050884955752212385f, 0.07254901960784313f, 0.0934640522875817f,
+ 0.10457516339869281f, 0.11699346405228758f,
+ 0.1255813953488372f, 0.1268939393939394f, 0.12533333333333332f,
+ 0.12500000000000003f, 0.12777777777777777f},
+ new float[]{1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
+ new float[]{0.44313725490196076f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5784313725490196f,
+ 0.6549019607843137f, 0.7549019607843137f, 0.8509803921568627f,
+ 0.9411764705882353f}
)
};
+ @SuppressWarnings("WeakerAccess")
+ static final ColorRange[] BLACKLISTED_COLORS = new ColorRange[] {
+
+ // Red
+ new ColorRange(
+ new Range<>(0f, 20f) /* H */,
+ new Range<>(0.7f, 1f) /* S */,
+ new Range<>(0.21f, 0.79f)) /* L */,
+ new ColorRange(
+ new Range<>(0f, 20f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.355f, 0.653f)),
+
+ // Red Orange
+ new ColorRange(
+ new Range<>(20f, 40f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.28f, 0.643f)),
+ new ColorRange(
+ new Range<>(20f, 40f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.414f, 0.561f)),
+ new ColorRange(
+ new Range<>(20f, 40f),
+ new Range<>(0f, 3f),
+ new Range<>(0.343f, 0.584f)),
+
+ // Orange
+ new ColorRange(
+ new Range<>(40f, 60f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.173f, 0.349f)),
+ new ColorRange(
+ new Range<>(40f, 60f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.233f, 0.427f)),
+ new ColorRange(
+ new Range<>(40f, 60f),
+ new Range<>(0f, 0.3f),
+ new Range<>(0.231f, 0.484f)),
+
+ // Yellow 60
+ new ColorRange(
+ new Range<>(60f, 80f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.488f, 0.737f)),
+ new ColorRange(
+ new Range<>(60f, 80f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.673f, 0.837f)),
+
+ // Yellow Green 80
+ new ColorRange(
+ new Range<>(80f, 100f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.469f, 0.61f)),
+
+ // Yellow green 100
+ new ColorRange(
+ new Range<>(100f, 120f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.388f, 0.612f)),
+ new ColorRange(
+ new Range<>(100f, 120f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.424f, 0.541f)),
+
+ // Green
+ new ColorRange(
+ new Range<>(120f, 140f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.375f, 0.52f)),
+ new ColorRange(
+ new Range<>(120f, 140f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.435f, 0.524f)),
+
+ // Green Blue 140
+ new ColorRange(
+ new Range<>(140f, 160f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.496f, 0.641f)),
+
+ // Seafoam
+ new ColorRange(
+ new Range<>(160f, 180f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.496f, 0.567f)),
+
+ // Cyan
+ new ColorRange(
+ new Range<>(180f, 200f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.52f, 0.729f)),
+
+ // Blue
+ new ColorRange(
+ new Range<>(220f, 240f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.396f, 0.571f)),
+ new ColorRange(
+ new Range<>(220f, 240f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.425f, 0.551f)),
+
+ // Blue Purple 240
+ new ColorRange(
+ new Range<>(240f, 260f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.418f, 0.639f)),
+ new ColorRange(
+ new Range<>(220f, 240f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.441f, 0.576f)),
+
+ // Blue Purple 260
+ new ColorRange(
+ new Range<>(260f, 280f),
+ new Range<>(0.3f, 1f), // Bigger range
+ new Range<>(0.461f, 0.553f)),
+
+ // Fuchsia
+ new ColorRange(
+ new Range<>(300f, 320f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.484f, 0.588f)),
+ new ColorRange(
+ new Range<>(300f, 320f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.48f, 0.592f)),
+
+ // Pink
+ new ColorRange(
+ new Range<>(320f, 340f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.466f, 0.629f)),
+
+ // Soft red
+ new ColorRange(
+ new Range<>(340f, 360f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.437f, 0.596f))
+ };
+
+ /**
+ * Representation of an HSL color range.
+ * <ul>
+ * <li>hsl[0] is Hue [0 .. 360)</li>
+ * <li>hsl[1] is Saturation [0...1]</li>
+ * <li>hsl[2] is Lightness [0...1]</li>
+ * </ul>
+ */
+ static class ColorRange {
+ private Range<Float> mHue;
+ private Range<Float> mSaturation;
+ private Range<Float> mLightness;
+
+ ColorRange(Range<Float> hue, Range<Float> saturation, Range<Float> lightness) {
+ mHue = hue;
+ mSaturation = saturation;
+ mLightness = lightness;
+ }
+
+ boolean containsColor(float h, float s, float l) {
+ if (!mHue.contains(h)) {
+ return false;
+ } else if (!mSaturation.contains(s)) {
+ return false;
+ } else if (!mLightness.contains(l)) {
+ return false;
+ }
+ return true;
+ }
+
+ float[] getCenter() {
+ return new float[] {
+ mHue.getLower() + (mHue.getUpper() - mHue.getLower()) / 2f,
+ mSaturation.getLower() + (mSaturation.getUpper() - mSaturation.getLower()) / 2f,
+ mLightness.getLower() + (mLightness.getUpper() - mLightness.getLower()) / 2f
+ };
+ }
+
+ @Override
+ public String toString() {
+ return String.format("H: %s, S: %s, L %s", mHue, mSaturation, mLightness);
+ }
+ }
+
}
diff --git a/src/com/android/launcher3/dynamicui/ColorExtractionService.java b/src/com/android/launcher3/dynamicui/ColorExtractionService.java
index 06a4dabdd..b9dd3b588 100644
--- a/src/com/android/launcher3/dynamicui/ColorExtractionService.java
+++ b/src/com/android/launcher3/dynamicui/ColorExtractionService.java
@@ -17,9 +17,9 @@
package com.android.launcher3.dynamicui;
import android.annotation.TargetApi;
-import android.app.IntentService;
import android.app.WallpaperManager;
-import android.content.Intent;
+import android.app.job.JobParameters;
+import android.app.job.JobService;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
@@ -27,6 +27,8 @@ import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
import android.support.v7.graphics.Palette;
import android.util.Log;
@@ -42,53 +44,84 @@ import java.io.IOException;
/**
* Extracts colors from the wallpaper, and saves results to {@link LauncherProvider}.
*/
-public class ColorExtractionService extends IntentService {
+public class ColorExtractionService extends JobService {
private static final String TAG = "ColorExtractionService";
+ private static final boolean DEBUG = false;
/** The fraction of the wallpaper to extract colors for use on the hotseat. */
private static final float HOTSEAT_FRACTION = 1f / 4;
- public ColorExtractionService() {
- super("ColorExtractionService");
- }
+ private HandlerThread mWorkerThread;
+ private Handler mWorkerHandler;
@Override
- protected void onHandleIntent(Intent intent) {
- WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
- int wallpaperId = ExtractionUtils.getWallpaperId(wallpaperManager);
-
- ExtractedColors extractedColors = new ExtractedColors();
- if (wallpaperManager.getWallpaperInfo() != null) {
- // We can't extract colors from live wallpapers, so just use the default color always.
- extractedColors.updateHotseatPalette(null);
+ public void onCreate() {
+ super.onCreate();
+ mWorkerThread = new HandlerThread("ColorExtractionService");
+ mWorkerThread.start();
+ mWorkerHandler = new Handler(mWorkerThread.getLooper());
+ }
- if (FeatureFlags.QSB_IN_HOTSEAT || FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
- extractedColors.updateWallpaperThemePalette(null);
- }
- } else {
- // We extract colors for the hotseat and status bar separately,
- // since they only consider part of the wallpaper.
- extractedColors.updateHotseatPalette(getHotseatPalette());
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mWorkerThread.quit();
+ }
- if (FeatureFlags.LIGHT_STATUS_BAR) {
- extractedColors.updateStatusBarPalette(getStatusBarPalette());
- }
+ @Override
+ public boolean onStartJob(final JobParameters jobParameters) {
+ if (DEBUG) Log.d(TAG, "onStartJob");
+ mWorkerHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ WallpaperManager wallpaperManager = WallpaperManager.getInstance(
+ ColorExtractionService.this);
+ int wallpaperId = ExtractionUtils.getWallpaperId(wallpaperManager);
+
+ ExtractedColors extractedColors = new ExtractedColors();
+ if (wallpaperManager.getWallpaperInfo() != null) {
+ // We can't extract colors from live wallpapers; always use the default color.
+ extractedColors.updateHotseatPalette(null);
+
+ if (FeatureFlags.QSB_IN_HOTSEAT || FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+ extractedColors.updateWallpaperThemePalette(null);
+ }
+ } else {
+ // We extract colors for the hotseat and status bar separately,
+ // since they only consider part of the wallpaper.
+ extractedColors.updateHotseatPalette(getHotseatPalette());
+
+ if (FeatureFlags.LIGHT_STATUS_BAR) {
+ extractedColors.updateStatusBarPalette(getStatusBarPalette());
+ }
+
+ if (FeatureFlags.QSB_IN_HOTSEAT || FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+ extractedColors.updateWallpaperThemePalette(getWallpaperPalette());
+ }
+ }
- if (FeatureFlags.QSB_IN_HOTSEAT || FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
- extractedColors.updateWallpaperThemePalette(getWallpaperPalette());
+ // Save the extracted colors and wallpaper id to LauncherProvider.
+ String colorsString = extractedColors.encodeAsString();
+ Bundle extras = new Bundle();
+ extras.putInt(LauncherSettings.Settings.EXTRA_WALLPAPER_ID, wallpaperId);
+ extras.putString(LauncherSettings.Settings.EXTRA_EXTRACTED_COLORS, colorsString);
+ getContentResolver().call(
+ LauncherSettings.Settings.CONTENT_URI,
+ LauncherSettings.Settings.METHOD_SET_EXTRACTED_COLORS_AND_WALLPAPER_ID,
+ null, extras);
+ jobFinished(jobParameters, false /* needsReschedule */);
+ if (DEBUG) Log.d(TAG, "job finished!");
}
- }
+ });
+ return true;
+ }
- // Save the extracted colors and wallpaper id to LauncherProvider.
- String colorsString = extractedColors.encodeAsString();
- Bundle extras = new Bundle();
- extras.putInt(LauncherSettings.Settings.EXTRA_WALLPAPER_ID, wallpaperId);
- extras.putString(LauncherSettings.Settings.EXTRA_EXTRACTED_COLORS, colorsString);
- getContentResolver().call(
- LauncherSettings.Settings.CONTENT_URI,
- LauncherSettings.Settings.METHOD_SET_EXTRACTED_COLORS_AND_WALLPAPER_ID,
- null, extras);
+ @Override
+ public boolean onStopJob(JobParameters jobParameters) {
+ if (DEBUG) Log.d(TAG, "onStopJob");
+ mWorkerHandler.removeCallbacksAndMessages(null);
+ return true;
}
@TargetApi(Build.VERSION_CODES.N)
diff --git a/src/com/android/launcher3/dynamicui/ExtractionUtils.java b/src/com/android/launcher3/dynamicui/ExtractionUtils.java
index 1cf5d55e0..92cb5dc08 100644
--- a/src/com/android/launcher3/dynamicui/ExtractionUtils.java
+++ b/src/com/android/launcher3/dynamicui/ExtractionUtils.java
@@ -18,8 +18,10 @@ package com.android.launcher3.dynamicui;
import android.annotation.TargetApi;
import android.app.WallpaperManager;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Build;
@@ -58,7 +60,11 @@ public class ExtractionUtils {
/** Starts the {@link ColorExtractionService} without checking the wallpaper id */
public static void startColorExtractionService(Context context) {
- context.startService(new Intent(context, ColorExtractionService.class));
+ JobScheduler jobScheduler = (JobScheduler) context.getSystemService(
+ Context.JOB_SCHEDULER_SERVICE);
+ jobScheduler.schedule(new JobInfo.Builder(Utilities.COLOR_EXTRACTION_JOB_ID,
+ new ComponentName(context, ColorExtractionService.class))
+ .setMinimumLatency(0).build());
}
private static boolean hasWallpaperIdChanged(Context context) {
diff --git a/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java b/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java
index ca85b6af1..e23f42f46 100644
--- a/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java
+++ b/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java
@@ -33,6 +33,7 @@ public class WallpaperColorInfo implements WallpaperManagerCompat.OnColorsChange
private int mMainColor;
private int mSecondaryColor;
private boolean mIsDark;
+ private boolean mSupportsDarkText;
private OnThemeChangeListener mOnThemeChangeListener;
private WallpaperColorInfo(Context context) {
@@ -54,12 +55,17 @@ public class WallpaperColorInfo implements WallpaperManagerCompat.OnColorsChange
return mIsDark;
}
+ public boolean supportsDarkText() {
+ return mSupportsDarkText;
+ }
+
@Override
public void onColorsChanged(WallpaperColorsCompat colors, int which) {
if (which == FLAG_SYSTEM) {
boolean wasDarkTheme = mIsDark;
+ boolean didSupportDarkText = mSupportsDarkText;
update(colors);
- notifyChange(wasDarkTheme != mIsDark);
+ notifyChange(wasDarkTheme != mIsDark || didSupportDarkText != mSupportsDarkText);
}
}
@@ -72,6 +78,7 @@ public class WallpaperColorInfo implements WallpaperManagerCompat.OnColorsChange
mMainColor = FALLBACK_COLOR;
mSecondaryColor = FALLBACK_COLOR;
}
+ mSupportsDarkText = wallpaperColors != null ? wallpaperColors.supportsDarkText() : false;
float[] hsl = new float[3];
ColorUtils.colorToHSL(mMainColor, hsl);
mIsDark = hsl[2] < 0.2f;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 236bf24a1..b793f491e 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -78,6 +78,7 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -933,7 +934,8 @@ public class FolderIcon extends FrameLayout implements FolderListener {
// If we are animating to the accepting state, animate the badge out.
float badgeScale = Math.max(0, mBadgeScale - mBackground.getScaleProgress());
mTempSpaceForBadgeOffset.set(getWidth() - mTempBounds.right, mTempBounds.top);
- mBadgeRenderer.draw(canvas, mBadgeInfo, mTempBounds,
+ IconPalette badgePalette = IconPalette.getFolderBadgePalette(getResources());
+ mBadgeRenderer.draw(canvas, badgePalette, mBadgeInfo, mTempBounds,
badgeScale, mTempSpaceForBadgeOffset);
}
}
diff --git a/src/com/android/launcher3/graphics/IconPalette.java b/src/com/android/launcher3/graphics/IconPalette.java
index 60ca7b236..6e01ed503 100644
--- a/src/com/android/launcher3/graphics/IconPalette.java
+++ b/src/com/android/launcher3/graphics/IconPalette.java
@@ -18,9 +18,12 @@ package com.android.launcher3.graphics;
import android.app.Notification;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.v4.graphics.ColorUtils;
import android.util.Log;
@@ -35,11 +38,12 @@ public class IconPalette {
private static final boolean DEBUG = false;
private static final String TAG = "IconPalette";
- public static final IconPalette FOLDER_ICON_PALETTE = new IconPalette(Color.parseColor("#BDC1C6"));
-
private static final float MIN_PRELOAD_COLOR_SATURATION = 0.2f;
private static final float MIN_PRELOAD_COLOR_LIGHTNESS = 0.6f;
+ private static IconPalette sBadgePalette;
+ private static IconPalette sFolderBadgePalette;
+
public final int dominantColor;
public final int backgroundColor;
public final ColorMatrixColorFilter backgroundColorMatrixFilter;
@@ -47,15 +51,19 @@ public class IconPalette {
public final int textColor;
public final int secondaryColor;
- private IconPalette(int color) {
+ private IconPalette(int color, boolean desaturateBackground) {
dominantColor = color;
- backgroundColor = dominantColor;
+ backgroundColor = desaturateBackground ? getMutedColor(dominantColor, 0.87f) : dominantColor;
ColorMatrix backgroundColorMatrix = new ColorMatrix();
Themes.setColorScaleOnMatrix(backgroundColor, backgroundColorMatrix);
backgroundColorMatrixFilter = new ColorMatrixColorFilter(backgroundColorMatrix);
- // Get slightly more saturated background color.
- Themes.setColorScaleOnMatrix(getMutedColor(dominantColor, 0.54f), backgroundColorMatrix);
- saturatedBackgroundColorMatrixFilter = new ColorMatrixColorFilter(backgroundColorMatrix);
+ if (!desaturateBackground) {
+ saturatedBackgroundColorMatrixFilter = backgroundColorMatrixFilter;
+ } else {
+ // Get slightly more saturated background color.
+ Themes.setColorScaleOnMatrix(getMutedColor(dominantColor, 0.54f), backgroundColorMatrix);
+ saturatedBackgroundColorMatrixFilter = new ColorMatrixColorFilter(backgroundColorMatrix);
+ }
textColor = getTextColorForBackground(backgroundColor);
secondaryColor = getLowContrastColor(backgroundColor);
}
@@ -78,8 +86,35 @@ public class IconPalette {
return result;
}
- public static IconPalette fromDominantColor(int dominantColor) {
- return new IconPalette(dominantColor);
+ public static IconPalette fromDominantColor(int dominantColor, boolean desaturateBackground) {
+ return new IconPalette(dominantColor, desaturateBackground);
+ }
+
+ /**
+ * Returns an IconPalette based on the badge_color in colors.xml.
+ * If that color is Color.TRANSPARENT, then returns null instead.
+ */
+ public static @Nullable IconPalette getBadgePalette(Resources resources) {
+ int badgeColor = resources.getColor(R.color.badge_color);
+ if (badgeColor == Color.TRANSPARENT) {
+ // Colors will be extracted per app icon, so a static palette won't work.
+ return null;
+ }
+ if (sBadgePalette == null) {
+ sBadgePalette = fromDominantColor(badgeColor, false);
+ }
+ return sBadgePalette;
+ }
+
+ /**
+ * Returns an IconPalette based on the folder_badge_color in colors.xml.
+ */
+ public static @NonNull IconPalette getFolderBadgePalette(Resources resources) {
+ if (sFolderBadgePalette == null) {
+ int badgeColor = resources.getColor(R.color.folder_badge_color);
+ sFolderBadgePalette = fromDominantColor(badgeColor, false);
+ }
+ return sFolderBadgePalette;
}
/**
@@ -134,43 +169,40 @@ public class IconPalette {
* This was copied from com.android.internal.util.NotificationColorUtil.
*/
private static int ensureTextContrast(int color, int bg) {
- return findContrastColor(color, bg, true, 4.5);
+ return findContrastColor(color, bg, 4.5);
}
/**
* Finds a suitable color such that there's enough contrast.
*
- * @param color the color to start searching from.
- * @param other the color to ensure contrast against. Assumed to be lighter than {@param color}
- * @param findFg if true, we assume {@param color} is a foreground, otherwise a background.
+ * @param fg the color to start searching from.
+ * @param bg the color to ensure contrast against.
* @param minRatio the minimum contrast ratio required.
* @return a color with the same hue as {@param color}, potentially darkened to meet the
* contrast ratio.
*
* This was copied from com.android.internal.util.NotificationColorUtil.
*/
- private static int findContrastColor(int color, int other, boolean findFg, double minRatio) {
- int fg = findFg ? color : other;
- int bg = findFg ? other : color;
+ private static int findContrastColor(int fg, int bg, double minRatio) {
if (ColorUtils.calculateContrast(fg, bg) >= minRatio) {
- return color;
+ return fg;
}
double[] lab = new double[3];
- ColorUtils.colorToLAB(findFg ? fg : bg, lab);
+ ColorUtils.colorToLAB(bg, lab);
+ double bgL = lab[0];
+ ColorUtils.colorToLAB(fg, lab);
+ double fgL = lab[0];
+ boolean isBgDark = bgL < 50;
- double low = 0, high = lab[0];
+ double low = isBgDark ? fgL : 0, high = isBgDark ? 100 : fgL;
final double a = lab[1], b = lab[2];
for (int i = 0; i < 15 && high - low > 0.00001; i++) {
final double l = (low + high) / 2;
- if (findFg) {
- fg = ColorUtils.LABToColor(l, a, b);
- } else {
- bg = ColorUtils.LABToColor(l, a, b);
- }
+ fg = ColorUtils.LABToColor(l, a, b);
if (ColorUtils.calculateContrast(fg, bg) > minRatio) {
- low = l;
+ if (isBgDark) high = l; else low = l;
} else {
- high = l;
+ if (isBgDark) low = l; else high = l;
}
}
return ColorUtils.LABToColor(low, a, b);
diff --git a/src/com/android/launcher3/graphics/IconShapeOverride.java b/src/com/android/launcher3/graphics/IconShapeOverride.java
index a0727fb4b..e2d1d50e4 100644
--- a/src/com/android/launcher3/graphics/IconShapeOverride.java
+++ b/src/com/android/launcher3/graphics/IconShapeOverride.java
@@ -15,13 +15,14 @@
*/
package com.android.launcher3.graphics;
+import static com.android.launcher3.Utilities.getDevicePrefs;
+
import android.annotation.TargetApi;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Build;
import android.os.SystemClock;
@@ -34,7 +35,6 @@ import android.text.TextUtils;
import android.util.Log;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherFiles;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -101,7 +101,7 @@ public class IconShapeOverride {
} catch (Exception e) {
Log.e(TAG, "Unable to override icon shape", e);
// revert value.
- prefs(context).edit().remove(KEY_PREFERENCE).apply();
+ getDevicePrefs(context).edit().remove(KEY_PREFERENCE).apply();
}
}
@@ -116,11 +116,7 @@ public class IconShapeOverride {
}
private static String getAppliedValue(Context context) {
- return prefs(context).getString(KEY_PREFERENCE, "");
- }
-
- private static SharedPreferences prefs(Context context) {
- return context.getSharedPreferences(LauncherFiles.DEVICE_PREFERENCES_KEY, 0);
+ return getDevicePrefs(context).getString(KEY_PREFERENCE, "");
}
public static void handlePreferenceUi(ListPreference preference) {
@@ -189,7 +185,7 @@ public class IconShapeOverride {
@Override
public void run() {
// Synchronously write the preference.
- prefs(mContext).edit().putString(KEY_PREFERENCE, mValue).commit();
+ getDevicePrefs(mContext).edit().putString(KEY_PREFERENCE, mValue).commit();
// Clear the icon cache.
LauncherAppState.getInstance(mContext).getIconCache().clear();
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 53521f22d..19e570247 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -266,9 +266,10 @@ public class LauncherIcons {
sOldBounds.set(icon.getBounds());
if (Utilities.isAtLeastO() && icon instanceof AdaptiveIconDrawable) {
- int offset = Math.min(left, top);
+ int offset = Math.max((int)(ShadowGenerator.BLUR_FACTOR * iconBitmapSize),
+ Math.min(left, top));
int size = Math.max(width, height);
- icon.setBounds(offset, offset, offset + size, offset + size);
+ icon.setBounds(offset, offset, size, size);
} else {
icon.setBounds(left, top, left+width, top+height);
}
diff --git a/src/com/android/launcher3/graphics/ShadowDrawable.java b/src/com/android/launcher3/graphics/ShadowDrawable.java
index 5f4fc6cc7..45c1b6afb 100644
--- a/src/com/android/launcher3/graphics/ShadowDrawable.java
+++ b/src/com/android/launcher3/graphics/ShadowDrawable.java
@@ -16,6 +16,8 @@
package com.android.launcher3.graphics;
+import android.annotation.TargetApi;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
@@ -26,7 +28,9 @@ import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.util.AttributeSet;
import com.android.launcher3.R;
@@ -40,6 +44,7 @@ import java.io.IOException;
/**
* A drawable which adds shadow around a child drawable.
*/
+@TargetApi(Build.VERSION_CODES.O)
public class ShadowDrawable extends Drawable {
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
@@ -99,6 +104,24 @@ public class ShadowDrawable extends Drawable {
return mState.mIntrinsicWidth;
}
+ @Override
+ public boolean canApplyTheme() {
+ return mState.canApplyTheme();
+ }
+
+ @Override
+ public void applyTheme(Resources.Theme t) {
+ if (mState.canApplyTheme()) {
+ // Workaround since ColorStateList does not expose applyTheme method
+ ColorDrawable cd = new ColorDrawable();
+ cd.setTintList(mState.mTintColor);
+ cd.applyTheme(t);
+
+ mState.mLastDrawnBitmap = null;
+ invalidateSelf();
+ }
+ }
+
private void regenerateBitmapCache() {
Bitmap bitmap = Bitmap.createBitmap(mState.mIntrinsicWidth, mState.mIntrinsicHeight,
Bitmap.Config.ARGB_8888);
@@ -109,6 +132,9 @@ public class ShadowDrawable extends Drawable {
d.setBounds(mState.mShadowSize, mState.mShadowSize,
mState.mIntrinsicWidth - mState.mShadowSize,
mState.mIntrinsicHeight - mState.mShadowSize);
+ if (mState.mTintColor != null) {
+ d.setTint(mState.mTintColor.getDefaultColor());
+ }
d.draw(canvas);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
@@ -146,6 +172,7 @@ public class ShadowDrawable extends Drawable {
R.styleable.ShadowDrawable_android_shadowColor, Color.BLACK);
mState.mShadowSize = a.getDimensionPixelSize(
R.styleable.ShadowDrawable_android_elevation, 0);
+ mState.mTintColor = a.getColorStateList(R.styleable.ShadowDrawable_android_tint);
mState.mIntrinsicHeight = d.getIntrinsicHeight() + 2 * mState.mShadowSize;
mState.mIntrinsicWidth = d.getIntrinsicWidth() + 2 * mState.mShadowSize;
@@ -165,6 +192,7 @@ public class ShadowDrawable extends Drawable {
int mShadowColor;
int mShadowSize;
+ ColorStateList mTintColor;
Bitmap mLastDrawnBitmap;
ConstantState mChildState;
@@ -178,5 +206,10 @@ public class ShadowDrawable extends Drawable {
public int getChangingConfigurations() {
return mChangingConfigurations;
}
+
+ @Override
+ public boolean canApplyTheme() {
+ return mTintColor != null;
+ }
}
}
diff --git a/src/com/android/launcher3/graphics/ShadowGenerator.java b/src/com/android/launcher3/graphics/ShadowGenerator.java
index 695015dcb..9ea11a7b2 100644
--- a/src/com/android/launcher3/graphics/ShadowGenerator.java
+++ b/src/com/android/launcher3/graphics/ShadowGenerator.java
@@ -37,10 +37,10 @@ public class ShadowGenerator {
// Percent of actual icon size
private static final float HALF_DISTANCE = 0.5f;
- private static final float BLUR_FACTOR = 0.5f/48;
+ public static final float BLUR_FACTOR = 0.5f/48;
// Percent of actual icon size
- private static final float KEY_SHADOW_DISTANCE = 1f/48;
+ public static final float KEY_SHADOW_DISTANCE = 1f/48;
private static final int KEY_SHADOW_ALPHA = 61;
private static final int AMBIENT_SHADOW_ALPHA = 30;
diff --git a/src/com/android/launcher3/graphics/TintedDrawableSpan.java b/src/com/android/launcher3/graphics/TintedDrawableSpan.java
index f72ce03f4..d7195750d 100644
--- a/src/com/android/launcher3/graphics/TintedDrawableSpan.java
+++ b/src/com/android/launcher3/graphics/TintedDrawableSpan.java
@@ -34,6 +34,7 @@ public class TintedDrawableSpan extends DynamicDrawableSpan {
super(ALIGN_BOTTOM);
mDrawable = context.getDrawable(resourceId);
mOldTint = 0;
+ mDrawable.setTint(0);
}
@Override
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index be93be4dc..d9c5143f2 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -91,11 +91,21 @@ public class BgDataModel {
public final Map<ShortcutKey, MutableInt> pinnedShortcutCounts = new HashMap<>();
/**
+ * True if the launcher has permission to access deep shortcuts.
+ */
+ public boolean hasShortcutHostPermission;
+
+ /**
* Maps all launcher activities to the id's of their shortcuts (if they have any).
*/
public final MultiHashMap<ComponentKey, String> deepShortcutMap = new MultiHashMap<>();
/**
+ * Entire list of widgets.
+ */
+ public final WidgetsModel widgetsModel = new WidgetsModel();
+
+ /**
* Clears all the data
*/
public synchronized void clear() {
diff --git a/src/com/android/launcher3/model/ExtendedModelTask.java b/src/com/android/launcher3/model/ExtendedModelTask.java
index 05419662b..080aaf54b 100644
--- a/src/com/android/launcher3/model/ExtendedModelTask.java
+++ b/src/com/android/launcher3/model/ExtendedModelTask.java
@@ -59,4 +59,15 @@ public abstract class ExtendedModelTask extends BaseModelUpdateTask {
}
});
}
+
+ public void bindUpdatedWidgets(BgDataModel dataModel) {
+ final MultiHashMap<PackageItemInfo, WidgetItem> widgets
+ = dataModel.widgetsModel.getWidgetsMap();
+ scheduleCallbackTask(new CallbackTask() {
+ @Override
+ public void execute(Callbacks callbacks) {
+ callbacks.bindAllWidgets(widgets);
+ }
+ });
+ }
}
diff --git a/src/com/android/launcher3/model/LoaderResults.java b/src/com/android/launcher3/model/LoaderResults.java
index 61fd356b8..28df64d39 100644
--- a/src/com/android/launcher3/model/LoaderResults.java
+++ b/src/com/android/launcher3/model/LoaderResults.java
@@ -24,6 +24,7 @@ import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.MainThreadExecutor;
@@ -58,6 +59,7 @@ public class LoaderResults {
private final BgDataModel mBgDataModel;
private final AllAppsList mBgAllAppsList;
private final int mPageToBindFirst;
+
private final WeakReference<Callbacks> mCallbacks;
public LoaderResults(LauncherAppState app, BgDataModel dataModel,
@@ -358,7 +360,6 @@ public class LoaderResults {
mUiExecutor.execute(r);
}
-
public void bindAllApps() {
// shallow copy
@SuppressWarnings("unchecked")
@@ -374,4 +375,18 @@ public class LoaderResults {
};
mUiExecutor.execute(r);
}
+
+ public void bindWidgets() {
+ final MultiHashMap<PackageItemInfo, WidgetItem> widgets
+ = mBgDataModel.widgetsModel.getWidgetsMap();
+ Runnable r = new Runnable() {
+ public void run() {
+ Callbacks callbacks = mCallbacks.get();
+ if (callbacks != null) {
+ callbacks.bindAllWidgets(widgets);
+ }
+ }
+ };
+ mUiExecutor.execute(r);
+ }
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
new file mode 100644
index 000000000..bcf516eed
--- /dev/null
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -0,0 +1,839 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.model;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.PackageInstaller;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.MutableInt;
+
+import com.android.launcher3.AllAppsList;
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.IconCache;
+import com.android.launcher3.InstallShortcutReceiver;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.FolderIconPreviewVerifier;
+import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.provider.ImportDataTask;
+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.LooperIdleLock;
+import com.android.launcher3.util.ManagedProfileHeuristic;
+import com.android.launcher3.util.MultiHashMap;
+import com.android.launcher3.util.PackageManagerHelper;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CancellationException;
+
+/**
+ * Runnable for the thread that loads the contents of the launcher:
+ * - workspace icons
+ * - widgets
+ * - all apps icons
+ * - deep shortcuts within apps
+ */
+public class LoaderTask implements Runnable {
+ private static final boolean DEBUG_LOADERS = false;
+ private static final String TAG = "LoaderTask";
+
+ private final LauncherAppState mApp;
+ private final AllAppsList mBgAllAppsList;
+ private final BgDataModel mBgDataModel;
+
+ private final LoaderResults mResults;
+
+ private final LauncherAppsCompat mLauncherApps;
+ private final UserManagerCompat mUserManager;
+ private final DeepShortcutManager mShortcutManager;
+ private final PackageInstallerCompat mPackageInstaller;
+ private final AppWidgetManagerCompat mAppWidgetManager;
+ private final IconCache mIconCache;
+
+ private boolean mStopped;
+
+ public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
+ LoaderResults results) {
+ mApp = app;
+ mBgAllAppsList = bgAllAppsList;
+ mBgDataModel = dataModel;
+ mResults = results;
+
+ mLauncherApps = LauncherAppsCompat.getInstance(mApp.getContext());
+ mUserManager = UserManagerCompat.getInstance(mApp.getContext());
+ mShortcutManager = DeepShortcutManager.getInstance(mApp.getContext());
+ mPackageInstaller = PackageInstallerCompat.getInstance(mApp.getContext());
+ mAppWidgetManager = AppWidgetManagerCompat.getInstance(mApp.getContext());
+ mIconCache = mApp.getIconCache();
+ }
+
+ private synchronized 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
+ // down.
+ LooperIdleLock idleLock = new LooperIdleLock(this, Looper.getMainLooper());
+ // Just in case mFlushingWorkerThread changes but we aren't woken up,
+ // wait no longer than 1sec at a time
+ while (!mStopped && idleLock.awaitLocked(1000));
+ }
+
+ private synchronized void verifyNotStopped() throws CancellationException {
+ if (mStopped) {
+ throw new CancellationException("Loader stopped");
+ }
+ }
+
+ public void run() {
+ synchronized (this) {
+ // Skip fast if we are already stopped.
+ if (mStopped) {
+ return;
+ }
+ }
+
+ try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
+ long now = 0;
+ if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
+ loadWorkspace();
+
+ verifyNotStopped();
+ if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace");
+ mResults.bindWorkspace();
+
+ // Take a break
+ if (DEBUG_LOADERS) {
+ Log.d(TAG, "step 1 completed, wait for idle");
+ now = SystemClock.uptimeMillis();
+ }
+ waitForIdle();
+ if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms");
+ verifyNotStopped();
+
+ // second step
+ if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps");
+ loadAllApps();
+
+ if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Binding all apps");
+ verifyNotStopped();
+ mResults.bindAllApps();
+
+ verifyNotStopped();
+ if (DEBUG_LOADERS) Log.d(TAG, "step 2.3: Update icon cache");
+ updateIconCache();
+
+ // Take a break
+ if (DEBUG_LOADERS) {
+ Log.d(TAG, "step 2 completed, wait for idle");
+ now = SystemClock.uptimeMillis();
+ }
+ waitForIdle();
+ if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms");
+ verifyNotStopped();
+
+ // third step
+ if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts");
+ loadDeepShortcuts();
+
+ verifyNotStopped();
+ if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts");
+ mResults.bindDeepShortcuts();
+
+ // 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");
+ mBgDataModel.widgetsModel.update(mApp, null);
+
+ verifyNotStopped();
+ if (DEBUG_LOADERS) Log.d(TAG, "step 4.2: Binding widgets");
+ mResults.bindWidgets();
+
+ transaction.commit();
+ } catch (CancellationException e) {
+ // Loader stopped, ignore
+ }
+ }
+
+ public synchronized void stopLocked() {
+ mStopped = true;
+ this.notify();
+ }
+
+ private void loadWorkspace() {
+ if (LauncherAppState.PROFILE_STARTUP) {
+ Trace.beginSection("Loading Workspace");
+ }
+
+ final Context context = mApp.getContext();
+ final ContentResolver contentResolver = context.getContentResolver();
+ final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
+ final boolean isSafeMode = pmHelper.isSafeMode();
+ final boolean isSdCardReady = Utilities.isBootCompleted();
+ final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>();
+
+ boolean clearDb = false;
+ try {
+ ImportDataTask.performImportIfPossible(context);
+ } catch (Exception e) {
+ // Migration failed. Clear workspace.
+ clearDb = true;
+ }
+
+ if (!clearDb && GridSizeMigrationTask.ENABLED &&
+ !GridSizeMigrationTask.migrateGridIfNeeded(context)) {
+ // Migration failed. Clear workspace.
+ clearDb = true;
+ }
+
+ if (clearDb) {
+ Log.d(TAG, "loadWorkspace: resetting launcher database");
+ LauncherSettings.Settings.call(contentResolver,
+ LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+ }
+
+ Log.d(TAG, "loadWorkspace: loading default favorites");
+ LauncherSettings.Settings.call(contentResolver,
+ LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
+
+ synchronized (mBgDataModel) {
+ mBgDataModel.clear();
+
+ final HashMap<String, Integer> installingPkgs =
+ mPackageInstaller.updateAndGetActiveSessionCache();
+ mBgDataModel.workspaceScreens.addAll(LauncherModel.loadWorkspaceScreensDb(context));
+
+ Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
+ final LoaderCursor c = new LoaderCursor(contentResolver.query(
+ LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
+
+ HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
+
+ try {
+ final int appWidgetIdIndex = c.getColumnIndexOrThrow(
+ LauncherSettings.Favorites.APPWIDGET_ID);
+ final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
+ LauncherSettings.Favorites.APPWIDGET_PROVIDER);
+ 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 optionsIndex = c.getColumnIndexOrThrow(
+ LauncherSettings.Favorites.OPTIONS);
+
+ final LongSparseArray<UserHandle> allUsers = c.allUsers;
+ final LongSparseArray<Boolean> quietMode = new LongSparseArray<>();
+ final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
+ for (UserHandle user : mUserManager.getUserProfiles()) {
+ long serialNo = mUserManager.getSerialNumberForUser(user);
+ allUsers.put(serialNo, user);
+ quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
+
+ boolean userUnlocked = mUserManager.isUserUnlocked(user);
+
+ // We can only query for shortcuts when the user is unlocked.
+ if (userUnlocked) {
+ List<ShortcutInfoCompat> pinnedShortcuts =
+ mShortcutManager.queryForPinnedShortcuts(null, user);
+ if (mShortcutManager.wasLastCallSuccess()) {
+ for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
+ shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
+ shortcut);
+ }
+ } else {
+ // Shortcut manager can fail due to some race condition when the
+ // lock state changes too frequently. For the purpose of the loading
+ // shortcuts, consider the user is still locked.
+ userUnlocked = false;
+ }
+ }
+ unlockedUsers.put(serialNo, userUnlocked);
+ }
+
+ ShortcutInfo info;
+ LauncherAppWidgetInfo appWidgetInfo;
+ Intent intent;
+ String targetPkg;
+
+ FolderIconPreviewVerifier verifier =
+ new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
+ while (!mStopped && c.moveToNext()) {
+ try {
+ if (c.user == null) {
+ // User has been deleted, remove the item.
+ c.markDeleted("User has been deleted");
+ continue;
+ }
+
+ 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:
+ intent = c.parseIntent();
+ if (intent == null) {
+ c.markDeleted("Invalid or null intent");
+ continue;
+ }
+
+ int disabledState = quietMode.get(c.serialNumber) ?
+ ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0;
+ ComponentName cn = intent.getComponent();
+ targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
+
+ 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) ||
+ mLauncherApps.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 (mLauncherApps.isActivityEnabledForProfile(cn, c.user)) {
+ // no special handling necessary for this item
+ c.markRestored();
+ } else {
+ if (c.hasRestoreFlag(ShortcutInfo.FLAG_AUTOINSTALL_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 {
+ c.markDeleted("Unable to find a launch target");
+ continue;
+ }
+ } else {
+ // The app is installed but the component is no
+ // longer available.
+ c.markDeleted("Invalid component removed: " + cn);
+ 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;
+ }
+ }
+
+ 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() &&
+ !verifier.isItemInPreview(c.getInt(rankIndex));
+
+ 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 = c.getAppShortcutInfo(
+ intent, allowMissingTarget, useLowResIcon);
+ } else if (c.itemType ==
+ LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+
+ 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.
+ 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 = c.loadSimpleShortcut();
+ info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
+ }
+ } else { // item type == ITEM_TYPE_SHORTCUT
+ info = c.loadSimpleShortcut();
+
+ // Shortcuts are only available on the primary profile
+ if (!TextUtils.isEmpty(targetPkg)
+ && pmHelper.isAppSuspended(targetPkg, c.user)) {
+ disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+ }
+
+ // App shortcuts that used to be automatically added to Launcher
+ // didn't always have the correct intent flags set, so do that
+ // here
+ if (intent.getAction() != null &&
+ intent.getCategories() != null &&
+ intent.getAction().equals(Intent.ACTION_MAIN) &&
+ intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
+ intent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ }
+ }
+
+ if (info != null) {
+ c.applyCommonProperties(info);
+
+ info.intent = intent;
+ info.rank = c.getInt(rankIndex);
+ info.spanX = 1;
+ info.spanY = 1;
+ info.isDisabled |= disabledState;
+ if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
+ info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
+ }
+
+ 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;
+ }
+ }
+
+ c.checkAndAddItem(info, mBgDataModel);
+ } else {
+ throw new RuntimeException("Unexpected null ShortcutInfo");
+ }
+ break;
+
+ case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
+ FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id);
+ c.applyCommonProperties(folderInfo);
+
+ // Do not trim the folder label, as is was set by the user.
+ folderInfo.title = c.getString(c.titleIndex);
+ folderInfo.spanX = 1;
+ folderInfo.spanY = 1;
+ folderInfo.options = c.getInt(optionsIndex);
+
+ // no special handling required for restored folders
+ c.markRestored();
+
+ c.checkAndAddItem(folderInfo, mBgDataModel);
+ break;
+
+ case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+ case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
+ // Read all Launcher-specific widget details
+ boolean customWidget = c.itemType ==
+ LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
+
+ int appWidgetId = c.getInt(appWidgetIdIndex);
+ String savedProvider = c.getString(appWidgetProviderIndex);
+
+ final ComponentName component =
+ ComponentName.unflattenFromString(savedProvider);
+
+ final boolean isIdValid = !c.hasRestoreFlag(
+ LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
+ final boolean wasProviderReady = !c.hasRestoreFlag(
+ LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY);
+
+ if (widgetProvidersMap == null) {
+ widgetProvidersMap = mAppWidgetManager.getAllProvidersMap();
+ }
+ final AppWidgetProviderInfo provider = widgetProvidersMap.get(
+ new ComponentKey(
+ ComponentName.unflattenFromString(savedProvider),
+ c.user));
+
+ final boolean isProviderReady = isValidProvider(provider);
+ if (!isSafeMode && !customWidget &&
+ wasProviderReady && !isProviderReady) {
+ c.markDeleted(
+ "Deleting widget that isn't installed anymore: "
+ + provider);
+ } else {
+ if (isProviderReady) {
+ appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
+ provider.provider);
+
+ // 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 = c.restoreFlag &
+ ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
+ if (!wasProviderReady) {
+ // If provider was not previously ready, update the
+ // status and UI flag.
+
+ // Id would be valid only if the widget restore broadcast was received.
+ if (isIdValid) {
+ status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+ } else {
+ status &= ~LauncherAppWidgetInfo
+ .FLAG_PROVIDER_NOT_READY;
+ }
+ }
+ appWidgetInfo.restoreStatus = status;
+ } else {
+ Log.v(TAG, "Widget restore pending id=" + c.id
+ + " appWidgetId=" + appWidgetId
+ + " status =" + c.restoreFlag);
+ appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
+ component);
+ appWidgetInfo.restoreStatus = c.restoreFlag;
+ Integer installProgress = installingPkgs.get(component.getPackageName());
+
+ 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) {
+ c.markDeleted("Unrestored widget removed: " + component);
+ continue;
+ }
+
+ appWidgetInfo.installProgress =
+ installProgress == null ? 0 : installProgress;
+ }
+ if (appWidgetInfo.hasRestoreFlag(
+ LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) {
+ appWidgetInfo.bindOptions = c.parseIntent();
+ }
+
+ c.applyCommonProperties(appWidgetInfo);
+ appWidgetInfo.spanX = c.getInt(spanXIndex);
+ appWidgetInfo.spanY = c.getInt(spanYIndex);
+ appWidgetInfo.user = c.user;
+
+ if (!c.isOnWorkspaceOrHotseat()) {
+ c.markDeleted("Widget found where container != " +
+ "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
+ continue;
+ }
+
+ if (!customWidget) {
+ String providerName =
+ appWidgetInfo.providerName.flattenToString();
+ if (!providerName.equals(savedProvider) ||
+ (appWidgetInfo.restoreStatus != c.restoreFlag)) {
+ c.updater()
+ .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
+ providerName)
+ .put(LauncherSettings.Favorites.RESTORED,
+ appWidgetInfo.restoreStatus)
+ .commit();
+ }
+ }
+
+ if (appWidgetInfo.restoreStatus !=
+ LauncherAppWidgetInfo.RESTORE_COMPLETED) {
+ String pkg = appWidgetInfo.providerName.getPackageName();
+ appWidgetInfo.pendingItemInfo = new PackageItemInfo(pkg);
+ appWidgetInfo.pendingItemInfo.user = appWidgetInfo.user;
+ mIconCache.getTitleAndIconForApp(
+ appWidgetInfo.pendingItemInfo, false);
+ }
+
+ c.checkAndAddItem(appWidgetInfo, mBgDataModel);
+ }
+ break;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Desktop items loading interrupted", e);
+ }
+ }
+ } finally {
+ Utilities.closeSilently(c);
+ }
+
+ // Break early if we've stopped loading
+ if (mStopped) {
+ mBgDataModel.clear();
+ return;
+ }
+
+ // Remove dead items
+ if (c.commitDeleted()) {
+ // Remove any empty folder
+ ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings
+ .call(contentResolver,
+ LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
+ .getSerializable(LauncherSettings.Settings.EXTRA_VALUE);
+ for (long folderId : deletedFolderIds) {
+ mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId));
+ mBgDataModel.folders.remove(folderId);
+ mBgDataModel.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<ShortcutKey> pendingShortcuts =
+ InstallShortcutReceiver.getPendingShortcuts(context);
+ for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
+ MutableInt numTimesPinned = mBgDataModel.pinnedShortcutCounts.get(key);
+ if ((numTimesPinned == null || numTimesPinned.value == 0)
+ && !pendingShortcuts.contains(key)) {
+ // Shortcut is pinned but doesn't exist on the workspace; unpin it.
+ mShortcutManager.unpinShortcut(key);
+ }
+ }
+
+ FolderIconPreviewVerifier verifier =
+ new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
+ // Sort the folder items and make sure all items in the preview are high resolution.
+ for (FolderInfo folder : mBgDataModel.folders) {
+ Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
+ verifier.setFolderInfo(folder);
+
+ int numItemsInPreview = 0;
+ for (ShortcutInfo info : folder.contents) {
+ if (info.usingLowResIcon
+ && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+ && verifier.isItemInPreview(info.rank)) {
+ mIconCache.getTitleAndIcon(info, false);
+ numItemsInPreview++;
+ }
+
+ if (numItemsInPreview >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+ break;
+ }
+ }
+ }
+
+ c.commitRestoredItems();
+ if (!isSdCardReady && !pendingPackages.isEmpty()) {
+ context.registerReceiver(
+ new SdCardAvailableReceiver(mApp, pendingPackages),
+ new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
+ null,
+ new Handler(LauncherModel.getWorkerLooper()));
+ }
+
+ // Remove any empty screens
+ ArrayList<Long> unusedScreens = new ArrayList<>(mBgDataModel.workspaceScreens);
+ for (ItemInfo item: mBgDataModel.itemsIdMap) {
+ long screenId = item.screenId;
+ if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
+ unusedScreens.contains(screenId)) {
+ unusedScreens.remove(screenId);
+ }
+ }
+
+ // If there are any empty screens remove them, and update.
+ if (unusedScreens.size() != 0) {
+ mBgDataModel.workspaceScreens.removeAll(unusedScreens);
+ LauncherModel.updateWorkspaceScreenOrder(context, mBgDataModel.workspaceScreens);
+ }
+ }
+ if (LauncherAppState.PROFILE_STARTUP) {
+ Trace.endSection();
+ }
+ }
+
+ private void updateIconCache() {
+ // Ignore packages which have a promise icon.
+ HashSet<String> packagesToIgnore = new HashSet<>();
+ synchronized (mBgDataModel) {
+ for (ItemInfo info : mBgDataModel.itemsIdMap) {
+ if (info instanceof ShortcutInfo) {
+ ShortcutInfo si = (ShortcutInfo) info;
+ if (si.isPromise() && si.getTargetComponent() != null) {
+ packagesToIgnore.add(si.getTargetComponent().getPackageName());
+ }
+ } else if (info instanceof LauncherAppWidgetInfo) {
+ LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info;
+ if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
+ packagesToIgnore.add(lawi.providerName.getPackageName());
+ }
+ }
+ }
+ }
+ mIconCache.updateDbIcons(packagesToIgnore);
+ }
+
+ private void loadAllApps() {
+ final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
+
+ final List<UserHandle> profiles = mUserManager.getUserProfiles();
+
+ // Clear the list of apps
+ mBgAllAppsList.clear();
+ for (UserHandle user : profiles) {
+ // Query for the set of apps
+ final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
+ final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
+ if (DEBUG_LOADERS) {
+ Log.d(TAG, "getActivityList took "
+ + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
+ Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
+ }
+ // Fail if we don't have any apps
+ // TODO: Fix this. Only fail for the current user.
+ if (apps == null || apps.isEmpty()) {
+ return;
+ }
+ boolean quietMode = mUserManager.isQuietModeEnabled(user);
+ // Create the ApplicationInfos
+ for (int i = 0; i < apps.size(); i++) {
+ LauncherActivityInfo app = apps.get(i);
+ // This builds the icon bitmaps.
+ mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
+ }
+
+ ManagedProfileHeuristic.onAllAppsLoaded(mApp.getContext(), apps, user);
+ }
+
+ if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
+ // get all active sessions and add them to the all apps list
+ for (PackageInstaller.SessionInfo info :
+ mPackageInstaller.getAllVerifiedSessions()) {
+ mBgAllAppsList.addPromiseApp(mApp.getContext(),
+ PackageInstallerCompat.PackageInstallInfo.fromInstallingState(info));
+ }
+ }
+
+ mBgAllAppsList.added = new ArrayList<>();
+ if (DEBUG_LOADERS) {
+ Log.d(TAG, "All apps loaded in in "
+ + (SystemClock.uptimeMillis() - loadTime) + "ms");
+ }
+ }
+
+ private void loadDeepShortcuts() {
+ mBgDataModel.deepShortcutMap.clear();
+ mBgDataModel.hasShortcutHostPermission = mShortcutManager.hasHostPermission();
+ if (mBgDataModel.hasShortcutHostPermission) {
+ for (UserHandle user : mUserManager.getUserProfiles()) {
+ if (mUserManager.isUserUnlocked(user)) {
+ List<ShortcutInfoCompat> shortcuts =
+ mShortcutManager.queryForAllShortcuts(user);
+ mBgDataModel.updateDeepShortcutMap(null, user, shortcuts);
+ }
+ }
+ }
+ }
+
+ public static boolean isValidProvider(AppWidgetProviderInfo provider) {
+ return (provider != null) && (provider.provider != null)
+ && (provider.provider.getPackageName() != null);
+ }
+}
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 8380f0136..46fea218f 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -44,6 +44,7 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
@@ -373,11 +374,9 @@ public class PackageUpdatedTask extends ExtendedModelTask {
} else if (Utilities.isAtLeastO() && mOp == OP_ADD) {
// Load widgets for the new package.
for (int i = 0; i < N; i++) {
- LauncherModel model = app.getModel();
- model.refreshAndBindWidgetsAndShortcuts(
- model.getCallback(), false /* bindFirst */,
- new PackageUserKey(packages[i], mUser) /* packageUser */);
+ dataModel.widgetsModel.update(app, new PackageUserKey(packages[i], mUser));
}
+ bindUpdatedWidgets(dataModel);
}
}
}
diff --git a/src/com/android/launcher3/model/SdCardAvailableReceiver.java b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
index bae5c73c1..3aedae69a 100644
--- a/src/com/android/launcher3/model/SdCardAvailableReceiver.java
+++ b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.util.MultiHashMap;
@@ -43,10 +44,10 @@ public class SdCardAvailableReceiver extends BroadcastReceiver {
private final Context mContext;
private final MultiHashMap<UserHandle, String> mPackages;
- public SdCardAvailableReceiver(LauncherModel model, Context context,
+ public SdCardAvailableReceiver(LauncherAppState app,
MultiHashMap<UserHandle, String> packages) {
- mModel = model;
- mContext = context;
+ mModel = app.getModel();
+ mContext = app.getContext();
mPackages = packages;
}
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 827675a83..ed900bf35 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -38,36 +38,26 @@ public class WidgetsModel {
private static final boolean DEBUG = false;
/* Map of widgets and shortcuts that are tracked per package. */
- private final MultiHashMap<PackageItemInfo, WidgetItem> mWidgetsList;
+ private final MultiHashMap<PackageItemInfo, WidgetItem> mWidgetsList = new MultiHashMap<>();
- private final IconCache mIconCache;
- private final AppFilter mAppFilter;
+ private AppFilter mAppFilter;
- public WidgetsModel(IconCache iconCache, AppFilter appFilter) {
- mIconCache = iconCache;
- mAppFilter = appFilter;
- mWidgetsList = new MultiHashMap<>();
- }
-
- public MultiHashMap<PackageItemInfo, WidgetItem> getWidgetsMap() {
- return mWidgetsList;
- }
-
- public boolean isEmpty() {
- return mWidgetsList.isEmpty();
+ public synchronized MultiHashMap<PackageItemInfo, WidgetItem> getWidgetsMap() {
+ return mWidgetsList.clone();
}
/**
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
*/
- public ArrayList<WidgetItem> update(Context context, @Nullable PackageUserKey packageUser) {
+ public void update(LauncherAppState app, @Nullable PackageUserKey packageUser) {
Preconditions.assertWorkerThread();
+ Context context = app.getContext();
final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
try {
PackageManager pm = context.getPackageManager();
- InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+ InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
// Widgets
AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(context);
@@ -81,7 +71,7 @@ public class WidgetsModel {
.getCustomShortcutActivityList(packageUser)) {
widgetsAndShortcuts.add(new WidgetItem(info));
}
- setWidgetsAndShortcuts(widgetsAndShortcuts, context, packageUser);
+ setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
} catch (Exception e) {
if (!FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(e)) {
// the returned value may be incomplete and will not be refreshed until the next
@@ -92,11 +82,12 @@ public class WidgetsModel {
throw e;
}
}
- return widgetsAndShortcuts;
+
+ app.getWidgetCache().removeObsoletePreviews(widgetsAndShortcuts, packageUser);
}
- private void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
- Context context, @Nullable PackageUserKey packageUser) {
+ private synchronized void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
+ LauncherAppState app, @Nullable PackageUserKey packageUser) {
if (DEBUG) {
Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
}
@@ -133,7 +124,7 @@ public class WidgetsModel {
}
}
- InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+ InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
UserHandle myUser = Process.myUserHandle();
// add and update.
@@ -152,6 +143,9 @@ public class WidgetsModel {
}
}
+ if (mAppFilter == null) {
+ mAppFilter = AppFilter.newInstance(app.getContext());
+ }
if (!mAppFilter.shouldShowApp(item.componentName)) {
if (DEBUG) {
Log.d(TAG, String.format("%s is filtered and not added to the widget tray.",
@@ -174,8 +168,9 @@ public class WidgetsModel {
}
// Update each package entry
+ IconCache iconCache = app.getIconCache();
for (PackageItemInfo p : tmpPackageItemInfos.values()) {
- mIconCache.getTitleAndIconForApp(p, true /* userLowResIcon */);
+ iconCache.getTitleAndIconForApp(p, true /* userLowResIcon */);
}
}
} \ No newline at end of file
diff --git a/src/com/android/launcher3/notification/NotificationFooterLayout.java b/src/com/android/launcher3/notification/NotificationFooterLayout.java
index 051c0333d..b83c9b95d 100644
--- a/src/com/android/launcher3/notification/NotificationFooterLayout.java
+++ b/src/com/android/launcher3/notification/NotificationFooterLayout.java
@@ -205,6 +205,7 @@ public class NotificationFooterLayout extends FrameLayout {
collapseFooter.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
+ ((ViewGroup) getParent()).findViewById(R.id.divider).setVisibility(GONE);
((ViewGroup) getParent()).removeView(NotificationFooterLayout.this);
}
});
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index 416d546ea..cc81b1121 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -34,6 +34,7 @@ import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
import com.android.launcher3.popup.PopupItemView;
import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.Themes;
import java.util.List;
@@ -98,7 +99,7 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
if (mNotificationHeaderTextColor == Notification.COLOR_DEFAULT) {
mNotificationHeaderTextColor =
IconPalette.resolveContrastColor(getContext(), palette.dominantColor,
- getResources().getColor(R.color.popup_header_background_color));
+ Themes.getAttrColor(getContext(), R.attr.popupColorPrimary));
}
mHeaderCount.setTextColor(mNotificationHeaderTextColor);
}
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index 0d6da77ee..9b8dd648f 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -87,11 +87,11 @@ public class NotificationMainView extends FrameLayout implements SwipeHelper.Cal
CharSequence title = mNotificationInfo.title;
CharSequence text = mNotificationInfo.text;
if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(text)) {
- mTitleView.setText(title);
- mTextView.setText(text);
+ mTitleView.setText(title.toString());
+ mTextView.setText(text.toString());
} else {
mTitleView.setMaxLines(2);
- mTitleView.setText(TextUtils.isEmpty(title) ? text : title);
+ mTitleView.setText(TextUtils.isEmpty(title) ? text.toString() : title.toString());
mTextView.setVisibility(GONE);
}
iconView.setBackground(mNotificationInfo.getIconForBackground(getContext(),
diff --git a/src/com/android/launcher3/pageindicators/CaretDrawable.java b/src/com/android/launcher3/pageindicators/CaretDrawable.java
index 416b2f0b5..5ade49728 100644
--- a/src/com/android/launcher3/pageindicators/CaretDrawable.java
+++ b/src/com/android/launcher3/pageindicators/CaretDrawable.java
@@ -38,6 +38,7 @@ public class CaretDrawable extends Drawable {
private Paint mCaretPaint = new Paint();
private Path mPath = new Path();
private final int mCaretSizePx;
+ private final boolean mUseShadow;
public CaretDrawable(Context context) {
final Resources res = context.getResources();
@@ -45,7 +46,7 @@ public class CaretDrawable extends Drawable {
final int strokeWidth = res.getDimensionPixelSize(R.dimen.all_apps_caret_stroke_width);
final int shadowSpread = res.getDimensionPixelSize(R.dimen.all_apps_caret_shadow_spread);
- mCaretPaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
+ mCaretPaint.setColor(Themes.getAttrColor(context, R.attr.workspaceTextColor));
mCaretPaint.setAntiAlias(true);
mCaretPaint.setStrokeWidth(strokeWidth);
mCaretPaint.setStyle(Paint.Style.STROKE);
@@ -60,6 +61,7 @@ public class CaretDrawable extends Drawable {
mShadowPaint.setStrokeCap(Paint.Cap.ROUND);
mShadowPaint.setStrokeJoin(Paint.Join.ROUND);
+ mUseShadow = !Themes.getAttrBoolean(context, R.attr.isWorkspaceDarkText);
mCaretSizePx = res.getDimensionPixelSize(R.dimen.all_apps_caret_size);
}
@@ -94,8 +96,9 @@ public class CaretDrawable extends Drawable {
mPath.moveTo(left, top + caretHeight * (1 - getNormalizedCaretProgress()));
mPath.lineTo(left + (width / 2), top + caretHeight * getNormalizedCaretProgress());
mPath.lineTo(left + width, top + caretHeight * (1 - getNormalizedCaretProgress()));
-
- canvas.drawPath(mPath, mShadowPaint);
+ if (mUseShadow) {
+ canvas.drawPath(mPath, mShadowPaint);
+ }
canvas.drawPath(mPath, mCaretPaint);
}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 152886e81..5463ef772 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -35,7 +35,6 @@ import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.IntDef;
-import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -50,7 +49,6 @@ import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
-import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
@@ -75,6 +73,7 @@ import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutsItemView;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.Themes;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -278,6 +277,9 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
int footerHeight = notificationFooterHasIcons ?
res.getDimensionPixelSize(R.dimen.notification_footer_height) : 0;
item.findViewById(R.id.footer).getLayoutParams().height = footerHeight;
+ if (notificationFooterHasIcons) {
+ mNotificationItemView.findViewById(R.id.divider).setVisibility(VISIBLE);
+ }
int roundedCorners = ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS;
if (shouldUnroundTopCorners) {
@@ -286,8 +288,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
if (shouldUnroundBottomCorners) {
roundedCorners &= ~ROUNDED_BOTTOM_CORNERS;
}
- int backgroundColor = ContextCompat.getColor(getContext(),
- R.color.notification_color_beneath);
+ int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorTertiary);
mNotificationItemView.setBackgroundWithCorners(backgroundColor, roundedCorners);
mNotificationItemView.getMainView().setAccessibilityDelegate(mAccessibilityDelegate);
@@ -312,9 +313,8 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
addView(item);
}
}
- int backgroundColor = ContextCompat.getColor(getContext(), mNotificationItemView == null
- ? R.color.popup_background_color
- : R.color.popup_header_background_color);
+ int backgroundColor = Themes.getAttrColor(mLauncher, mNotificationItemView == null
+ ? R.attr.popupColorPrimary : R.attr.popupColorSecondary);
mShortcutsItemView.setBackgroundWithCorners(backgroundColor, shortcutsItemRoundedCorners);
}
@@ -541,7 +541,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
// since the latter expects the arrow which hasn't been added yet.
PopupItemView itemAttachedToArrow = (PopupItemView)
(getChildAt(mIsAboveIcon ? getChildCount() - 1 : 0));
- arrowPaint.setColor(ContextCompat.getColor(mLauncher, R.color.popup_background_color));
+ arrowPaint.setColor(Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary));
// The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
int radius = getResources().getDimensionPixelSize(R.dimen.popup_arrow_corner_radius);
arrowPaint.setPathEffect(new CornerPathEffect(radius));
@@ -627,9 +627,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
BadgeInfo badgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo);
if (mNotificationItemView != null && badgeInfo != null) {
- IconPalette palette = mOriginalIcon.getIcon() instanceof FastBitmapDrawable
- ? ((FastBitmapDrawable) mOriginalIcon.getIcon()).getIconPalette()
- : null;
+ IconPalette palette = mOriginalIcon.getBadgePalette();
mNotificationItemView.updateHeader(badgeInfo.getNotificationCount(), palette);
}
}
@@ -668,8 +666,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
removeNotification.playSequentially(hideArrow, showArrow);
removeNotification.start();
if (mShortcutsItemView != null) {
- int backgroundColor = ContextCompat.getColor(getContext(),
- R.color.popup_background_color);
+ int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary);
// With notifications gone, all corners of shortcuts item should be rounded.
mShortcutsItemView.setBackgroundWithCorners(backgroundColor,
ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS);
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
index 3e4cd0192..314f24417 100644
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -16,6 +16,8 @@
package com.android.launcher3.provider;
+import static com.android.launcher3.Utilities.getDevicePrefs;
+
import android.content.ContentProviderOperation;
import android.content.ContentValues;
import android.content.Context;
@@ -36,7 +38,6 @@ import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
import com.android.launcher3.DefaultLayoutParser;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.LauncherFiles;
import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -377,10 +378,6 @@ public class ImportDataTask {
return false;
}
- private static SharedPreferences getDevicePrefs(Context c) {
- return c.getSharedPreferences(LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
- }
-
private static final int getMyHotseatLayoutId(Context context) {
return LauncherAppState.getIDP(context).numHotseatIcons <= 5
? R.xml.dw_phone_hotseat
diff --git a/src/com/android/launcher3/qsb/QsbBlockerView.java b/src/com/android/launcher3/qsb/QsbBlockerView.java
deleted file mode 100644
index 5379336de..000000000
--- a/src/com/android/launcher3/qsb/QsbBlockerView.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.qsb;
-
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.Workspace.OnStateChangeListener;
-import com.android.launcher3.Workspace.State;
-
-/**
- * A simple view used to show the region blocked by QSB during drag and drop.
- */
-public class QsbBlockerView extends View implements OnStateChangeListener {
-
- private static final int VISIBLE_ALPHA = 100;
-
- private final Paint mBgPaint;
-
- public QsbBlockerView(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mBgPaint.setColor(Color.WHITE);
- mBgPaint.setAlpha(0);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- Workspace w = Launcher.getLauncher(getContext()).getWorkspace();
- w.setOnStateChangeListener(this);
- prepareStateChange(w.getState(), null);
- }
-
- @Override
- public void prepareStateChange(State toState, AnimatorSet targetAnim) {
- int finalAlpha = getAlphaForState(toState);
- if (targetAnim == null) {
- mBgPaint.setAlpha(finalAlpha);
- invalidate();
- } else {
- ObjectAnimator anim = ObjectAnimator.ofArgb(mBgPaint, "alpha", finalAlpha);
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- invalidate();
- }
- });
- targetAnim.play(anim);
- }
- }
-
- private static int getAlphaForState(State state) {
- switch (state) {
- case SPRING_LOADED:
- case OVERVIEW:
- case OVERVIEW_HIDDEN:
- return VISIBLE_ALPHA;
- }
- return 0;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- canvas.drawPaint(mBgPaint);
- }
-}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutCache.java b/src/com/android/launcher3/shortcuts/ShortcutCache.java
index d4db96d31..5742d1de3 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutCache.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutCache.java
@@ -19,9 +19,8 @@ package com.android.launcher3.shortcuts;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.LruCache;
-
-import java.util.HashMap;
import java.util.List;
/**
@@ -31,18 +30,15 @@ import java.util.List;
*/
@TargetApi(Build.VERSION_CODES.N)
public class ShortcutCache {
- private static final String TAG = "ShortcutCache";
- private static final boolean LOGD = false;
-
private static final int CACHE_SIZE = 30; // Max number shortcuts we cache.
- private LruCache<ShortcutKey, ShortcutInfoCompat> mCachedShortcuts;
+ private final LruCache<ShortcutKey, ShortcutInfoCompat> mCachedShortcuts;
// We always keep pinned shortcuts in the cache.
- private HashMap<ShortcutKey, ShortcutInfoCompat> mPinnedShortcuts;
+ private final ArrayMap<ShortcutKey, ShortcutInfoCompat> mPinnedShortcuts;
public ShortcutCache() {
mCachedShortcuts = new LruCache<>(CACHE_SIZE);
- mPinnedShortcuts = new HashMap<>();
+ mPinnedShortcuts = new ArrayMap<>();
}
/**
diff --git a/src/com/android/launcher3/util/MultiStateAlphaController.java b/src/com/android/launcher3/util/MultiStateAlphaController.java
deleted file mode 100644
index 956fc9eba..000000000
--- a/src/com/android/launcher3/util/MultiStateAlphaController.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-
-import java.util.Arrays;
-
-/**
- * A utility class which divides the alpha for a view across multiple states.
- */
-public class MultiStateAlphaController {
-
- private final View mTargetView;
- private final float[] mAlphas;
- private final AccessibilityManager mAm;
- private int mZeroAlphaListenerCount = 0;
-
- public MultiStateAlphaController(View view, int stateCount) {
- mTargetView = view;
- mAlphas = new float[stateCount];
- Arrays.fill(mAlphas, 1);
-
- mAm = (AccessibilityManager) view.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- }
-
- public void setAlphaAtIndex(float alpha, int index) {
- mAlphas[index] = alpha;
- updateAlpha();
- }
-
- private void updateAlpha() {
- // Only update the alpha if no zero-alpha animation is running.
- if (mZeroAlphaListenerCount > 0) {
- return;
- }
- float finalAlpha = 1;
- for (float a : mAlphas) {
- finalAlpha = finalAlpha * a;
- }
- mTargetView.setAlpha(finalAlpha);
- mTargetView.setVisibility(finalAlpha > 0 ? View.VISIBLE
- : (mAm.isEnabled() ? View.GONE : View.INVISIBLE));
- }
-
- /**
- * Returns an animator which changes the alpha at the index {@param index}
- * to {@param finalAlpha}. Alphas at other index are not affected.
- */
- public Animator animateAlphaAtIndex(float finalAlpha, final int index) {
- final ValueAnimator anim;
-
- if (Float.compare(finalAlpha, mAlphas[index]) == 0) {
- // Return a dummy animator to avoid null checks.
- anim = ValueAnimator.ofFloat(0, 0);
- } else {
- ValueAnimator animator = ValueAnimator.ofFloat(mAlphas[index], finalAlpha);
- animator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- float value = (Float) valueAnimator.getAnimatedValue();
- setAlphaAtIndex(value, index);
- }
- });
- anim = animator;
- }
-
- if (Float.compare(finalAlpha, 0f) == 0) {
- // In case when any channel is animating to 0, and the current alpha is also 0, do not
- // update alpha of the target view while the animation is running.
- // We special case '0' because if any channel is set to 0, values of other
- // channels do not matter.
- anim.addListener(new ZeroAlphaAnimatorListener());
- }
- return anim;
- }
-
- private class ZeroAlphaAnimatorListener extends AnimatorListenerAdapter {
-
- private boolean mStartedAtZero = false;
-
- @Override
- public void onAnimationStart(Animator animation) {
- mStartedAtZero = Float.compare(mTargetView.getAlpha(), 0f) == 0;
- if (mStartedAtZero) {
- mZeroAlphaListenerCount++;
- mTargetView.setAlpha(0);
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mStartedAtZero) {
- mZeroAlphaListenerCount--;
- updateAlpha();
- }
- }
- }
-}
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index 9cf45b0c7..89597b9ed 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.ColorMatrix;
+import android.graphics.drawable.Drawable;
/**
* Various utility methods associated with theming.
@@ -44,6 +45,13 @@ public class Themes {
return value;
}
+ public static Drawable getAttrDrawable(Context context, int attr) {
+ TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
+ Drawable value = ta.getDrawable(0);
+ ta.recycle();
+ return value;
+ }
+
/**
* Returns the alpha corresponding to the theme attribute {@param attr}, in the range [0, 255].
*/