Merge "Consume work mode API" into ub-launcher3-master
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
index 435d57e..410a36f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
@@ -29,7 +29,6 @@
import android.support.animation.SpringAnimation;
import android.util.Log;
import android.view.MotionEvent;
-import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
@@ -42,7 +41,6 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
@@ -50,6 +48,10 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.FloatRange;
import com.android.launcher3.util.TouchController;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.RecentsView;
+import com.android.quickstep.TouchInteractionService;
+import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
import java.util.ArrayList;
@@ -86,6 +88,8 @@
private static final int FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE = 1 << 0;
private static final int FLAG_OVERVIEW_DISABLED_FLING = 1 << 1;
private static final int FLAG_OVERVIEW_DISABLED_CANCEL_STATE = 1 << 2;
+ private static final int FLAG_RECENTS_PLAN_LOADING = 1 << 3;
+ private static final int FLAG_OVERVIEW_DISABLED = 1 << 4;
private final Launcher mLauncher;
private final SwipeDetector mDetector;
@@ -98,9 +102,10 @@
private TaggedAnimatorSetBuilder mTaggedAnimatorSetBuilder;
private AnimatorSet mQuickOverviewAnimation;
private boolean mAnimatingToOverview;
- private TwoStateAnimationController mTwoStateAnimationController;
+ private CroppedAnimationController mCroppedAnimationController;
private AnimatorPlaybackController mCurrentAnimation;
+ private LauncherState mFromState;
private LauncherState mToState;
private float mStartProgress;
@@ -240,11 +245,27 @@
+ MAX_PROGRESS_TO_OVERVIEW - MIN_PROGRESS_TO_OVERVIEW;
// Build current animation
+ mFromState = mLauncher.getStateManager().getState();
mToState = mLauncher.isInState(ALL_APPS) ? NORMAL : ALL_APPS;
mTaggedAnimatorSetBuilder = new TaggedAnimatorSetBuilder();
mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(
mToState, mTaggedAnimatorSetBuilder, maxAccuracy);
+ if (TouchInteractionService.isConnected()) {
+ // Load recents plan
+ RecentsModel recentsModel = RecentsModel.getInstance(mLauncher);
+ if (recentsModel.getLastLoadPlan() != null) {
+ onRecentsPlanLoaded(recentsModel.getLastLoadPlan());
+ } else {
+ mDragPauseDetector.addDisabledFlags(FLAG_RECENTS_PLAN_LOADING);
+ }
+ // Reload again so that we get the latest list
+ // TODO: Use callback instead of polling everytime
+ recentsModel.loadTasks(-1, this::onRecentsPlanLoaded);
+ } else {
+ mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED);
+ }
+
mCurrentAnimation.getTarget().addListener(this);
mStartProgress = 0;
mProgressMultiplier = (mLauncher.isInState(ALL_APPS) ? 1 : -1) / range;
@@ -262,6 +283,14 @@
}
}
+ private void onRecentsPlanLoaded(RecentsTaskLoadPlan plan) {
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ recentsView.update(plan);
+ recentsView.initToPage(0);
+
+ mDragPauseDetector.clearDisabledFlags(FLAG_RECENTS_PLAN_LOADING);
+ }
+
private float getShiftRange() {
return mLauncher.getAllAppsController().getShiftRange();
}
@@ -287,16 +316,11 @@
@Override
public void onDragEnd(float velocity, boolean fling) {
- if (!fling && mDragPauseDetector.isEnabled() && mDragPauseDetector.isTriggered()) {
- snapToOverview(velocity);
- return;
- }
-
mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_FLING);
final long animationDuration;
final int logAction;
- final LauncherState targetState;
+ LauncherState targetState;
final float progress = mCurrentAnimation.getProgressFraction();
if (fling) {
@@ -317,7 +341,7 @@
targetState = mToState;
animationDuration = SwipeDetector.calculateDuration(velocity, 1 - progress);
} else {
- targetState = mToState == ALL_APPS ? NORMAL : ALL_APPS;
+ targetState = mFromState;
animationDuration = SwipeDetector.calculateDuration(velocity, progress);
}
}
@@ -328,7 +352,13 @@
h.animateToFinalPosition(0 /* pos */, 1 /* startValue */);
}
}
- mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(targetState, logAction));
+ mCurrentAnimation.setEndAction(() -> {
+ LauncherState finalState = targetState;
+ if (mDragPauseDetector.isTriggered() && targetState == NORMAL) {
+ finalState = OVERVIEW;
+ }
+ onSwipeInteractionCompleted(finalState, logAction);
+ });
float nextFrameProgress = Utilities.boundToRange(
progress + velocity * SINGLE_FRAME_MS / getShiftRange(), 0f, 1f);
@@ -341,7 +371,7 @@
}
private void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
- if (targetState == mToState) {
+ if (targetState != mFromState) {
// Transition complete. log the action
mLauncher.getUserEventDispatcher().logActionOnContainer(logAction,
mToState == ALL_APPS ? Direction.UP : Direction.DOWN,
@@ -354,33 +384,6 @@
mLauncher.getStateManager().goToState(targetState, false /* animated */);
}
- private void snapToOverview(float velocity) {
- mAnimatingToOverview = true;
-
- final float progress = mCurrentAnimation.getProgressFraction();
- float endProgress = mToState == NORMAL ? 1f : 0f;
- long animationDuration = SwipeDetector.calculateDuration(
- velocity, Math.abs(endProgress - progress));
- float nextFrameProgress = Utilities.boundToRange(
- progress + velocity * SINGLE_FRAME_MS / getShiftRange(), 0f, 1f);
-
- mCurrentAnimation.setEndAction(() -> {
- // TODO: Add logging
- clearState();
- mLauncher.getStateManager().goToState(OVERVIEW, true /* animated */);
- });
-
- if (mTwoStateAnimationController != null) {
- mTwoStateAnimationController.goBackToStart(endProgress);
- }
-
- ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
- anim.setFloatValues(nextFrameProgress, endProgress);
- anim.setDuration(animationDuration);
- anim.setInterpolator(scrollInterpolatorForVelocity(velocity));
- anim.start();
- }
-
private void onDragPauseDetected() {
final ValueAnimator twoStepAnimator = ValueAnimator.ofFloat(0, 1);
twoStepAnimator.setDuration(mCurrentAnimation.getDuration());
@@ -409,33 +412,29 @@
mQuickOverviewAnimation.start();
}
- private void onQuickOverviewAnimationComplete(ValueAnimator twoStepAnimator) {
+ private void onQuickOverviewAnimationComplete(ValueAnimator animator) {
if (mAnimatingToOverview) {
return;
}
- // The remaining state handlers are on the OVERVIEW state. Create two animations, one
- // towards the NORMAL state and one towards ALL_APPS state and control them based on the
- // swipe progress.
+ // For the remainder to the interaction, the user can either go to the ALL_APPS state or
+ // the OVERVIEW state.
+ // The remaining state handlers are on the OVERVIEW state. Create one animation towards the
+ // ALL_APPS state and only call it when the user moved above the current range.
AnimationConfig config = new AnimationConfig();
config.duration = (long) (2 * getShiftRange());
config.userControlled = true;
- LauncherState fromState = mToState == ALL_APPS ? NORMAL : ALL_APPS;
- AnimatorSetBuilder builderToTargetState = new AnimatorSetBuilder();
- AnimatorSetBuilder builderToSourceState = new AnimatorSetBuilder();
-
+ AnimatorSetBuilder builderToAllAppsState = new AnimatorSetBuilder();
StateHandler[] handlers = mLauncher.getStateManager().getStateHandlers();
for (int i = OTHER_HANDLERS_START_INDEX; i < handlers.length; i++) {
- handlers[i].setStateWithAnimation(mToState, builderToTargetState, config);
- handlers[i].setStateWithAnimation(fromState, builderToSourceState, config);
+ handlers[i].setStateWithAnimation(ALL_APPS, builderToAllAppsState, config);
}
- mTwoStateAnimationController = new TwoStateAnimationController(
- AnimatorPlaybackController.wrap(builderToSourceState.build(), config.duration),
- AnimatorPlaybackController.wrap(builderToTargetState.build(), config.duration),
- twoStepAnimator.getAnimatedFraction());
- twoStepAnimator.addUpdateListener(mTwoStateAnimationController);
+ mCroppedAnimationController = new CroppedAnimationController(
+ AnimatorPlaybackController.wrap(builderToAllAppsState.build(), config.duration),
+ new FloatRange(animator.getAnimatedFraction(), mToState == ALL_APPS ? 1 : 0));
+ animator.addUpdateListener(mCroppedAnimationController);
}
private void clearState() {
@@ -450,69 +449,49 @@
mQuickOverviewAnimation.cancel();
mQuickOverviewAnimation = null;
}
- mTwoStateAnimationController = null;
+ mCroppedAnimationController = null;
mAnimatingToOverview = false;
mDetector.finishedScrolling();
}
/**
- * {@link AnimatorUpdateListener} which interpolates two animations based the progress
+ * {@link AnimatorUpdateListener} which controls another animation for a fraction of range
*/
- private static class TwoStateAnimationController implements AnimatorUpdateListener {
+ private static class CroppedAnimationController implements AnimatorUpdateListener {
- private final AnimatorPlaybackController mControllerTowardsStart;
- private final AnimatorPlaybackController mControllerTowardsEnd;
+ private final AnimatorPlaybackController mTarget;
+ private final FloatRange mRange;
- private Interpolator mInterpolator = Interpolators.LINEAR;
- private float mStartFraction;
- private float mLastFraction;
-
- TwoStateAnimationController(AnimatorPlaybackController controllerTowardsStart,
- AnimatorPlaybackController controllerTowardsEnd, float startFraction) {
- mControllerTowardsStart = controllerTowardsStart;
- mControllerTowardsEnd = controllerTowardsEnd;
- mLastFraction = mStartFraction = startFraction;
+ CroppedAnimationController(AnimatorPlaybackController target, FloatRange range) {
+ mTarget = target;
+ mRange = range;
}
+
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
- mLastFraction = mInterpolator.getInterpolation(valueAnimator.getAnimatedFraction());
- if (mLastFraction > mStartFraction) {
- if (mStartFraction >= 1) {
- mControllerTowardsEnd.setPlayFraction(0);
- } else {
- mControllerTowardsEnd.setPlayFraction(
- (mLastFraction - mStartFraction) / (1 - mStartFraction));
- }
- } else {
- if (mStartFraction <= 0) {
- mControllerTowardsStart.setPlayFraction(0);
- } else {
- mControllerTowardsStart.setPlayFraction(
- (mStartFraction - mLastFraction) / mStartFraction);
- }
- }
- }
+ float fraction = valueAnimator.getAnimatedFraction();
- /**
- * Changes the interpolator such that from this point ({@link #mLastFraction}), the
- * animation run towards {@link #mStartFraction}. This allows us to animate the UI back
- * to the original point.
- * @param endFraction expected end point for this animation. Should either be 0 or 1.
- */
- public void goBackToStart(float endFraction) {
- if (mLastFraction == mStartFraction || mLastFraction == endFraction) {
- mInterpolator = (v) -> mStartFraction;
- } else if (mLastFraction > mStartFraction && endFraction < mStartFraction) {
- mInterpolator = (v) -> Math.max(v, mStartFraction);
- } else if (mLastFraction < mStartFraction && endFraction > mStartFraction) {
- mInterpolator = (v) -> Math.min(mStartFraction, v);
+ if (mRange.start < mRange.end) {
+ if (fraction <= mRange.start) {
+ mTarget.setPlayFraction(0);
+ } else if (fraction >= mRange.end) {
+ mTarget.setPlayFraction(1);
+ } else {
+ mTarget.setPlayFraction((fraction - mRange.start) / (mRange.end - mRange.start));
+ }
+ } else if (mRange.start > mRange.end) {
+ if (fraction >= mRange.start) {
+ mTarget.setPlayFraction(0);
+ } else if (fraction <= mRange.end) {
+ mTarget.setPlayFraction(1);
+ } else {
+ mTarget.setPlayFraction((fraction - mRange.start) / (mRange.end - mRange.start));
+ }
} else {
- final float start = mLastFraction;
- final float range = endFraction - mLastFraction;
- mInterpolator = (v) ->
- SwipeDetector.interpolate(start, mStartFraction, (v - start) / range);
+ // mRange.start == mRange.end
+ mTarget.setPlayFraction(0);
}
}
}
diff --git a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
index 095b445..09fd8f0 100644
--- a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
+++ b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
@@ -44,6 +44,7 @@
import com.android.launcher3.states.InternalStateHandler;
import com.android.launcher3.uioverrides.RecentsViewStateController;
import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.views.AllAppsScrim;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
@@ -97,6 +98,7 @@
private RecentsView mRecentsView;
private RecentsViewStateController mStateController;
private Hotseat mHotseat;
+ private AllAppsScrim mAllAppsScrim;
private RecentsTaskLoadPlan mLoadPlan;
private boolean mLauncherReady;
@@ -182,6 +184,7 @@
mRecentsView = mLauncher.getOverviewPanel();
mStateController = mRecentsView.getStateController();
mHotseat = mLauncher.getHotseat();
+ mAllAppsScrim = mLauncher.findViewById(R.id.all_apps_scrim);
// Optimization
mLauncher.getAppsView().setVisibility(View.GONE);
@@ -222,7 +225,9 @@
float shift = mCurrentShift.value * mActivityMultiplier.value;
int hotseatSize = getHotseatSize();
- mHotseat.setTranslationY((1 - shift) * hotseatSize);
+ float hotseatTranslation = (1 - shift) * hotseatSize;
+ mHotseat.setTranslationY(hotseatTranslation);
+ mAllAppsScrim.setTranslationY(hotseatTranslation);
mRectEvaluator.evaluate(shift, mSourceRect, mTargetRect);
@@ -324,6 +329,7 @@
private void cleanupLauncher() {
// TODO: These should be done as part of ActivityOptions#OnAnimationStarted
mHotseat.setTranslationY(0);
+ mAllAppsScrim.setTranslationY(0);
mLauncher.setOnResumeCallback(() -> mDragView.close(false));
}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
new file mode 100644
index 0000000..112f156
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 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.quickstep;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Looper;
+import android.os.UserHandle;
+
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.R;
+import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
+import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.PreloadOptions;
+import com.android.systemui.shared.recents.model.RecentsTaskLoader;
+import com.android.systemui.shared.system.BackgroundExecutor;
+
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+
+/**
+ * Singleton class to load and manage recents model.
+ */
+public class RecentsModel {
+
+ // We do not need any synchronization for this variable as its only written on UI thread.
+ private static RecentsModel INSTANCE;
+
+ public static RecentsModel getInstance(final Context context) {
+ if (INSTANCE == null) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ INSTANCE = new RecentsModel(context.getApplicationContext());
+ } else {
+ try {
+ return new MainThreadExecutor().submit(
+ () -> RecentsModel.getInstance(context)).get();
+ } catch (InterruptedException|ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return INSTANCE;
+ }
+
+ private final Context mContext;
+ private final RecentsTaskLoader mRecentsTaskLoader;
+ private final MainThreadExecutor mMainThreadExecutor;
+
+ private RecentsTaskLoadPlan mLastLoadPlan;
+ private RecentsModel(Context context) {
+ mContext = context;
+
+ Resources res = context.getResources();
+ mRecentsTaskLoader = new RecentsTaskLoader(mContext,
+ res.getInteger(R.integer.config_recentsMaxThumbnailCacheSize),
+ res.getInteger(R.integer.config_recentsMaxIconCacheSize), 0);
+ mRecentsTaskLoader.startLoader(mContext);
+
+ mMainThreadExecutor = new MainThreadExecutor();
+ }
+
+ public RecentsTaskLoader getRecentsTaskLoader() {
+ return mRecentsTaskLoader;
+ }
+
+ /**
+ * Preloads the task plan
+ * @param taskId The running task id or -1
+ * @param callback The callback to receive the task plan once its complete or null. This is
+ * always called on the UI thread.
+ */
+ public void loadTasks(int taskId, Consumer<RecentsTaskLoadPlan> callback) {
+ BackgroundExecutor.get().submit(() -> {
+ // Preload the plan
+ RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(mContext);
+ PreloadOptions opts = new PreloadOptions();
+ opts.loadTitles = false;
+ loadPlan.preloadPlan(opts, mRecentsTaskLoader, taskId, UserHandle.myUserId());
+ // Set the load plan on UI thread
+ mMainThreadExecutor.execute(() -> {
+ mLastLoadPlan = loadPlan;
+ if (callback != null) {
+ callback.accept(loadPlan);
+ }
+ });
+ });
+ }
+
+ public RecentsTaskLoadPlan getLastLoadPlan() {
+ return mLastLoadPlan;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java
index 6161858..00901c6 100644
--- a/quickstep/src/com/android/quickstep/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/RecentsView.java
@@ -146,7 +146,8 @@
}
public void update(RecentsTaskLoadPlan loadPlan) {
- final RecentsTaskLoader loader = TouchInteractionService.getRecentsTaskLoader();
+ final RecentsTaskLoader loader = RecentsModel.getInstance(getContext())
+ .getRecentsTaskLoader();
TaskStack stack = loadPlan != null ? loadPlan.getTaskStack() : null;
if (stack == null) {
removeAllViews();
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/TaskThumbnailView.java
index 4a9bfea..3d4d451 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailView.java
@@ -173,4 +173,10 @@
}
invalidate();
}
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ updateThumbnailMatrix();
+ }
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index f457a59..4321791 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -28,10 +28,8 @@
import android.app.ActivityOptions;
import android.app.Service;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.PointF;
@@ -39,7 +37,6 @@
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import android.view.Choreographer;
import android.view.Display;
@@ -52,14 +49,9 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.R;
import com.android.launcher3.util.TraceHelper;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.PreloadOptions;
-import com.android.systemui.shared.recents.model.RecentsTaskLoader;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.BackgroundExecutor;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -74,8 +66,6 @@
private static final String TAG = "TouchInteractionService";
- private static RecentsTaskLoader sRecentsTaskLoader;
-
private final IBinder mMyBinder = new IOverviewProxy.Stub() {
@Override
@@ -93,12 +83,18 @@
= this::handleTouchDownOnOtherActivity;
private final Consumer<MotionEvent> mNoOpTouchConsumer = (ev) -> {};
+ private static boolean sConnected = false;
+
+ public static boolean isConnected() {
+ return sConnected;
+ }
+
private ActivityManagerWrapper mAM;
private RunningTaskInfo mRunningTask;
+ private RecentsModel mRecentsModel;
private Intent mHomeIntent;
private ComponentName mLauncher;
private MotionEventQueue mEventQueue;
- private MainThreadExecutor mMainThreadExecutor;
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
@@ -117,6 +113,7 @@
public void onCreate() {
super.onCreate();
mAM = ActivityManagerWrapper.getInstance();
+ mRecentsModel = RecentsModel.getInstance(this);
mHomeIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
@@ -126,16 +123,15 @@
mLauncher = new ComponentName(getPackageName(), info.activityInfo.name);
mHomeIntent.setComponent(mLauncher);
- Resources res = getResources();
- if (sRecentsTaskLoader == null) {
- sRecentsTaskLoader = new RecentsTaskLoader(this,
- res.getInteger(R.integer.config_recentsMaxThumbnailCacheSize),
- res.getInteger(R.integer.config_recentsMaxIconCacheSize), 0);
- sRecentsTaskLoader.startLoader(this);
- }
-
- mMainThreadExecutor = new MainThreadExecutor();
mEventQueue = new MotionEventQueue(Choreographer.getInstance(), this::handleMotionEvent);
+ mRecentsModel.loadTasks(-1, null);
+ sConnected = true;
+ }
+
+ @Override
+ public void onDestroy() {
+ sConnected = false;
+ super.onDestroy();
}
@Override
@@ -144,10 +140,6 @@
return mMyBinder;
}
- public static RecentsTaskLoader getRecentsTaskLoader() {
- return sRecentsTaskLoader;
- }
-
private void handleMotionEvent(MotionEvent ev) {
if (ev.getActionMasked() == ACTION_DOWN) {
mRunningTask = mAM.getRunningTask();
@@ -255,12 +247,9 @@
final NavBarSwipeInteractionHandler handler =
new NavBarSwipeInteractionHandler(mRunningTask, this);
- // Preload and start the recents activity on a background thread
- final Context context = this;
- final RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(context);
- final int taskId = mRunningTask.id;
TraceHelper.partitionSection("TouchInt", "Thershold crossed ");
+ // Start the recents activity on a background thread
BackgroundExecutor.get().submit(() -> {
// Get the snap shot before
handler.setTaskSnapshot(getCurrentTaskSnapshot());
@@ -275,15 +264,10 @@
ActivityOptions.makeCustomAnimation(this, 0, 0), UserHandle.myUserId(),
null, null);
*/
-
- // Preload the plan
- RecentsTaskLoader loader = TouchInteractionService.getRecentsTaskLoader();
- PreloadOptions opts = new PreloadOptions();
- opts.loadTitles = false;
- loadPlan.preloadPlan(opts, loader, taskId, UserHandle.myUserId());
- // Set the load plan on UI thread
- mMainThreadExecutor.execute(() -> handler.setRecentsTaskLoadPlan(loadPlan));
});
+
+ // Preload the plan
+ mRecentsModel.loadTasks(mRunningTask.id, handler::setRecentsTaskLoadPlan);
mInteractionHandler = handler;
}
diff --git a/res/drawable/bg_deferred_app_widget.xml b/res/drawable/bg_deferred_app_widget.xml
new file mode 100644
index 0000000..07bae48
--- /dev/null
+++ b/res/drawable/bg_deferred_app_widget.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2015, 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.
+*/
+-->
+
+<inset
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:inset="8dp">
+ <color android:color="#77000000" />
+</inset>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 42fee80..4abdfd6 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -32,8 +32,6 @@
<!-- Popup container -->
<color name="notification_icon_default_color">#757575</color> <!-- Gray 600 -->
- <color name="badge_color">#1DE9B6</color> <!-- Teal A400 -->
- <color name="folder_badge_color">#1DE9B6</color> <!-- Teal A400 -->
<color name="icon_background">#E0E0E0</color> <!-- Gray 300 -->
<color name="legacy_icon_background">#FFFFFF</color>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 36eb34b..e10719b 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -212,10 +212,6 @@
<!-- (touch_size - icon_size) / 2 -->
<dimen name="system_shortcut_header_icon_padding">12dp</dimen>
-<!-- Icon badges (with notification counts) -->
- <dimen name="badge_small_padding">0dp</dimen>
- <dimen name="badge_large_padding">3dp</dimen>
-
<!-- Notifications -->
<dimen name="bg_round_rect_radius">12dp</dimen>
<dimen name="notification_padding_start">16dp</dimen>
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 9a6be0b..824040a 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -155,6 +155,7 @@
public static void closeAllOpenViews(Launcher launcher, boolean animate) {
closeOpenViews(launcher, animate, TYPE_ALL);
+ launcher.finishAutoCancelActionMode();
}
public static void closeAllOpenViews(Launcher launcher) {
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 1e95333..7648e30 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -19,6 +19,7 @@
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.util.FocusLogic;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
public class AppWidgetResizeFrame extends AbstractFloatingView implements View.OnKeyListener {
private static final int SNAP_DURATION = 150;
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 162aa08..9775955 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -29,6 +29,7 @@
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
+import android.os.Build.VERSION;
import android.os.Bundle;
import android.os.Process;
import android.text.TextUtils;
@@ -438,7 +439,7 @@
// Auto installs should always support the current platform version.
mValues.put(LauncherSettings.Favorites.ICON, Utilities.flattenBitmap(
LauncherIcons.createBadgedIconBitmap(
- icon, Process.myUserHandle(), mContext, Build.VERSION.SDK_INT)));
+ icon, Process.myUserHandle(), mContext, VERSION.SDK_INT).icon));
mValues.put(Favorites.ICON_PACKAGE, mIconRes.getResourcePackageName(iconId));
mValues.put(Favorites.ICON_RESOURCE, mIconRes.getResourceName(iconId));
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index a590504..46d7227 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -88,7 +88,7 @@
private BadgeInfo mBadgeInfo;
private BadgeRenderer mBadgeRenderer;
- private IconPalette mBadgePalette;
+ private int mBadgeColor;
private float mBadgeScale;
private boolean mForceHideBadge;
private Point mTempSpaceForBadgeOffset = new Point();
@@ -183,7 +183,7 @@
*/
public void reset() {
mBadgeInfo = null;
- mBadgePalette = null;
+ mBadgeColor = Color.TRANSPARENT;
mBadgeScale = 0f;
mForceHideBadge = false;
}
@@ -193,7 +193,7 @@
}
public void applyFromShortcutInfo(ShortcutInfo info, boolean promiseStateChanged) {
- applyIconAndLabel(info.iconBitmap, info);
+ applyIconAndLabel(info);
setTag(info);
if (promiseStateChanged || (info.hasPromiseIconUi())) {
applyPromiseState(promiseStateChanged);
@@ -203,7 +203,7 @@
}
public void applyFromApplicationInfo(AppInfo info) {
- applyIconAndLabel(info.iconBitmap, info);
+ applyIconAndLabel(info);
// We don't need to check the info since it's not a ShortcutInfo
super.setTag(info);
@@ -219,7 +219,7 @@
}
public void applyFromPackageItemInfo(PackageItemInfo info) {
- applyIconAndLabel(info.iconBitmap, info);
+ applyIconAndLabel(info);
// We don't need to check the info since it's not a ShortcutInfo
super.setTag(info);
@@ -227,8 +227,10 @@
verifyHighRes();
}
- private void applyIconAndLabel(Bitmap icon, ItemInfo info) {
- FastBitmapDrawable iconDrawable = DrawableFactory.get(getContext()).newIcon(icon, info);
+ private void applyIconAndLabel(ItemInfoWithIcon info) {
+ FastBitmapDrawable iconDrawable = DrawableFactory.get(getContext()).newIcon(info);
+ mBadgeColor = IconPalette.getMutedColor(info.iconColor, 0.54f);
+
iconDrawable.setIsDisabled(info.isDisabled());
setIcon(iconDrawable);
setText(info.title);
@@ -401,7 +403,7 @@
final int scrollX = getScrollX();
final int scrollY = getScrollY();
canvas.translate(scrollX, scrollY);
- mBadgeRenderer.draw(canvas, mBadgePalette, mBadgeInfo, mTempIconBounds, mBadgeScale,
+ mBadgeRenderer.draw(canvas, mBadgeColor, mTempIconBounds, mBadgeScale,
mTempSpaceForBadgeOffset);
canvas.translate(-scrollX, -scrollY);
}
@@ -532,7 +534,7 @@
preloadDrawable.setLevel(progressLevel);
} else {
preloadDrawable = DrawableFactory.get(getContext())
- .newPendingIcon(info.iconBitmap, getContext());
+ .newPendingIcon(info, getContext());
preloadDrawable.setLevel(progressLevel);
setIcon(preloadDrawable);
}
@@ -550,10 +552,6 @@
float newBadgeScale = isBadged ? 1f : 0;
mBadgeRenderer = mLauncher.getDeviceProfile().mBadgeRenderer;
if (wasBadged || isBadged) {
- 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();
@@ -565,10 +563,6 @@
}
}
- 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 01b6ca9..4087df1 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -60,6 +60,7 @@
import com.android.launcher3.util.ParcelableSparseArray;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 7b3da67..3bfc41c 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -248,7 +248,7 @@
computeAllAppsButtonSize(context);
// This is done last, after iconSizePx is calculated above.
- mBadgeRenderer = new BadgeRenderer(context, iconSizePx);
+ mBadgeRenderer = new BadgeRenderer(iconSizePx);
}
DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 1272e0a..bd19dfa 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -32,7 +32,7 @@
import android.util.Property;
import android.util.SparseArray;
-import com.android.launcher3.graphics.IconPalette;
+import com.android.launcher3.graphics.BitmapInfo;
public class FastBitmapDrawable extends Drawable {
@@ -40,19 +40,9 @@
private static final float DISABLED_DESATURATION = 1f;
private static final float DISABLED_BRIGHTNESS = 0.5f;
- public static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() {
+ public static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = (input) ->
+ (input < 0.05f) ? (input / 0.05f) : ((input < 0.3f) ? 1 : (1 - input) / 0.7f);
- @Override
- public float getInterpolation(float input) {
- if (input < 0.05f) {
- return input / 0.05f;
- } else if (input < 0.3f){
- return 1;
- } else {
- return (1 - input) / 0.7f;
- }
- }
- };
public static final int CLICK_FEEDBACK_DURATION = 2000;
// Since we don't need 256^2 values for combinations of both the brightness and saturation, we
@@ -69,12 +59,11 @@
protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
private final Bitmap mBitmap;
+ protected final int mIconColor;
private boolean mIsPressed;
private boolean mIsDisabled;
- private IconPalette mIconPalette;
-
private static final Property<FastBitmapDrawable, Float> BRIGHTNESS
= new Property<FastBitmapDrawable, Float>(Float.TYPE, "brightness") {
@Override
@@ -99,7 +88,20 @@
private ObjectAnimator mBrightnessAnimator;
public FastBitmapDrawable(Bitmap b) {
+ this(b, Color.TRANSPARENT);
+ }
+
+ public FastBitmapDrawable(BitmapInfo info) {
+ this(info.icon, info.color);
+ }
+
+ public FastBitmapDrawable(ItemInfoWithIcon info) {
+ this(info.iconBitmap, info.iconColor);
+ }
+
+ protected FastBitmapDrawable(Bitmap b, int iconColor) {
mBitmap = b;
+ mIconColor = iconColor;
setFilterBitmap(true);
}
@@ -108,14 +110,6 @@
canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
}
- public IconPalette getIconPalette() {
- if (mIconPalette == null) {
- mIconPalette = IconPalette.fromDominantColor(Utilities
- .findDominantColorByHue(mBitmap, 20), true /* desaturateBackground */);
- }
- return mIconPalette;
- }
-
@Override
public void setColorFilter(ColorFilter cf) {
// No op
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index ab853e5..baa60b0 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -33,18 +33,21 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
-import android.os.Build;
+import android.os.Build.VERSION;
import android.os.Handler;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
import android.support.annotation.NonNull;
+import android.support.v4.graphics.ColorUtils;
import android.text.TextUtils;
import android.util.Log;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.BitmapInfo;
+import com.android.launcher3.graphics.ColorExtractor;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.uioverrides.UiFactory;
@@ -81,14 +84,13 @@
@Thunk static final Object ICON_UPDATE_TOKEN = new Object();
- public static class CacheEntry {
- public Bitmap icon;
+ public static class CacheEntry extends BitmapInfo {
public CharSequence title = "";
public CharSequence contentDescription = "";
public boolean isLowResIcon;
}
- private final HashMap<UserHandle, Bitmap> mDefaultIcons = new HashMap<>();
+ private final HashMap<UserHandle, BitmapInfo> mDefaultIcons = new HashMap<>();
@Thunk final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
private final Context mContext;
@@ -190,9 +192,9 @@
return mIconProvider.getIcon(info, mIconDpi, flattenDrawable);
}
- protected Bitmap makeDefaultIcon(UserHandle user) {
+ protected BitmapInfo makeDefaultIcon(UserHandle user) {
Drawable unbadged = getFullResDefaultActivityIcon();
- return LauncherIcons.createBadgedIconBitmap(unbadged, user, mContext, Build.VERSION_CODES.O);
+ return LauncherIcons.createBadgedIconBitmap(unbadged, user, mContext, VERSION.SDK_INT);
}
/**
@@ -376,16 +378,16 @@
}
if (entry == null) {
entry = new CacheEntry();
- entry.icon = LauncherIcons.createBadgedIconBitmap(getFullResIcon(app), app.getUser(),
- mContext, app.getApplicationInfo().targetSdkVersion);
+ LauncherIcons.createBadgedIconBitmap(getFullResIcon(app), app.getUser(),
+ mContext, app.getApplicationInfo().targetSdkVersion).applyTo(entry);
}
entry.title = app.getLabel();
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
mCache.put(key, entry);
Bitmap lowResIcon = generateLowResIcon(entry.icon);
- ContentValues values = newContentValues(entry.icon, lowResIcon, entry.title.toString(),
- app.getApplicationInfo().packageName);
+ ContentValues values = newContentValues(entry.icon, lowResIcon, entry.color,
+ entry.title.toString(), app.getApplicationInfo().packageName);
addIconToDB(values, app.getComponentName(), info, userSerial);
}
@@ -459,7 +461,7 @@
// null info means not installed, but if we have a component from the intent then
// we should still look in the cache for restored app icons.
if (info.getTargetComponent() == null) {
- info.iconBitmap = getDefaultIcon(info.user);
+ getDefaultIcon(info.user).applyTo(info);
info.title = "";
info.contentDescription = "";
info.usingLowResIcon = false;
@@ -494,11 +496,11 @@
private void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) {
info.title = Utilities.trim(entry.title);
info.contentDescription = entry.contentDescription;
- info.iconBitmap = entry.icon == null ? getDefaultIcon(info.user) : entry.icon;
info.usingLowResIcon = entry.isLowResIcon;
+ ((entry.icon == null) ? getDefaultIcon(info.user) : entry).applyTo(info);
}
- public synchronized Bitmap getDefaultIcon(UserHandle user) {
+ public synchronized BitmapInfo getDefaultIcon(UserHandle user) {
if (!mDefaultIcons.containsKey(user)) {
mDefaultIcons.put(user, makeDefaultIcon(user));
}
@@ -506,7 +508,7 @@
}
public boolean isDefaultIcon(Bitmap icon, UserHandle user) {
- return mDefaultIcons.get(user) == icon;
+ return getDefaultIcon(user).icon == icon;
}
/**
@@ -533,9 +535,9 @@
providerFetchedOnce = true;
if (info != null) {
- entry.icon = LauncherIcons.createBadgedIconBitmap(
+ LauncherIcons.createBadgedIconBitmap(
getFullResIcon(info), info.getUser(), mContext,
- infoProvider.get().getApplicationInfo().targetSdkVersion);
+ info.getApplicationInfo().targetSdkVersion).applyTo(entry);
} else {
if (usePackageIcon) {
CacheEntry packageEntry = getEntryForPackageLocked(
@@ -543,7 +545,7 @@
if (packageEntry != null) {
if (DEBUG) Log.d(TAG, "using package default icon for " +
componentName.toShortString());
- entry.icon = packageEntry.icon;
+ packageEntry.applyTo(entry);
entry.title = packageEntry.title;
entry.contentDescription = packageEntry.contentDescription;
}
@@ -551,7 +553,7 @@
if (entry.icon == null) {
if (DEBUG) Log.d(TAG, "using default icon for " +
componentName.toShortString());
- entry.icon = getDefaultIcon(user);
+ getDefaultIcon(user).applyTo(entry);
}
}
}
@@ -594,7 +596,7 @@
entry.title = title;
}
if (icon != null) {
- entry.icon = LauncherIcons.createIconBitmap(icon, mContext);
+ LauncherIcons.createIconBitmap(icon, mContext).applyTo(entry);
}
if (!TextUtils.isEmpty(title) && entry.icon != null) {
mCache.put(cacheKey, entry);
@@ -633,22 +635,23 @@
// Load the full res icon for the application, but if useLowResIcon is set, then
// only keep the low resolution icon instead of the larger full-sized icon
- Bitmap icon = LauncherIcons.createBadgedIconBitmap(
+ BitmapInfo iconInfo = LauncherIcons.createBadgedIconBitmap(
appInfo.loadIcon(mPackageManager), user, mContext, appInfo.targetSdkVersion);
if (mInstantAppResolver.isInstantApp(appInfo)) {
- LauncherIcons.badgeWithDrawable(icon,
+ LauncherIcons.badgeWithDrawable(iconInfo.icon,
mContext.getDrawable(R.drawable.ic_instant_app_badge), mContext);
}
- Bitmap lowResIcon = generateLowResIcon(icon);
+ Bitmap lowResIcon = generateLowResIcon(iconInfo.icon);
entry.title = appInfo.loadLabel(mPackageManager);
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
- entry.icon = useLowResIcon ? lowResIcon : icon;
+ entry.icon = useLowResIcon ? lowResIcon : iconInfo.icon;
+ entry.color = iconInfo.color;
entry.isLowResIcon = useLowResIcon;
// Add the icon in the DB here, since these do not get written during
// package updates.
- ContentValues values =
- newContentValues(icon, lowResIcon, entry.title.toString(), packageName);
+ ContentValues values = newContentValues(iconInfo.icon, lowResIcon, entry.color,
+ entry.title.toString(), packageName);
addIconToDB(values, cacheKey.componentName, info,
mUserManager.getSerialNumberForUser(user));
@@ -671,14 +674,16 @@
try {
c = mIconDb.query(
new String[]{lowRes ? IconDB.COLUMN_ICON_LOW_RES : IconDB.COLUMN_ICON,
- IconDB.COLUMN_LABEL},
+ IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL},
IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
new String[]{cacheKey.componentName.flattenToString(),
Long.toString(mUserManager.getSerialNumberForUser(cacheKey.user))});
if (c.moveToNext()) {
entry.icon = loadIconNoResize(c, 0, lowRes ? mLowResOptions : mHighResOptions);
+ // Set the alpha to be 255, so that we never have a wrong color
+ entry.color = ColorUtils.setAlphaComponent(c.getInt(1), 255);
entry.isLowResIcon = lowRes;
- entry.title = c.getString(1);
+ entry.title = c.getString(2);
if (entry.title == null) {
entry.title = "";
entry.contentDescription = "";
@@ -771,7 +776,7 @@
}
private static final class IconDB extends SQLiteCacheHelper {
- private final static int DB_VERSION = 17;
+ private final static int DB_VERSION = 18;
private final static int RELEASE_VERSION = DB_VERSION +
(FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1);
@@ -784,6 +789,7 @@
private final static String COLUMN_VERSION = "version";
private final static String COLUMN_ICON = "icon";
private final static String COLUMN_ICON_LOW_RES = "icon_low_res";
+ private final static String COLUMN_ICON_COLOR = "icon_color";
private final static String COLUMN_LABEL = "label";
private final static String COLUMN_SYSTEM_STATE = "system_state";
@@ -802,6 +808,7 @@
COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " +
COLUMN_ICON + " BLOB, " +
COLUMN_ICON_LOW_RES + " BLOB, " +
+ COLUMN_ICON_COLOR + " INTEGER NOT NULL DEFAULT 0, " +
COLUMN_LABEL + " TEXT, " +
COLUMN_SYSTEM_STATE + " TEXT, " +
"PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " +
@@ -809,11 +816,12 @@
}
}
- private ContentValues newContentValues(Bitmap icon, Bitmap lowResIcon, String label,
- String packageName) {
+ private ContentValues newContentValues(Bitmap icon, Bitmap lowResIcon, int iconColor,
+ String label, String packageName) {
ContentValues values = new ContentValues();
values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(icon));
values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap(lowResIcon));
+ values.put(IconDB.COLUMN_ICON_COLOR, iconColor);
values.put(IconDB.COLUMN_LABEL, label);
values.put(IconDB.COLUMN_SYSTEM_STATE, mIconProvider.getIconSystemState(packageName));
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index df1eec6..c476421 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -40,6 +40,7 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.graphics.BitmapInfo;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
@@ -480,7 +481,7 @@
final LauncherAppState app = LauncherAppState.getInstance(mContext);
// Set default values until proper values is loaded.
appInfo.title = "";
- appInfo.iconBitmap = app.getIconCache().getDefaultIcon(user);
+ app.getIconCache().getDefaultIcon(user).applyTo(appInfo);
final ShortcutInfo si = appInfo.makeShortcut();
if (Looper.myLooper() == LauncherModel.getWorkerLooper()) {
app.getIconCache().getTitleAndIcon(si, activityInfo, false /* useLowResIcon */);
@@ -497,7 +498,7 @@
return Pair.create((ItemInfo) si, (Object) activityInfo);
} else if (shortcutInfo != null) {
ShortcutInfo si = new ShortcutInfo(shortcutInfo, mContext);
- si.iconBitmap = LauncherIcons.createShortcutIcon(shortcutInfo, mContext);
+ LauncherIcons.createShortcutIcon(shortcutInfo, mContext).applyTo(si);
return Pair.create((ItemInfo) si, (Object) shortcutInfo);
} else if (providerInfo != null) {
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
@@ -641,18 +642,20 @@
// users wouldn't get here without intent forwarding anyway.
info.user = Process.myUserHandle();
+ BitmapInfo iconInfo = null;
if (bitmap instanceof Bitmap) {
- info.iconBitmap = LauncherIcons.createIconBitmap((Bitmap) bitmap, app.getContext());
+ iconInfo = LauncherIcons.createIconBitmap((Bitmap) bitmap, app.getContext());
} else {
Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
if (extra instanceof Intent.ShortcutIconResource) {
info.iconResource = (Intent.ShortcutIconResource) extra;
- info.iconBitmap = LauncherIcons.createIconBitmap(info.iconResource, app.getContext());
+ iconInfo = LauncherIcons.createIconBitmap(info.iconResource, app.getContext());
}
}
- if (info.iconBitmap == null) {
- info.iconBitmap = app.getIconCache().getDefaultIcon(info.user);
+ if (iconInfo == null) {
+ iconInfo = app.getIconCache().getDefaultIcon(info.user);
}
+ iconInfo.applyTo(info);
info.title = Utilities.trim(name);
info.contentDescription = UserManagerCompat.getInstance(app.getContext())
diff --git a/src/com/android/launcher3/ItemInfoWithIcon.java b/src/com/android/launcher3/ItemInfoWithIcon.java
index fea4dda..bf985c3 100644
--- a/src/com/android/launcher3/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/ItemInfoWithIcon.java
@@ -29,6 +29,11 @@
public Bitmap iconBitmap;
/**
+ * Dominant color in the {@link #iconBitmap}.
+ */
+ public int iconColor;
+
+ /**
* Indicates whether we're using a low res icon
*/
public boolean usingLowResIcon;
@@ -96,6 +101,7 @@
protected ItemInfoWithIcon(ItemInfoWithIcon info) {
super(info);
iconBitmap = info.iconBitmap;
+ iconColor = info.iconColor;
usingLowResIcon = info.usingLowResIcon;
runtimeStatusFlags = info.runtimeStatusFlags;
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 44660ec..adfb44c 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -29,8 +29,6 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_APPS;
-import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_WIDGETS;
import android.Manifest;
import android.animation.Animator;
@@ -72,6 +70,7 @@
import android.text.method.TextKeyListener;
import android.util.Log;
import android.util.SparseArray;
+import android.view.ActionMode;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -127,15 +126,16 @@
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.PendingRequestArgs;
-import com.android.launcher3.util.RunnableWithId;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
import com.android.launcher3.widget.WidgetAddFlowHandler;
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetListRowEntry;
@@ -198,6 +198,10 @@
// Type: SparseArray<Parcelable>
private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel";
+ // When starting an action mode, setting this tag will cause the action mode to be cancelled
+ // automatically when user interacts with the launcher.
+ public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
+
private LauncherStateManager mStateManager;
private boolean mIsSafeModeEnabled;
@@ -236,10 +240,6 @@
@Thunk boolean mWorkspaceLoading = true;
- private boolean mPaused = true;
- private boolean mOnResumeNeedsLoad;
-
- private final ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<>();
private OnResumeCallback mOnResumeCallback;
private ViewOnDrawExecutor mPendingExecutor;
@@ -275,6 +275,7 @@
private boolean mAppLaunchSuccess;
private RotationPrefChangeHandler mRotationPrefChangeHandler;
+ private ActionMode mCurrentActionMode;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -333,11 +334,6 @@
mAppWidgetHost = new LauncherAppWidgetHost(this);
mAppWidgetHost.startListening();
- // If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
- // this also ensures that any synchronous binding below doesn't re-trigger another
- // LauncherModel load.
- mPaused = false;
-
mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null);
setupViews();
@@ -777,10 +773,7 @@
mLauncherCallbacks.onStart();
}
mAppWidgetHost.setListenIfResumed(true);
-
- if (!isWorkspaceLoading()) {
- NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
- }
+ NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
if (mShouldFadeInScrim && mDragLayer.getBackground() != null) {
if (mScrimAnimator != null) {
@@ -810,21 +803,6 @@
mAppLaunchSuccess = false;
getUserEventDispatcher().resetElapsedSessionMillis();
- mPaused = false;
- if (mOnResumeNeedsLoad) {
- setWorkspaceLoading(true);
- mModel.startLoader(getCurrentWorkspaceScreen());
- mOnResumeNeedsLoad = false;
- }
- if (mBindOnResumeCallbacks.size() > 0) {
- // We might have postponed some bind calls until onResume (see waitUntilResume) --
- // execute them here
- for (int i = 0; i < mBindOnResumeCallbacks.size(); i++) {
- mBindOnResumeCallbacks.get(i).run();
- }
- mBindOnResumeCallbacks.clear();
- }
-
setOnResumeCallback(null);
// Process any items that were added while Launcher was away.
InstallShortcutReceiver.disableAndFlushInstallQueue(
@@ -847,7 +825,6 @@
InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED);
super.onPause();
- mPaused = true;
mDragController.cancelDrag();
mDragController.resetLastGestureUpTime();
@@ -1161,20 +1138,12 @@
};
public void updateIconBadges(final Set<PackageUserKey> updatedBadges) {
- Runnable r = new Runnable() {
- @Override
- public void run() {
- mWorkspace.updateIconBadges(updatedBadges);
- mAppsView.updateIconBadges(updatedBadges);
+ mWorkspace.updateIconBadges(updatedBadges);
+ mAppsView.updateIconBadges(updatedBadges);
- PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(Launcher.this);
- if (popup != null) {
- popup.updateNotificationHeader(updatedBadges);
- }
- }
- };
- if (!waitUntilResume(r)) {
- r.run();
+ PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(Launcher.this);
+ if (popup != null) {
+ popup.updateNotificationHeader(updatedBadges);
}
}
@@ -1626,6 +1595,9 @@
@Override
public void onBackPressed() {
+ if (finishAutoCancelActionMode()) {
+ return;
+ }
if (mLauncherCallbacks != null && mLauncherCallbacks.handleBackPressed()) {
return;
}
@@ -2156,33 +2128,6 @@
return result;
}
- /**
- * If the activity is currently paused, signal that we need to run the passed Runnable
- * in onResume.
- *
- * This needs to be called from incoming places where resources might have been loaded
- * while the activity is paused. That is because the Configuration (e.g., rotation) might be
- * wrong when we're not running, and if the activity comes back to what the configuration was
- * when we were paused, activity is not restarted.
- *
- * Implementation of the method from LauncherModel.Callbacks.
- *
- * @return {@code true} if we are currently paused. The caller might be able to skip some work
- */
- @Thunk boolean waitUntilResume(Runnable run) {
- if (mPaused) {
- if (LOGD) Log.d(TAG, "Deferring update until onResume");
- if (run instanceof RunnableWithId) {
- // Remove any runnables which have the same id
- while (mBindOnResumeCallbacks.remove(run)) { }
- }
- mBindOnResumeCallbacks.add(run);
- return true;
- } else {
- return false;
- }
- }
-
public void setOnResumeCallback(OnResumeCallback callback) {
if (mOnResumeCallback != null) {
mOnResumeCallback.onLauncherResume();
@@ -2191,31 +2136,6 @@
}
/**
- * If the activity is currently paused, signal that we need to re-run the loader
- * in onResume.
- *
- * This needs to be called from incoming places where resources might have been loaded
- * while we are paused. That is becaues the Configuration might be wrong
- * when we're not running, and if it comes back to what it was when we
- * were paused, we are not restarted.
- *
- * Implementation of the method from LauncherModel.Callbacks.
- *
- * @return true if we are currently paused. The caller might be able to
- * skip some work in that case since we will come back again.
- */
- @Override
- public boolean setLoadOnResume() {
- if (mPaused) {
- if (LOGD) Log.d(TAG, "setLoadOnResume");
- mOnResumeNeedsLoad = true;
- return true;
- } else {
- return false;
- }
- }
-
- /**
* Implementation of the method from LauncherModel.Callbacks.
*/
@Override
@@ -2233,7 +2153,6 @@
*/
@Override
public void clearPendingBinds() {
- mBindOnResumeCallbacks.clear();
if (mPendingExecutor != null) {
mPendingExecutor.markCompleted();
mPendingExecutor = null;
@@ -2297,18 +2216,8 @@
}
@Override
- public void bindAppsAdded(final ArrayList<Long> newScreens,
- final ArrayList<ItemInfo> addNotAnimated,
- final ArrayList<ItemInfo> addAnimated) {
- Runnable r = new Runnable() {
- public void run() {
- bindAppsAdded(newScreens, addNotAnimated, addAnimated);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
+ public void bindAppsAdded(ArrayList<Long> newScreens, ArrayList<ItemInfo> addNotAnimated,
+ ArrayList<ItemInfo> addAnimated) {
// Add the new screens
if (newScreens != null) {
bindAddScreens(newScreens);
@@ -2334,15 +2243,6 @@
*/
@Override
public void bindItems(final List<ItemInfo> items, final boolean forceAnimateIcons) {
- Runnable r = new Runnable() {
- public void run() {
- bindItems(items, forceAnimateIcons);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
// Get the list of added items and intersect them with the set of items here
final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
final Collection<Animator> bounceAnims = new ArrayList<>();
@@ -2585,7 +2485,7 @@
}
if (((PendingAppWidgetHostView) view).isReinflateIfNeeded()) {
- view.reinflate();
+ view.reInflate();
}
getModelWriter().updateItemInDatabase(info);
@@ -2613,27 +2513,11 @@
@Override
public void finishFirstPageBind(final ViewOnDrawExecutor executor) {
- Runnable r = new Runnable() {
- public void run() {
- finishFirstPageBind(executor);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
- Runnable onComplete = new Runnable() {
- @Override
- public void run() {
- if (executor != null) {
- executor.onLoadAnimationCompleted();
- }
- }
- };
if (mDragLayer.getAlpha() < 1) {
- mDragLayer.animate().alpha(1).withEndAction(onComplete).start();
- } else {
- onComplete.run();
+ mDragLayer.animate().alpha(1).withEndAction(
+ executor == null ? null : executor::onLoadAnimationCompleted).start();
+ } else if (executor != null) {
+ executor.onLoadAnimationCompleted();
}
}
@@ -2643,10 +2527,6 @@
* Implementation of the method from LauncherModel.Callbacks.
*/
public void finishBindingItems() {
- Runnable r = this::finishBindingItems;
- if (waitUntilResume(r)) {
- return;
- }
TraceHelper.beginSection("finishBindingItems");
mWorkspace.restoreInstanceStateForRemainingPages();
@@ -2661,7 +2541,6 @@
InstallShortcutReceiver.disableAndFlushInstallQueue(
InstallShortcutReceiver.FLAG_LOADER_RUNNING, this);
- NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
TraceHelper.endSection("finishBindingItems");
}
@@ -2687,21 +2566,12 @@
*
* Implementation of the method from LauncherModel.Callbacks.
*/
- public void bindAllApplications(final ArrayList<AppInfo> apps) {
- Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_APPS) {
- public void run() {
- bindAllApplications(apps);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
+ public void bindAllApplications(ArrayList<AppInfo> apps) {
if (mAppsView != null) {
Executor pendingExecutor = getPendingExecutor();
if (pendingExecutor != null && !isInState(ALL_APPS)) {
// Wait until the fade in animation has finished before setting all apps list.
- pendingExecutor.execute(r);
+ pendingExecutor.execute(() -> bindAllApplications(apps));
return;
}
@@ -2734,47 +2604,22 @@
*
* Implementation of the method from LauncherModel.Callbacks.
*/
- public void bindAppsAddedOrUpdated(final ArrayList<AppInfo> apps) {
- Runnable r = new Runnable() {
- public void run() {
- bindAppsAddedOrUpdated(apps);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
+ @Override
+ public void bindAppsAddedOrUpdated(ArrayList<AppInfo> apps) {
if (mAppsView != null) {
mAppsView.addOrUpdateApps(apps);
}
}
@Override
- public void bindPromiseAppProgressUpdated(final PromiseAppInfo app) {
- Runnable r = new Runnable() {
- public void run() {
- bindPromiseAppProgressUpdated(app);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
+ public void bindPromiseAppProgressUpdated(PromiseAppInfo app) {
if (mAppsView != null) {
mAppsView.updatePromiseAppProgress(app);
}
}
@Override
- public void bindWidgetsRestored(final ArrayList<LauncherAppWidgetInfo> widgets) {
- Runnable r = new Runnable() {
- public void run() {
- bindWidgetsRestored(widgets);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
+ public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) {
mWorkspace.widgetsRestored(widgets);
}
@@ -2785,16 +2630,7 @@
* @param updated list of shortcuts which have changed.
*/
@Override
- public void bindShortcutsChanged(final ArrayList<ShortcutInfo> updated, final UserHandle user) {
- Runnable r = new Runnable() {
- public void run() {
- bindShortcutsChanged(updated, user);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
+ public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated, final UserHandle user) {
if (!updated.isEmpty()) {
mWorkspace.updateShortcuts(updated);
}
@@ -2806,16 +2642,7 @@
* Implementation of the method from LauncherModel.Callbacks.
*/
@Override
- public void bindRestoreItemsChange(final HashSet<ItemInfo> updates) {
- Runnable r = new Runnable() {
- public void run() {
- bindRestoreItemsChange(updates);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
+ public void bindRestoreItemsChange(HashSet<ItemInfo> updates) {
mWorkspace.updateRestoreItems(updates);
}
@@ -2828,29 +2655,12 @@
*/
@Override
public void bindWorkspaceComponentsRemoved(final ItemInfoMatcher matcher) {
- Runnable r = new Runnable() {
- public void run() {
- bindWorkspaceComponentsRemoved(matcher);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
mWorkspace.removeItemsByMatcher(matcher);
mDragController.onAppsRemoved(matcher);
}
@Override
public void bindAppInfosRemoved(final ArrayList<AppInfo> appInfos) {
- Runnable r = new Runnable() {
- public void run() {
- bindAppInfosRemoved(appInfos);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
// Update AllApps
if (mAppsView != null) {
mAppsView.removeApps(appInfos);
@@ -2860,16 +2670,6 @@
@Override
public void bindAllWidgets(final ArrayList<WidgetListRowEntry> allWidgets) {
mPopupDataProvider.setAllWidgets(allWidgets);
- Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_WIDGETS) {
- @Override
- public void run() {
- bindAllWidgets(allWidgets);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
if (topView != null) {
topView.onWidgetsBound();
@@ -3015,6 +2815,26 @@
}
}
+ @Override
+ public void onActionModeStarted(ActionMode mode) {
+ super.onActionModeStarted(mode);
+ mCurrentActionMode = mode;
+ }
+
+ @Override
+ public void onActionModeFinished(ActionMode mode) {
+ super.onActionModeFinished(mode);
+ mCurrentActionMode = null;
+ }
+
+ public boolean finishAutoCancelActionMode() {
+ if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) {
+ mCurrentActionMode.finish();
+ return true;
+ }
+ return false;
+ }
+
/**
* Callback for listening for onResume
*/
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 0136c70..a46692b 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -27,6 +27,7 @@
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.notification.NotificationListener;
import com.android.launcher3.util.ConfigMonitor;
import com.android.launcher3.util.Preconditions;
@@ -39,6 +40,8 @@
public class LauncherAppState {
+ public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher";
+
// We do not need any synchronization for this variable as its only written on UI thread.
private static LauncherAppState INSTANCE;
@@ -103,6 +106,10 @@
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ filter.addAction(ACTION_FORCE_ROLOAD);
+ }
+
mContext.registerReceiver(mModel, filter);
UserManagerCompat.getInstance(mContext).enableAndResetCache();
new ConfigMonitor(mContext).register();
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 9aa74b3..7bc7139 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -26,11 +26,14 @@
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
+import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.widget.Toast;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.widget.DeferredAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import java.util.ArrayList;
@@ -84,6 +87,14 @@
// have been established by this point, and we will end up populating the
// widgets upon bind anyway. See issue 14255011 for more context.
}
+
+ // We go in reverse order and inflate any deferred widget
+ for (int i = mViews.size() - 1; i >= 0; i--) {
+ LauncherAppWidgetHostView view = mViews.valueAt(i);
+ if (view instanceof DeferredAppWidgetHostView) {
+ view.reInflate();
+ }
+ }
}
@Override
@@ -178,6 +189,11 @@
inflater.inflate(appWidget.initialLayout, lahv);
lahv.setAppWidget(0, appWidget);
return lahv;
+ } else if ((mFlags & FLAG_LISTENING) == 0) {
+ DeferredAppWidgetHostView view = new DeferredAppWidgetHostView(context);
+ view.setAppWidget(appWidgetId, appWidget);
+ mViews.put(appWidgetId, view);
+ return view;
} else {
try {
return super.createView(context, appWidgetId, appWidget);
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 469c5f1..ea4b280 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -16,6 +16,9 @@
package com.android.launcher3;
+import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
+import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD;
+
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProviderOperation;
@@ -37,6 +40,7 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.model.AddWorkspaceItemsTask;
import com.android.launcher3.model.BgDataModel;
@@ -135,7 +139,6 @@
};
public interface Callbacks {
- public boolean setLoadOnResume();
public int getCurrentWorkspaceScreen();
public void clearPendingBinds();
public void startBinding();
@@ -405,6 +408,8 @@
enqueueModelUpdateTask(new UserLockStateChangedTask(user));
}
}
+ } else if (IS_DOGFOOD_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
+ forceReload();
}
}
@@ -419,25 +424,11 @@
mModelLoaded = false;
}
- // Do this here because if the launcher activity is running it will be restarted.
- // If it's not running startLoaderFromBackground will merely tell it that it needs
- // to reload.
- startLoaderFromBackground();
- }
-
- /**
- * When the launcher is in the background, it's possible for it to miss paired
- * configuration changes. So whenever we trigger the loader from the background
- * tell the launcher that it needs to re-run the loader when it comes back instead
- * of doing it now.
- */
- public void startLoaderFromBackground() {
+ // Start the loader if launcher is already running, otherwise the loader will run,
+ // the next time launcher starts
Callbacks callbacks = getCallback();
if (callbacks != null) {
- // Only actually run the loader if they're not paused.
- if (!callbacks.setLoadOnResume()) {
- startLoader(callbacks.getCurrentWorkspaceScreen());
- }
+ startLoader(callbacks.getCurrentWorkspaceScreen());
}
}
@@ -629,7 +620,7 @@
@Override
public ShortcutInfo get() {
si.updateFromDeepShortcutInfo(info, mApp.getContext());
- si.iconBitmap = LauncherIcons.createShortcutIcon(info, mApp.getContext());
+ LauncherIcons.createShortcutIcon(info, mApp.getContext()).applyTo(si);
return si;
}
});
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 0137b26..d25f958 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -264,6 +264,7 @@
private void onStateTransitionEnd(LauncherState state) {
mLauncher.getWorkspace().setClipChildren(!state.disablePageClipping);
mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+ mLauncher.finishAutoCancelActionMode();
}
/**
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 841c0cd..1a63326 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -23,6 +23,7 @@
import android.view.ViewGroup;
import com.android.launcher3.CellLayout.ContainerType;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
public class ShortcutAndWidgetContainer extends ViewGroup {
static final String TAG = "ShortcutAndWidgetContainer";
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 40f8378..158c540 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -28,7 +28,6 @@
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -45,7 +44,6 @@
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
-import android.util.SparseArray;
import android.util.TypedValue;
import android.view.View;
@@ -288,89 +286,6 @@
}
}
- /**
- * This picks a dominant color, looking for high-saturation, high-value, repeated hues.
- * @param bitmap The bitmap to scan
- * @param samples The approximate max number of samples to use.
- */
- public static int findDominantColorByHue(Bitmap bitmap, int samples) {
- final int height = bitmap.getHeight();
- final int width = bitmap.getWidth();
- int sampleStride = (int) Math.sqrt((height * width) / samples);
- if (sampleStride < 1) {
- sampleStride = 1;
- }
-
- // This is an out-param, for getting the hsv values for an rgb
- float[] hsv = new float[3];
-
- // First get the best hue, by creating a histogram over 360 hue buckets,
- // where each pixel contributes a score weighted by saturation, value, and alpha.
- float[] hueScoreHistogram = new float[360];
- float highScore = -1;
- int bestHue = -1;
-
- int[] pixels = new int[samples];
- int pixelCount = 0;
-
- for (int y = 0; y < height; y += sampleStride) {
- for (int x = 0; x < width; x += sampleStride) {
- int argb = bitmap.getPixel(x, y);
- int alpha = 0xFF & (argb >> 24);
- if (alpha < 0x80) {
- // Drop mostly-transparent pixels.
- continue;
- }
- // Remove the alpha channel.
- int rgb = argb | 0xFF000000;
- Color.colorToHSV(rgb, hsv);
- // Bucket colors by the 360 integer hues.
- int hue = (int) hsv[0];
- if (hue < 0 || hue >= hueScoreHistogram.length) {
- // Defensively avoid array bounds violations.
- continue;
- }
- if (pixelCount < samples) {
- pixels[pixelCount++] = rgb;
- }
- float score = hsv[1] * hsv[2];
- hueScoreHistogram[hue] += score;
- if (hueScoreHistogram[hue] > highScore) {
- highScore = hueScoreHistogram[hue];
- bestHue = hue;
- }
- }
- }
-
- SparseArray<Float> rgbScores = new SparseArray<>();
- int bestColor = 0xff000000;
- highScore = -1;
- // Go back over the RGB colors that match the winning hue,
- // creating a histogram of weighted s*v scores, for up to 100*100 [s,v] buckets.
- // The highest-scoring RGB color wins.
- for (int i = 0; i < pixelCount; i++) {
- int rgb = pixels[i];
- Color.colorToHSV(rgb, hsv);
- int hue = (int) hsv[0];
- if (hue == bestHue) {
- float s = hsv[1];
- float v = hsv[2];
- int bucket = (int) (s * 100) + (int) (v * 10000);
- // Score by cumulative saturation * value.
- float score = s * v;
- Float oldTotal = rgbScores.get(bucket);
- float newTotal = oldTotal == null ? score : oldTotal + score;
- rgbScores.put(bucket, newTotal);
- if (newTotal > highScore) {
- highScore = newTotal;
- // All the colors in the winning bucket are very similar. Last in wins.
- bestColor = rgb;
- }
- }
- }
- return bestColor;
- }
-
/*
* Finds a system apk which had a broadcast receiver listening to a particular action.
* @param action intent action used to find the apk
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 417366d..efafb9c 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -88,8 +88,10 @@
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.WallpaperOffsetInterpolator;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
import java.util.ArrayList;
import java.util.HashSet;
@@ -3510,7 +3512,7 @@
return false;
});
for (PendingAppWidgetHostView view : views) {
- view.reinflate();
+ view.reInflate();
}
}
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 512db72..3b6fea9 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -21,20 +21,17 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.ButtonDropTarget;
import com.android.launcher3.CellLayout;
-import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.FolderInfo;
-import com.android.launcher3.InfoDropTarget;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.UninstallDropTarget;
import com.android.launcher3.Workspace;
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragOptions;
diff --git a/src/com/android/launcher3/badge/BadgeInfo.java b/src/com/android/launcher3/badge/BadgeInfo.java
index 08d8ad4..170f231 100644
--- a/src/com/android/launcher3/badge/BadgeInfo.java
+++ b/src/com/android/launcher3/badge/BadgeInfo.java
@@ -16,12 +16,6 @@
package com.android.launcher3.badge;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Shader;
-import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import com.android.launcher3.notification.NotificationInfo;
@@ -56,12 +50,6 @@
/** This will only be initialized if the badge should display the notification icon. */
private NotificationInfo mNotificationInfo;
- /**
- * When retrieving the notification icon, we draw it into this shader, which can be clipped
- * as necessary when drawn in a badge.
- */
- private Shader mNotificationIcon;
-
public BadgeInfo(PackageUserKey packageUserKey) {
mPackageUserKey = packageUserKey;
mNotificationKeys = new ArrayList<>();
@@ -112,7 +100,6 @@
public void setNotificationToShow(@Nullable NotificationInfo notificationInfo) {
mNotificationInfo = notificationInfo;
- mNotificationIcon = null;
}
public boolean hasNotificationToShow() {
@@ -120,35 +107,6 @@
}
/**
- * Returns a shader to set on a Paint that will draw the notification icon in a badge.
- *
- * The shader is cached until {@link #setNotificationToShow(NotificationInfo)} is called.
- */
- public @Nullable Shader getNotificationIconForBadge(Context context, int badgeColor,
- int badgeSize, int badgePadding) {
- if (mNotificationInfo == null) {
- return null;
- }
- if (mNotificationIcon == null) {
- Drawable icon = mNotificationInfo.getIconForBackground(context, badgeColor)
- .getConstantState().newDrawable();
- int iconSize = badgeSize - badgePadding * 2;
- icon.setBounds(0, 0, iconSize, iconSize);
- Bitmap iconBitmap = Bitmap.createBitmap(badgeSize, badgeSize, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(iconBitmap);
- canvas.translate(badgePadding, badgePadding);
- icon.draw(canvas);
- mNotificationIcon = new BitmapShader(iconBitmap, Shader.TileMode.CLAMP,
- Shader.TileMode.CLAMP);
- }
- return mNotificationIcon;
- }
-
- public boolean isIconLarge() {
- return mNotificationInfo != null && mNotificationInfo.isIconLarge();
- }
-
- /**
* Whether newBadge represents the same PackageUserKey as this badge, and icons with
* this badge should be invalidated. So, for instance, if a badge has 3 notifications
* and one of those notifications is updated, this method should return false because
diff --git a/src/com/android/launcher3/badge/BadgeRenderer.java b/src/com/android/launcher3/badge/BadgeRenderer.java
index 6ce334e..02ec9f0 100644
--- a/src/com/android/launcher3/badge/BadgeRenderer.java
+++ b/src/com/android/launcher3/badge/BadgeRenderer.java
@@ -16,21 +16,17 @@
package com.android.launcher3.badge;
-import android.content.Context;
-import android.content.res.Resources;
+import static android.graphics.Paint.ANTI_ALIAS_FLAG;
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.Shader;
-import android.support.annotation.Nullable;
import android.util.Log;
-import android.util.SparseArray;
-import com.android.launcher3.R;
-import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.graphics.ShadowGenerator;
/**
@@ -41,147 +37,58 @@
private static final String TAG = "BadgeRenderer";
- private static final boolean DOTS_ONLY = true;
-
// The badge sizes are defined as percentages of the app icon size.
- private static final float SIZE_PERCENTAGE = 0.38f;
+ private static final float SIZE_PERCENTAGE = 0.23f;
// Used to expand the width of the badge for each additional digit.
- private static final float CHAR_SIZE_PERCENTAGE = 0.12f;
- private static final float TEXT_SIZE_PERCENTAGE = 0.26f;
private static final float OFFSET_PERCENTAGE = 0.02f;
- private static final float STACK_OFFSET_PERCENTAGE_X = 0.05f;
- private static final float STACK_OFFSET_PERCENTAGE_Y = 0.06f;
- private static final float DOT_SCALE = 0.6f;
- private final Context mContext;
private final int mSize;
- private final int mCharSize;
- private final int mTextHeight;
private final int mOffset;
- private final int mStackOffsetX;
- private final int mStackOffsetY;
- private final IconDrawer mLargeIconDrawer;
- private final IconDrawer mSmallIconDrawer;
- private final Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG
- | Paint.FILTER_BITMAP_FLAG);
- private final SparseArray<Bitmap> mBackgroundsWithShadow;
+ private final float mCircleRadius;
+ private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG);
- public BadgeRenderer(Context context, int iconSizePx) {
- mContext = context;
- Resources res = context.getResources();
+ private final Bitmap mBackgroundWithShadow;
+ private final int mBitmapOffset;
+
+ public BadgeRenderer(int iconSizePx) {
mSize = (int) (SIZE_PERCENTAGE * iconSizePx);
- mCharSize = (int) (CHAR_SIZE_PERCENTAGE * iconSizePx);
mOffset = (int) (OFFSET_PERCENTAGE * iconSizePx);
- mStackOffsetX = (int) (STACK_OFFSET_PERCENTAGE_X * iconSizePx);
- mStackOffsetY = (int) (STACK_OFFSET_PERCENTAGE_Y * iconSizePx);
- mTextPaint.setTextSize(iconSizePx * TEXT_SIZE_PERCENTAGE);
- mTextPaint.setTextAlign(Paint.Align.CENTER);
- mLargeIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_small_padding));
- mSmallIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_large_padding));
- // Measure the text height.
- Rect tempTextHeight = new Rect();
- mTextPaint.getTextBounds("0", 0, 1, tempTextHeight);
- mTextHeight = tempTextHeight.height();
- mBackgroundsWithShadow = new SparseArray<>(3);
+ ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT);
+ mBackgroundWithShadow = builder.setupBlurForSize(mSize).createPill(mSize, mSize);
+ mCircleRadius = builder.radius;
+
+ mBitmapOffset = -mBackgroundWithShadow.getHeight() / 2; // Same as width.
}
/**
* 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 color The color (based on the icon) to use for the badge.
* @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, IconPalette palette, @Nullable BadgeInfo badgeInfo,
- Rect iconBounds, float badgeScale, Point spaceForOffset) {
- if (palette == null || iconBounds == null || spaceForOffset == null) {
+ public void draw(
+ Canvas canvas, int color, Rect iconBounds, float badgeScale, Point spaceForOffset) {
+ if (iconBounds == null || spaceForOffset == null) {
Log.e(TAG, "Invalid null argument(s) passed in call to draw.");
return;
}
- mTextPaint.setColor(palette.textColor);
- IconDrawer iconDrawer = badgeInfo != null && badgeInfo.isIconLarge()
- ? mLargeIconDrawer : mSmallIconDrawer;
- Shader icon = badgeInfo == null ? null : badgeInfo.getNotificationIconForBadge(
- mContext, palette.backgroundColor, mSize, iconDrawer.mPadding);
- String notificationCount = badgeInfo == null ? "0"
- : String.valueOf(badgeInfo.getNotificationCount());
- int numChars = notificationCount.length();
- int width = DOTS_ONLY ? mSize : mSize + mCharSize * (numChars - 1);
- // Lazily load the background with shadow.
- Bitmap backgroundWithShadow = mBackgroundsWithShadow.get(numChars);
- if (backgroundWithShadow == null) {
- backgroundWithShadow = new ShadowGenerator.Builder(Color.WHITE)
- .setupBlurForSize(mSize).createPill(width, mSize);
- mBackgroundsWithShadow.put(numChars, backgroundWithShadow);
- }
canvas.save(Canvas.MATRIX_SAVE_FLAG);
// We draw the badge relative to its center.
- int badgeCenterX = iconBounds.right - width / 2;
+ int badgeCenterX = iconBounds.right - mSize / 2;
int badgeCenterY = iconBounds.top + mSize / 2;
- boolean isText = !DOTS_ONLY && badgeInfo != null && badgeInfo.getNotificationCount() != 0;
- boolean isIcon = !DOTS_ONLY && icon != null;
- boolean isDot = !(isText || isIcon);
- if (isDot) {
- badgeScale *= DOT_SCALE;
- }
+
int offsetX = Math.min(mOffset, spaceForOffset.x);
int offsetY = Math.min(mOffset, spaceForOffset.y);
canvas.translate(badgeCenterX + offsetX, badgeCenterY - offsetY);
canvas.scale(badgeScale, badgeScale);
- // Prepare the background and shadow and possible stacking effect.
- mBackgroundPaint.setColorFilter(palette.backgroundColorMatrixFilter);
- int backgroundWithShadowSize = backgroundWithShadow.getHeight(); // Same as width.
- boolean shouldStack = !isDot && badgeInfo != null
- && badgeInfo.getNotificationKeys().size() > 1;
- if (shouldStack) {
- int offsetDiffX = mStackOffsetX - mOffset;
- int offsetDiffY = mStackOffsetY - mOffset;
- canvas.translate(offsetDiffX, offsetDiffY);
- canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
- -backgroundWithShadowSize / 2, mBackgroundPaint);
- canvas.translate(-offsetDiffX, -offsetDiffY);
- }
- if (isText) {
- canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
- -backgroundWithShadowSize / 2, mBackgroundPaint);
- canvas.drawText(notificationCount, 0, mTextHeight / 2, mTextPaint);
- } else if (isIcon) {
- canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
- -backgroundWithShadowSize / 2, mBackgroundPaint);
- iconDrawer.drawIcon(icon, canvas);
- } else if (isDot) {
- mBackgroundPaint.setColorFilter(palette.saturatedBackgroundColorMatrixFilter);
- canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
- -backgroundWithShadowSize / 2, mBackgroundPaint);
- }
+ mCirclePaint.setColor(Color.BLACK);
+ canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset, mBitmapOffset, mCirclePaint);
+ mCirclePaint.setColor(color);
+ canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint);
canvas.restore();
}
-
- /** Draws the notification icon with padding of a given size. */
- private class IconDrawer {
-
- private final int mPadding;
- private final Bitmap mCircleClipBitmap;
- private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG |
- Paint.FILTER_BITMAP_FLAG);
-
- public IconDrawer(int padding) {
- mPadding = padding;
- mCircleClipBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ALPHA_8);
- Canvas canvas = new Canvas();
- canvas.setBitmap(mCircleClipBitmap);
- canvas.drawCircle(mSize / 2, mSize / 2, mSize / 2 - padding, mPaint);
- }
-
- public void drawIcon(Shader icon, Canvas canvas) {
- mPaint.setShader(icon);
- canvas.drawBitmap(mCircleClipBitmap, -mSize / 2, -mSize / 2, mPaint);
- mPaint.setShader(null);
- }
- }
}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
index 3214b46..5cd90b1 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
@@ -137,8 +137,7 @@
ShortcutInfoCompat compat = new ShortcutInfoCompat(request.getShortcutInfo());
ShortcutInfo info = new ShortcutInfo(compat, context);
// Apply the unbadged icon and fetch the actual icon asynchronously.
- info.iconBitmap = LauncherIcons
- .createShortcutIcon(compat, context, false /* badged */);
+ LauncherIcons.createShortcutIcon(compat, context, false /* badged */).applyTo(info);
LauncherAppState.getInstance(context).getModel()
.updateAndBindShortcutInfo(info, compat);
return info;
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
index 4cc70d3..299f090 100644
--- a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
@@ -18,6 +18,7 @@
import static android.app.WallpaperManager.FLAG_SYSTEM;
import static com.android.launcher3.Utilities.getDevicePrefs;
+import static com.android.launcher3.graphics.ColorExtractor.findDominantColorByHue;
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
@@ -257,7 +258,7 @@
String value = VERSION_PREFIX + wallpaperId;
if (bitmap != null) {
- int color = Utilities.findDominantColorByHue(bitmap, MAX_WALLPAPER_EXTRACTION_AREA);
+ int color = findDominantColorByHue(bitmap, MAX_WALLPAPER_EXTRACTION_AREA);
value += "," + color;
}
diff --git a/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java b/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java
index ed5cfeb..26e5066 100644
--- a/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java
+++ b/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java
@@ -18,12 +18,14 @@
import android.content.ComponentName;
import android.content.Intent;
+import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.android.launcher3.AppInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.graphics.ColorExtractor;
public class AppDiscoveryAppInfo extends AppInfo {
@@ -41,6 +43,8 @@
this.intent = item.isInstantApp ? item.launchIntent : item.installIntent;
this.title = item.title;
this.iconBitmap = item.bitmap;
+ this.iconColor = iconBitmap == null ? Color.TRANSPARENT :
+ ColorExtractor.findDominantColorByHue(item.bitmap);
this.usingLowResIcon = false;
this.isInstantApp = item.isInstantApp;
this.isRecent = item.isRecent;
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 27d6b89..c75e616 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -157,6 +157,8 @@
mTouchCompleteListener.onTouchComplete();
}
mTouchCompleteListener = null;
+ } else if (action == MotionEvent.ACTION_DOWN) {
+ mLauncher.finishAutoCancelActionMode();
}
mActiveController = null;
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 7c89df3..11ff88f 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -265,7 +265,7 @@
mDrawBitmap = !(dr instanceof FolderAdaptiveIcon);
if (info.isDisabled()) {
- FastBitmapDrawable d = new FastBitmapDrawable(null);
+ FastBitmapDrawable d = new FastBitmapDrawable((Bitmap) null);
d.setIsDisabled(true);
mBaseFilter = (ColorMatrixColorFilter) d.getColorFilter();
}
@@ -367,7 +367,8 @@
return new FixedSizeEmptyDrawable(iconSize);
}
ShortcutInfoCompat si = (ShortcutInfoCompat) obj;
- Bitmap badge = LauncherIcons.getShortcutInfoBadge(si, appState.getIconCache());
+ Bitmap badge = LauncherIcons.getShortcutInfoBadge(si, appState.getIconCache())
+ .iconBitmap;
float badgeSize = mLauncher.getResources().getDimension(R.dimen.profile_badge_size);
float insetFraction = (iconSize - badgeSize) / iconSize;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 5983029..4d8d171 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -63,7 +63,6 @@
import com.android.launcher3.dragndrop.BaseItemDragListener;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragView;
-import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -499,8 +498,7 @@
// 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);
- IconPalette badgePalette = IconPalette.getFolderBadgePalette(getResources());
- mBadgeRenderer.draw(canvas, badgePalette, mBadgeInfo, mTempBounds,
+ mBadgeRenderer.draw(canvas, mBackground.getBadgeColor(), mTempBounds,
badgeScale, mTempSpaceForBadgeOffset);
}
}
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index eba5d98..285aef8 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -200,6 +200,10 @@
return ColorUtils.setAlphaComponent(mBgColor, alpha);
}
+ public int getBadgeColor() {
+ return mBgColor;
+ }
+
public void drawBackground(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(getBgColor());
diff --git a/src/com/android/launcher3/graphics/BitmapInfo.java b/src/com/android/launcher3/graphics/BitmapInfo.java
new file mode 100644
index 0000000..ab906e2
--- /dev/null
+++ b/src/com/android/launcher3/graphics/BitmapInfo.java
@@ -0,0 +1,43 @@
+/*
+ * 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.graphics;
+
+import android.graphics.Bitmap;
+
+import com.android.launcher3.ItemInfoWithIcon;
+
+public class BitmapInfo {
+
+ public Bitmap icon;
+ public int color;
+
+ public void applyTo(ItemInfoWithIcon info) {
+ info.iconBitmap = icon;
+ info.iconColor = color;
+ }
+
+ public void applyTo(BitmapInfo info) {
+ info.icon = icon;
+ info.color = color;
+ }
+
+ public static BitmapInfo fromBitmap(Bitmap bitmap) {
+ BitmapInfo info = new BitmapInfo();
+ info.icon = bitmap;
+ info.color = ColorExtractor.findDominantColorByHue(bitmap);
+ return info;
+ }
+}
diff --git a/src/com/android/launcher3/graphics/ColorExtractor.java b/src/com/android/launcher3/graphics/ColorExtractor.java
new file mode 100644
index 0000000..e9d72b7
--- /dev/null
+++ b/src/com/android/launcher3/graphics/ColorExtractor.java
@@ -0,0 +1,113 @@
+/*
+ * 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.graphics;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.util.SparseArray;
+
+/**
+ * Utility class for extracting colors from a bitmap.
+ */
+public class ColorExtractor {
+
+ public static int findDominantColorByHue(Bitmap bitmap) {
+ return findDominantColorByHue(bitmap, 20);
+ }
+
+ /**
+ * This picks a dominant color, looking for high-saturation, high-value, repeated hues.
+ * @param bitmap The bitmap to scan
+ * @param samples The approximate max number of samples to use.
+ */
+ public static int findDominantColorByHue(Bitmap bitmap, int samples) {
+ final int height = bitmap.getHeight();
+ final int width = bitmap.getWidth();
+ int sampleStride = (int) Math.sqrt((height * width) / samples);
+ if (sampleStride < 1) {
+ sampleStride = 1;
+ }
+
+ // This is an out-param, for getting the hsv values for an rgb
+ float[] hsv = new float[3];
+
+ // First get the best hue, by creating a histogram over 360 hue buckets,
+ // where each pixel contributes a score weighted by saturation, value, and alpha.
+ float[] hueScoreHistogram = new float[360];
+ float highScore = -1;
+ int bestHue = -1;
+
+ int[] pixels = new int[samples];
+ int pixelCount = 0;
+
+ for (int y = 0; y < height; y += sampleStride) {
+ for (int x = 0; x < width; x += sampleStride) {
+ int argb = bitmap.getPixel(x, y);
+ int alpha = 0xFF & (argb >> 24);
+ if (alpha < 0x80) {
+ // Drop mostly-transparent pixels.
+ continue;
+ }
+ // Remove the alpha channel.
+ int rgb = argb | 0xFF000000;
+ Color.colorToHSV(rgb, hsv);
+ // Bucket colors by the 360 integer hues.
+ int hue = (int) hsv[0];
+ if (hue < 0 || hue >= hueScoreHistogram.length) {
+ // Defensively avoid array bounds violations.
+ continue;
+ }
+ if (pixelCount < samples) {
+ pixels[pixelCount++] = rgb;
+ }
+ float score = hsv[1] * hsv[2];
+ hueScoreHistogram[hue] += score;
+ if (hueScoreHistogram[hue] > highScore) {
+ highScore = hueScoreHistogram[hue];
+ bestHue = hue;
+ }
+ }
+ }
+
+ SparseArray<Float> rgbScores = new SparseArray<>();
+ int bestColor = 0xff000000;
+ highScore = -1;
+ // Go back over the RGB colors that match the winning hue,
+ // creating a histogram of weighted s*v scores, for up to 100*100 [s,v] buckets.
+ // The highest-scoring RGB color wins.
+ for (int i = 0; i < pixelCount; i++) {
+ int rgb = pixels[i];
+ Color.colorToHSV(rgb, hsv);
+ int hue = (int) hsv[0];
+ if (hue == bestHue) {
+ float s = hsv[1];
+ float v = hsv[2];
+ int bucket = (int) (s * 100) + (int) (v * 10000);
+ // Score by cumulative saturation * value.
+ float score = s * v;
+ Float oldTotal = rgbScores.get(bucket);
+ float newTotal = oldTotal == null ? score : oldTotal + score;
+ rgbScores.put(bucket, newTotal);
+ if (newTotal > highScore) {
+ highScore = newTotal;
+ // All the colors in the winning bucket are very similar. Last in wins.
+ bestColor = rgb;
+ }
+ }
+ }
+ return bestColor;
+ }
+}
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 902906f..6a328e9 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -31,7 +31,7 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index 371479b..32d9e41 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -31,7 +31,7 @@
import android.util.ArrayMap;
import android.util.Log;
import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.ItemInfo;
+import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsBackgroundDrawable;
@@ -64,18 +64,18 @@
/**
* Returns a FastBitmapDrawable with the icon.
*/
- public FastBitmapDrawable newIcon(Bitmap icon, ItemInfo info) {
- return new FastBitmapDrawable(icon);
+ public FastBitmapDrawable newIcon(ItemInfoWithIcon info) {
+ return new FastBitmapDrawable(info);
}
/**
* Returns a FastBitmapDrawable with the icon.
*/
- public PreloadIconDrawable newPendingIcon(Bitmap icon, Context context) {
+ public PreloadIconDrawable newPendingIcon(ItemInfoWithIcon info, Context context) {
if (mPreloadProgressPath == null) {
mPreloadProgressPath = getPreloadProgressPath(context);
}
- return new PreloadIconDrawable(icon, mPreloadProgressPath, context);
+ return new PreloadIconDrawable(info, mPreloadProgressPath, context);
}
diff --git a/src/com/android/launcher3/graphics/IconPalette.java b/src/com/android/launcher3/graphics/IconPalette.java
index 6e01ed5..9c3b77e 100644
--- a/src/com/android/launcher3/graphics/IconPalette.java
+++ b/src/com/android/launcher3/graphics/IconPalette.java
@@ -18,12 +18,7 @@
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;
@@ -41,37 +36,10 @@
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;
- public final ColorMatrixColorFilter saturatedBackgroundColorMatrixFilter;
- public final int textColor;
- public final int secondaryColor;
-
- private IconPalette(int color, boolean desaturateBackground) {
- dominantColor = color;
- backgroundColor = desaturateBackground ? getMutedColor(dominantColor, 0.87f) : dominantColor;
- ColorMatrix backgroundColorMatrix = new ColorMatrix();
- Themes.setColorScaleOnMatrix(backgroundColor, backgroundColorMatrix);
- backgroundColorMatrixFilter = 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);
- }
-
/**
* Returns a color suitable for the progress bar color of preload icon.
*/
- public int getPreloadProgressColor(Context context) {
+ public static int getPreloadProgressColor(Context context, int dominantColor) {
int result = dominantColor;
// Make sure that the dominant color has enough saturation to be visible properly.
@@ -86,37 +54,6 @@
return result;
}
- 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;
- }
-
/**
* Resolves a color such that it has enough contrast to be used as the
* color of an icon or text on the given background color.
@@ -208,30 +145,8 @@
return ColorUtils.LABToColor(low, a, b);
}
- private static int getMutedColor(int color, float whiteScrimAlpha) {
+ public static int getMutedColor(int color, float whiteScrimAlpha) {
int whiteScrim = ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * whiteScrimAlpha));
return ColorUtils.compositeColors(whiteScrim, color);
}
-
- private static int getTextColorForBackground(int backgroundColor) {
- return getLighterOrDarkerVersionOfColor(backgroundColor, 4.5f);
- }
-
- private static int getLowContrastColor(int color) {
- return getLighterOrDarkerVersionOfColor(color, 1.5f);
- }
-
- private static int getLighterOrDarkerVersionOfColor(int color, float contrastRatio) {
- int whiteMinAlpha = ColorUtils.calculateMinimumAlpha(Color.WHITE, color, contrastRatio);
- int blackMinAlpha = ColorUtils.calculateMinimumAlpha(Color.BLACK, color, contrastRatio);
- int translucentWhiteOrBlack;
- if (whiteMinAlpha >= 0) {
- translucentWhiteOrBlack = ColorUtils.setAlphaComponent(Color.WHITE, whiteMinAlpha);
- } else if (blackMinAlpha >= 0) {
- translucentWhiteOrBlack = ColorUtils.setAlphaComponent(Color.BLACK, blackMinAlpha);
- } else {
- translucentWhiteOrBlack = Color.WHITE;
- }
- return ColorUtils.compositeColors(translucentWhiteOrBlack, color);
- }
}
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 8c4738c..fdb6313 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.Intent.ShortcutIconResource;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -42,6 +41,7 @@
import com.android.launcher3.AppInfo;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.IconCache;
+import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -51,6 +51,7 @@
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.Provider;
+import com.android.launcher3.util.Themes;
/**
* Helper methods for generating various launcher icons
@@ -69,7 +70,7 @@
* Returns a bitmap suitable for the all apps view. If the package or the resource do not
* exist, it returns null.
*/
- public static Bitmap createIconBitmap(ShortcutIconResource iconRes, Context context) {
+ public static BitmapInfo createIconBitmap(ShortcutIconResource iconRes, Context context) {
PackageManager packageManager = context.getPackageManager();
// the resource
try {
@@ -92,12 +93,13 @@
/**
* Returns a bitmap which is of the appropriate size to be displayed as an icon
*/
- public static Bitmap createIconBitmap(Bitmap icon, Context context) {
+ public static BitmapInfo createIconBitmap(Bitmap icon, Context context) {
final int iconBitmapSize = LauncherAppState.getIDP(context).iconBitmapSize;
if (iconBitmapSize == icon.getWidth() && iconBitmapSize == icon.getHeight()) {
- return icon;
+ return BitmapInfo.fromBitmap(icon);
}
- return createIconBitmap(new BitmapDrawable(context.getResources(), icon), context, 1f);
+ return BitmapInfo.fromBitmap(
+ createIconBitmap(new BitmapDrawable(context.getResources(), icon), context, 1f));
}
/**
@@ -105,7 +107,7 @@
* view or workspace. The icon is badged for {@param user}.
* The bitmap is also visually normalized with other icons.
*/
- public static Bitmap createBadgedIconBitmap(
+ public static BitmapInfo createBadgedIconBitmap(
Drawable icon, UserHandle user, Context context, int iconAppTargetSdk) {
IconNormalizer normalizer;
@@ -141,18 +143,20 @@
}
}
+ final Bitmap result;
if (user != null && !Process.myUserHandle().equals(user)) {
BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap);
Drawable badged = context.getPackageManager().getUserBadgedIcon(
drawable, user);
if (badged instanceof BitmapDrawable) {
- return ((BitmapDrawable) badged).getBitmap();
+ result = ((BitmapDrawable) badged).getBitmap();
} else {
- return createIconBitmap(badged, context, 1f);
+ result = createIconBitmap(badged, context, 1f);
}
} else {
- return bitmap;
+ result = bitmap;
}
+ return BitmapInfo.fromBitmap(result);
}
/**
@@ -302,29 +306,23 @@
return drawable;
}
- public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context) {
+ public static BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context) {
return createShortcutIcon(shortcutInfo, context, true /* badged */);
}
- public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
+ public static BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
boolean badged) {
return createShortcutIcon(shortcutInfo, context, badged, null);
}
- public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
- final Bitmap fallbackIcon) {
- // If the shortcut is pinned but no longer has an icon in the system,
- // keep the current icon instead of reverting to the default icon.
- return createShortcutIcon(shortcutInfo, context, true, Provider.of(fallbackIcon));
- }
-
- public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
+ public static BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
boolean badged, @Nullable Provider<Bitmap> fallbackIconProvider) {
LauncherAppState app = LauncherAppState.getInstance(context);
Drawable unbadgedDrawable = DeepShortcutManager.getInstance(context)
.getShortcutIconDrawable(shortcutInfo,
app.getInvariantDeviceProfile().fillResIconDpi);
IconCache cache = app.getIconCache();
+
Bitmap unbadgedBitmap = null;
if (unbadgedDrawable != null) {
unbadgedBitmap = LauncherIcons.createScaledBitmapWithoutShadow(
@@ -334,27 +332,32 @@
unbadgedBitmap = fallbackIconProvider.get();
}
if (unbadgedBitmap == null) {
- unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle());
+ unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
}
}
+ BitmapInfo result = new BitmapInfo();
if (!badged) {
- return unbadgedBitmap;
+ result.color = Themes.getColorAccent(context);
+ result.icon = unbadgedBitmap;
+ return result;
}
int size = app.getInvariantDeviceProfile().iconBitmapSize;
final Bitmap unbadgedfinal = unbadgedBitmap;
- final Bitmap badge = getShortcutInfoBadge(shortcutInfo, cache);
+ final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
- return UiFactory.createFromRenderer(size, size, false, (c) -> {
+ result.color = badge.iconColor;
+ result.icon = UiFactory.createFromRenderer(size, size, false, (c) -> {
ShadowGenerator.getInstance(context).recreateIcon(unbadgedfinal, c);
badgeWithDrawable(c, new FastBitmapDrawable(badge), context);
});
+ return result;
}
- public static Bitmap getShortcutInfoBadge(ShortcutInfoCompat shortcutInfo, IconCache cache) {
- final Bitmap badgeBitmap;
+ public static ItemInfoWithIcon getShortcutInfoBadge(
+ ShortcutInfoCompat shortcutInfo, IconCache cache) {
ComponentName cn = shortcutInfo.getActivity();
if (cn != null) {
// Get the app info for the source activity.
@@ -365,13 +368,12 @@
.addCategory(Intent.CATEGORY_LAUNCHER)
.setComponent(cn);
cache.getTitleAndIcon(appInfo, false);
- badgeBitmap = appInfo.iconBitmap;
+ return appInfo;
} else {
PackageItemInfo pkgInfo = new PackageItemInfo(shortcutInfo.getPackage());
cache.getTitleAndIconForApp(pkgInfo, false);
- badgeBitmap = pkgInfo.iconBitmap;
+ return pkgInfo;
}
- return badgeBitmap;
}
/**
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index 6d486ee..a40b6df 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -32,6 +32,7 @@
import android.util.SparseArray;
import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.anim.Interpolators;
import java.lang.ref.WeakReference;
@@ -86,7 +87,7 @@
private final Paint mProgressPaint;
private Bitmap mShadowBitmap;
- private int mIndicatorColor = 0;
+ private final int mIndicatorColor;
private int mTrackAlpha;
private float mTrackLength;
@@ -103,8 +104,8 @@
/**
* @param progressPath fixed path in the bounds [0, 0, 100, 100] representing a progress bar.
*/
- public PreloadIconDrawable(Bitmap b, Path progressPath, Context context) {
- super(b);
+ public PreloadIconDrawable(ItemInfoWithIcon info, Path progressPath, Context context) {
+ super(info);
mContext = context;
mProgressPath = progressPath;
mScaledTrackPath = new Path();
@@ -113,6 +114,7 @@
mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
mProgressPaint.setStyle(Paint.Style.STROKE);
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
+ mIndicatorColor = IconPalette.getPreloadProgressColor(context, mIconColor);
setInternalProgress(0);
}
@@ -266,9 +268,6 @@
mScaledTrackPath.reset();
mTrackAlpha = MAX_PAINT_ALPHA;
setIsDisabled(true);
- } else if (mIndicatorColor == 0) {
- // Update the indicator color
- mIndicatorColor = getIconPalette().getPreloadProgressColor(mContext);
}
if (progress < 1 && progress > 0) {
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index ccef9b7..b1d07f1 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -44,6 +44,7 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.BitmapInfo;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.util.ContentWriter;
@@ -151,10 +152,9 @@
info.user = user;
info.itemType = itemType;
info.title = getTitle();
- info.iconBitmap = loadIcon(info);
// the fallback icon
- if (info.iconBitmap == null) {
- info.iconBitmap = mIconCache.getDefaultIcon(info.user);
+ if (!loadIcon(info)) {
+ mIconCache.getDefaultIcon(info.user).applyTo(info);
}
// TODO: If there's an explicit component and we can't install that, delete it.
@@ -165,8 +165,7 @@
/**
* Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
*/
- protected Bitmap loadIcon(ShortcutInfo info) {
- Bitmap icon = null;
+ protected boolean loadIcon(ShortcutInfo info) {
if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
String packageName = getString(iconPackageIndex);
String resourceName = getString(iconResourceIndex);
@@ -174,24 +173,24 @@
info.iconResource = new ShortcutIconResource();
info.iconResource.packageName = packageName;
info.iconResource.resourceName = resourceName;
- icon = LauncherIcons.createIconBitmap(info.iconResource, mContext);
+ BitmapInfo iconInfo = LauncherIcons.createIconBitmap(info.iconResource, mContext);
+ if (iconInfo != null) {
+ iconInfo.applyTo(info);
+ return true;
+ }
}
}
- if (icon == null) {
- // Failed to load from resource, try loading from DB.
- byte[] data = getBlob(iconIndex);
- try {
- icon = LauncherIcons.createIconBitmap(
- BitmapFactory.decodeByteArray(data, 0, data.length), mContext);
- } catch (Exception e) {
- Log.e(TAG, "Failed to load icon for info " + info, e);
- return null;
- }
+
+ // Failed to load from resource, try loading from DB.
+ byte[] data = getBlob(iconIndex);
+ try {
+ LauncherIcons.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length),
+ mContext).applyTo(info);
+ return true;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to load icon for info " + info, e);
+ return false;
}
- if (icon == null) {
- Log.e(TAG, "Failed to load icon for info " + info);
- }
- return icon;
}
/**
@@ -211,9 +210,8 @@
info.user = user;
info.intent = intent;
- info.iconBitmap = loadIcon(info);
// the fallback icon
- if (info.iconBitmap == null) {
+ if (!loadIcon(info)) {
mIconCache.getTitleAndIcon(info, false /* useLowResIcon */);
}
@@ -269,8 +267,7 @@
mIconCache.getTitleAndIcon(info, lai, useLowResIcon);
if (mIconCache.isDefaultIcon(info.iconBitmap, user)) {
- Bitmap icon = loadIcon(info);
- info.iconBitmap = icon != null ? icon : info.iconBitmap;
+ loadIcon(info);
}
if (lai != null) {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 310416f..b13b48a 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -472,12 +472,12 @@
public Bitmap get() {
// If the pinned deep shortcut is no longer published,
// use the last saved icon instead of the default.
- return c.loadIcon(finalInfo);
+ return c.loadIcon(finalInfo)
+ ? finalInfo.iconBitmap : null;
}
};
- info.iconBitmap = LauncherIcons
- .createShortcutIcon(pinnedShortcut, context,
- true /* badged */, fallbackIconProvider);
+ LauncherIcons.createShortcutIcon(pinnedShortcut, context,
+ true /* badged */, fallbackIconProvider).applyTo(info);
if (pmHelper.isAppSuspended(
pinnedShortcut.getPackage(), info.user)) {
info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 470dadf..18ae61b 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -40,6 +40,7 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.BitmapInfo;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -191,9 +192,10 @@
// Update shortcuts which use iconResource.
if ((si.iconResource != null)
&& packageSet.contains(si.iconResource.packageName)) {
- Bitmap icon = LauncherIcons.createIconBitmap(si.iconResource, context);
- if (icon != null) {
- si.iconBitmap = icon;
+ BitmapInfo iconInfo =
+ LauncherIcons.createIconBitmap(si.iconResource, context);
+ if (iconInfo != null) {
+ iconInfo.applyTo(si);
infoUpdated = true;
}
}
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index c1f33a6..0b75e2c 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -29,6 +29,7 @@
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiHashMap;
+import com.android.launcher3.util.Provider;
import java.util.ArrayList;
import java.util.HashSet;
@@ -92,8 +93,10 @@
}
for (final ShortcutInfo shortcutInfo : shortcutInfos) {
shortcutInfo.updateFromDeepShortcutInfo(fullDetails, context);
- shortcutInfo.iconBitmap = LauncherIcons.createShortcutIcon(fullDetails, context,
- shortcutInfo.iconBitmap);
+ // If the shortcut is pinned but no longer has an icon in the system,
+ // keep the current icon instead of reverting to the default icon.
+ LauncherIcons.createShortcutIcon(fullDetails, context, true,
+ Provider.of(shortcutInfo.iconBitmap)).applyTo(shortcutInfo);
updatedShortcutInfos.add(shortcutInfo);
}
}
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 2e9ac72..b033405 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -32,6 +32,7 @@
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.Provider;
import java.util.ArrayList;
import java.util.HashMap;
@@ -91,8 +92,10 @@
}
si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
si.updateFromDeepShortcutInfo(shortcut, context);
- si.iconBitmap = LauncherIcons.createShortcutIcon(shortcut, context,
- si.iconBitmap);
+ // If the shortcut is pinned but no longer has an icon in the system,
+ // keep the current icon instead of reverting to the default icon.
+ LauncherIcons.createShortcutIcon(shortcut, context, true,
+ Provider.of(si.iconBitmap)).applyTo(si);
} else {
si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
}
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index 120de04..6918935 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -83,7 +83,7 @@
if (mIconDrawable == null) {
mIconDrawable = new BitmapDrawable(context.getResources(), LauncherAppState
.getInstance(context).getIconCache()
- .getDefaultIcon(statusBarNotification.getUser()));
+ .getDefaultIcon(statusBarNotification.getUser()).icon);
mBadgeIcon = Notification.BADGE_ICON_NONE;
}
intent = notification.contentIntent;
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index 5bbd19c..2fefa85 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -18,6 +18,7 @@
import android.app.Notification;
import android.content.Context;
+import android.graphics.Color;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.view.MotionEvent;
@@ -114,12 +115,12 @@
}
}
- public void updateHeader(int notificationCount, @Nullable IconPalette palette) {
+ public void updateHeader(int notificationCount, int iconColor) {
mHeaderCount.setText(notificationCount <= 1 ? "" : String.valueOf(notificationCount));
- if (palette != null) {
+ if (Color.alpha(iconColor) > 0) {
if (mNotificationHeaderTextColor == Notification.COLOR_DEFAULT) {
mNotificationHeaderTextColor =
- IconPalette.resolveContrastColor(mContext, palette.dominantColor,
+ IconPalette.resolveContrastColor(mContext, iconColor,
Themes.getAttrColor(mContext, R.attr.popupColorPrimary));
}
mHeaderText.setTextColor(mNotificationHeaderTextColor);
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 6481183..cedf291 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -54,6 +54,7 @@
import com.android.launcher3.DropTarget;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.ItemInfo;
+import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherModel;
@@ -742,11 +743,11 @@
}
private void updateNotificationHeader() {
- ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
+ ItemInfoWithIcon itemInfo = (ItemInfoWithIcon) mOriginalIcon.getTag();
BadgeInfo badgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo);
if (mNotificationItemView != null && badgeInfo != null) {
- IconPalette palette = mOriginalIcon.getBadgePalette();
- mNotificationItemView.updateHeader(badgeInfo.getNotificationCount(), palette);
+ mNotificationItemView.updateHeader(
+ badgeInfo.getNotificationCount(), itemInfo.iconColor);
}
}
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index 6c83d12..4adfb7c 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -148,8 +148,8 @@
final ShortcutInfoCompat shortcut = shortcuts.get(i);
final ShortcutInfo si = new ShortcutInfo(shortcut, launcher);
// Use unbadged icon for the menu.
- si.iconBitmap = LauncherIcons.createShortcutIcon(
- shortcut, launcher, false /* badged */);
+ LauncherIcons.createShortcutIcon(shortcut, launcher, false /* badged */)
+ .applyTo(si);
si.rank = i;
final DeepShortcutView view = shortcutViews.get(i);
diff --git a/src/com/android/launcher3/util/RunnableWithId.java b/src/com/android/launcher3/util/RunnableWithId.java
deleted file mode 100644
index 030eb09..0000000
--- a/src/com/android/launcher3/util/RunnableWithId.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.util;
-
-/**
- * A runnable with an id associated which is used for equality check.
- */
-public abstract class RunnableWithId implements Runnable {
-
- public static final int RUNNABLE_ID_BIND_APPS = 1;
- public static final int RUNNABLE_ID_BIND_WIDGETS = 2;
-
- public final int id;
-
- public RunnableWithId(int id) {
- this.id = id;
- }
-
- @Override
- public boolean equals(Object obj) {
- return obj instanceof RunnableWithId && ((RunnableWithId) obj).id == id;
- }
-}
diff --git a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
new file mode 100644
index 0000000..37e5efcb
--- /dev/null
+++ b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
@@ -0,0 +1,83 @@
+/*
+ * 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.widget;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.util.TypedValue;
+import android.widget.RemoteViews;
+
+import com.android.launcher3.R;
+
+/**
+ * A widget host views created while the host has not bind to the system service.
+ */
+public class DeferredAppWidgetHostView extends LauncherAppWidgetHostView {
+
+ private final TextPaint mPaint;
+ private Layout mSetupTextLayout;
+
+ public DeferredAppWidgetHostView(Context context) {
+ super(context);
+ setWillNotDraw(false);
+
+ mPaint = new TextPaint();
+ mPaint.setColor(Color.WHITE);
+ mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
+ mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics()));
+ setBackgroundResource(R.drawable.bg_deferred_app_widget);
+ }
+
+ @Override
+ public void updateAppWidget(RemoteViews remoteViews) {
+ // Not allowed
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ AppWidgetProviderInfo info = getAppWidgetInfo();
+ if (info == null || TextUtils.isEmpty(info.label)) {
+ return;
+ }
+
+ // Use double padding so that there is extra space between background and text
+ int availableWidth = getMeasuredWidth() - 2 * (getPaddingLeft() + getPaddingRight());
+ if (mSetupTextLayout != null && mSetupTextLayout.getText().equals(info.label)
+ && mSetupTextLayout.getWidth() == availableWidth) {
+ return;
+ }
+ mSetupTextLayout = new StaticLayout(info.label, mPaint, availableWidth,
+ Layout.Alignment.ALIGN_CENTER, 1, 0, true);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mSetupTextLayout != null) {
+ canvas.translate(getPaddingLeft() * 2,
+ (getHeight() - mSetupTextLayout.getHeight()) / 2);
+ mSetupTextLayout.draw(canvas);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
similarity index 92%
rename from src/com/android/launcher3/LauncherAppWidgetHostView.java
rename to src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 6f953e5..0b1474a 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.launcher3.widget;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
@@ -37,6 +37,15 @@
import android.widget.Advanceable;
import android.widget.RemoteViews;
+import com.android.launcher3.CheckLongPressHelper;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.SimpleOnStylusPressListener;
+import com.android.launcher3.StylusEventHelper;
+import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener;
@@ -59,14 +68,10 @@
private final CheckLongPressHelper mLongPressHelper;
private final StylusEventHelper mStylusEventHelper;
- private final Launcher mLauncher;
-
- private static final int DONT_REINFLATE = 0;
- private static final int REINFLATE_ON_RESUME = 1;
- private static final int REINFLATE_ON_CONFIG_CHANGE = 2;
+ protected final Launcher mLauncher;
@ViewDebug.ExportedProperty(category = "launcher")
- private int mReinflateStatus;
+ private boolean mReinflateOnConfigChange;
private float mSlop;
@@ -128,12 +133,7 @@
// Consequently, the widgets will be inflated for the orientation of the foreground activity
// (framework issue). On resuming, we ensure that any widgets are inflated for the current
// orientation.
- if (mReinflateStatus == DONT_REINFLATE && !isSameOrientation()) {
- mReinflateStatus = REINFLATE_ON_RESUME;
- if (!mLauncher.waitUntilResume(new ReInflateRunnable())) {
- mReinflateStatus = REINFLATE_ON_CONFIG_CHANGE;
- }
- }
+ mReinflateOnConfigChange = !isSameOrientation();
}
private boolean isSameOrientation() {
@@ -485,40 +485,20 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- if (mReinflateStatus == REINFLATE_ON_CONFIG_CHANGE) {
- // We are finally in the same orientation
- reinflateIfNecessary();
+ // Only reinflate when the final configuration is same as the required configuration
+ if (mReinflateOnConfigChange && isSameOrientation()) {
+ mReinflateOnConfigChange = false;
+ if (isAttachedToWindow()) {
+ reInflate();
+ }
}
}
- private void reinflateIfNecessary() {
- if (!isSameOrientation()) {
- // We cannot reinflate yet, wait until next config change
- mReinflateStatus = REINFLATE_ON_CONFIG_CHANGE;
- return;
- }
-
- mReinflateStatus = DONT_REINFLATE;
- if (isAttachedToWindow()) {
- LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
- reinflate();
- }
- }
-
- public void reinflate() {
+ public void reInflate() {
LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
// Remove and rebind the current widget (which was inflated in the wrong
// orientation), but don't delete it from the database
mLauncher.removeItem(this, info, false /* deleteFromDb */);
mLauncher.bindAppWidget(info);
}
-
- private class ReInflateRunnable implements Runnable {
- @Override
- public void run() {
- if (mReinflateStatus == REINFLATE_ON_RESUME) {
- reinflateIfNecessary();
- }
- }
- }
}
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
similarity index 92%
rename from src/com/android/launcher3/PendingAppWidgetHostView.java
rename to src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index b86d413..6970833 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.launcher3.widget;
import android.content.Context;
import android.graphics.Bitmap;
@@ -32,10 +32,19 @@
import android.view.View;
import android.view.View.OnClickListener;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.IconCache;
import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
implements OnClickListener, ItemInfoUpdateReceiver {
@@ -48,9 +57,6 @@
private final LauncherAppWidgetInfo mInfo;
private final int mStartState;
private final boolean mDisabledForSafeMode;
- private Launcher mLauncher;
-
- private Bitmap mIcon;
private Drawable mCenterDrawable;
private Drawable mSettingIconDrawable;
@@ -64,7 +70,6 @@
IconCache cache, boolean disabledForSafeMode) {
super(new ContextThemeWrapper(context, R.style.WidgetContainerTheme));
- mLauncher = Launcher.getLauncher(context);
mInfo = info;
mStartState = info.restoreStatus;
mDisabledForSafeMode = disabledForSafeMode;
@@ -122,53 +127,44 @@
@Override
public void reapplyItemInfo(ItemInfoWithIcon info) {
- Bitmap icon = info.iconBitmap;
- if (mIcon == icon) {
- return;
- }
- mIcon = icon;
if (mCenterDrawable != null) {
mCenterDrawable.setCallback(null);
mCenterDrawable = null;
}
- if (mIcon != null) {
+ if (info.iconBitmap != null) {
// The view displays three modes,
// 1) App icon in the center
// 2) Preload icon in the center
// 3) Setup icon in the center and app icon in the top right corner.
DrawableFactory drawableFactory = DrawableFactory.get(getContext());
if (mDisabledForSafeMode) {
- FastBitmapDrawable disabledIcon = drawableFactory.newIcon(mIcon, mInfo);
+ FastBitmapDrawable disabledIcon = drawableFactory.newIcon(info);
disabledIcon.setIsDisabled(true);
mCenterDrawable = disabledIcon;
mSettingIconDrawable = null;
} else if (isReadyForClickSetup()) {
- mCenterDrawable = drawableFactory.newIcon(mIcon, mInfo);
+ mCenterDrawable = drawableFactory.newIcon(info);
mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate();
-
- updateSettingColor();
+ updateSettingColor(info.iconColor);
} else {
mCenterDrawable = DrawableFactory.get(getContext())
- .newPendingIcon(mIcon, getContext());
- mCenterDrawable.setCallback(this);
+ .newPendingIcon(info, getContext());
mSettingIconDrawable = null;
applyState();
}
+ mCenterDrawable.setCallback(this);
mDrawableSizeChanged = true;
}
invalidate();
}
- private void updateSettingColor() {
- int color = Utilities.findDominantColorByHue(mIcon, 20);
+ private void updateSettingColor(int dominantColor) {
// Make the dominant color bright.
float[] hsv = new float[3];
- Color.colorToHSV(color, hsv);
+ Color.colorToHSV(dominantColor, hsv);
hsv[1] = Math.min(hsv[1], MIN_SATUNATION);
hsv[2] = 1;
- color = Color.HSVToColor(hsv);
-
- mSettingIconDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ mSettingIconDrawable.setColorFilter(Color.HSVToColor(hsv), PorterDuff.Mode.SRC_IN);
}
@Override
diff --git a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
index bbb6772..cf90afd 100644
--- a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -9,12 +9,12 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
+import android.graphics.Color;
import android.os.Process;
import android.os.UserHandle;
import android.support.annotation.NonNull;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.provider.ProviderTestRule;
-import android.support.test.runner.AndroidJUnit4;
import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppFilter;
@@ -27,6 +27,7 @@
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherProvider;
+import com.android.launcher3.graphics.BitmapInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.Provider;
import com.android.launcher3.util.TestLauncherProvider;
@@ -208,7 +209,7 @@
CacheEntry entry = mCache.get(new ComponentKey(componentName, user));
if (entry == null) {
entry = new CacheEntry();
- entry.icon = getDefaultIcon(user);
+ getDefaultIcon(user).applyTo(entry);
}
return entry;
}
@@ -216,6 +217,7 @@
public void addCache(ComponentName key, String title) {
CacheEntry entry = new CacheEntry();
entry.icon = newIcon();
+ entry.color = Color.RED;
entry.title = title;
mCache.put(new ComponentKey(key, Process.myUserHandle()), entry);
}
@@ -225,8 +227,8 @@
}
@Override
- protected Bitmap makeDefaultIcon(UserHandle user) {
- return newIcon();
+ protected BitmapInfo makeDefaultIcon(UserHandle user) {
+ return BitmapInfo.fromBitmap(newIcon());
}
}
}
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 173c556..dfefa31 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -17,6 +17,7 @@
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.graphics.BitmapInfo;
import org.junit.Before;
import org.junit.Test;
@@ -138,7 +139,8 @@
assertTrue(mLoaderCursor.moveToNext());
Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
- when(mMockIconCache.getDefaultIcon(eq(mLoaderCursor.user))).thenReturn(icon);
+ when(mMockIconCache.getDefaultIcon(eq(mLoaderCursor.user)))
+ .thenReturn(BitmapInfo.fromBitmap(icon));
ShortcutInfo info = mLoaderCursor.loadSimpleShortcut();
assertEquals(icon, info.iconBitmap);
assertEquals("my-shortcut", info.title);
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index 87103d7..32f90a6 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -30,12 +30,12 @@
import android.support.test.uiautomator.UiSelector;
import com.android.launcher3.LauncherAppWidgetHost;
-import com.android.launcher3.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.PendingAppWidgetHostView;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
import com.android.launcher3.Workspace;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.PackageInstallerCompat;