Snap for 5048123 from c96f0cc323 to qt-release

Change-Id: If6cb7420c7faab11b0eae57f2ad9659d96c6e26d
This commit is contained in:
android-build-team Robot
2018-10-04 03:05:05 +00:00
73 changed files with 1361 additions and 848 deletions
Binary file not shown.
@@ -84,7 +84,8 @@ public class StatusBarTouchController implements TouchController {
}
if (action == ACTION_MOVE) {
float dy = ev.getY() - mTranslator.getDownY();
if (dy > mTouchSlop) {
float dx = ev.getX() - mTranslator.getDownX();
if (dy > mTouchSlop && dy > Math.abs(dx)) {
mTranslator.dispatchDownEvents(ev);
mTranslator.processMotionEvent(ev);
return true;
@@ -220,7 +220,7 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
}
@Override
public boolean onDrag(float displacement, float velocity) {
public boolean onDrag(float displacement) {
float totalDisplacement = displacement + mDisplacementShift;
boolean isGoingUp =
totalDisplacement == 0 ? mCurrentAnimationIsGoingUp : totalDisplacement < 0;
@@ -91,7 +91,7 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
* Updates the UI to indicate quick interaction.
*/
void onQuickInteractionStart(T activity, @Nullable RunningTaskInfo taskInfo,
boolean activityVisible);
boolean activityVisible, TouchInteractionLog touchInteractionLog);
float getTranslationYForQuickScrub(TransformedRect targetRect, DeviceProfile dp,
Context context);
@@ -153,13 +153,14 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
@Override
public void onQuickInteractionStart(Launcher activity, RunningTaskInfo taskInfo,
boolean activityVisible) {
boolean activityVisible, TouchInteractionLog touchInteractionLog) {
LauncherState fromState = activity.getStateManager().getState();
activity.getStateManager().goToState(FAST_OVERVIEW, activityVisible);
QuickScrubController controller = activity.<RecentsView>getOverviewPanel()
.getQuickScrubController();
controller.onQuickScrubStart(activityVisible && !fromState.overviewUi, this);
controller.onQuickScrubStart(activityVisible && !fromState.overviewUi, this,
touchInteractionLog);
if (!activityVisible) {
// For the duration of the gesture, lock the screen orientation to ensure that we
@@ -425,14 +426,14 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
@Override
public void onQuickInteractionStart(RecentsActivity activity, RunningTaskInfo taskInfo,
boolean activityVisible) {
boolean activityVisible, TouchInteractionLog touchInteractionLog) {
QuickScrubController controller = activity.<RecentsView>getOverviewPanel()
.getQuickScrubController();
// TODO: match user is as well
boolean startingFromHome = !activityVisible &&
(taskInfo == null || Objects.equals(taskInfo.topActivity, mHomeComponent));
controller.onQuickScrubStart(startingFromHome, this);
controller.onQuickScrubStart(startingFromHome, this, touchInteractionLog);
if (activityVisible) {
mUiHandler.postDelayed(controller::onFinishedTransitionToQuickScrub,
OVERVIEW_TRANSITION_MS);
@@ -49,8 +49,8 @@ public class DeferredTouchConsumer implements TouchConsumer {
}
@Override
public void updateTouchTracking(int interactionType) {
mTarget.updateTouchTracking(interactionType);
public void onQuickScrubStart() {
mTarget.onQuickScrubStart();
}
@Override
@@ -146,7 +146,7 @@ public class MotionEventQueue {
if (event.getActionMasked() == ACTION_VIRTUAL) {
switch (event.getAction()) {
case ACTION_QUICK_SCRUB_START:
mConsumer.updateTouchTracking(INTERACTION_QUICK_SCRUB);
mConsumer.onQuickScrubStart();
break;
case ACTION_QUICK_SCRUB_PROGRESS:
mConsumer.onQuickScrubProgress(event.getX());
@@ -162,7 +162,7 @@ public class MotionEventQueue {
break;
case ACTION_SHOW_OVERVIEW_FROM_ALT_TAB:
mConsumer.onShowOverviewFromAltTab();
mConsumer.updateTouchTracking(INTERACTION_QUICK_SCRUB);
mConsumer.onQuickScrubStart();
break;
case ACTION_QUICK_STEP:
mConsumer.onQuickStep(event);
@@ -28,9 +28,9 @@ import android.util.LruCache;
import android.util.SparseArray;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.graphics.BitmapInfo;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.icons.LauncherIcons;
import com.android.systemui.shared.recents.model.IconLoader;
import com.android.systemui.shared.recents.model.TaskKeyLruCache;
@@ -79,6 +79,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
private final Choreographer mBackgroundThreadChoreographer;
private final OverviewCallbacks mOverviewCallbacks;
private final TaskOverlayFactory mTaskOverlayFactory;
private final TouchInteractionLog mTouchInteractionLog;
private final boolean mIsDeferredDownTarget;
private final PointF mDownPos = new PointF();
@@ -100,7 +101,8 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl,
MainThreadExecutor mainThreadExecutor, Choreographer backgroundThreadChoreographer,
@HitTarget int downHitTarget, OverviewCallbacks overviewCallbacks,
TaskOverlayFactory taskOverlayFactory, VelocityTracker velocityTracker) {
TaskOverlayFactory taskOverlayFactory, VelocityTracker velocityTracker,
TouchInteractionLog touchInteractionLog) {
super(base);
mRunningTask = runningTaskInfo;
@@ -113,6 +115,8 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
mIsDeferredDownTarget = activityControl.deferStartingActivity(downHitTarget);
mOverviewCallbacks = overviewCallbacks;
mTaskOverlayFactory = taskOverlayFactory;
mTouchInteractionLog = touchInteractionLog;
mTouchInteractionLog.setTouchConsumer(this);
}
@Override
@@ -125,6 +129,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
if (mVelocityTracker == null) {
return;
}
mTouchInteractionLog.addMotionEvent(ev);
switch (ev.getActionMasked()) {
case ACTION_DOWN: {
TraceHelper.beginSection("TouchInt");
@@ -215,10 +220,13 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
}
private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
mTouchInteractionLog.startRecentsAnimation();
// Create the shared handler
RecentsAnimationState animationState = new RecentsAnimationState();
final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler(
animationState.id, mRunningTask, this, touchTimeMs, mActivityControlHelper);
animationState.id, mRunningTask, this, touchTimeMs, mActivityControlHelper,
mTouchInteractionLog);
// Preload the plan
mRecentsModel.loadTasks(mRunningTask.id, null);
@@ -315,7 +323,13 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
}
@Override
public void updateTouchTracking(int interactionType) {
public Choreographer getIntrimChoreographer(MotionEventQueue queue) {
mEventQueue = queue;
return mBackgroundThreadChoreographer;
}
@Override
public void onQuickScrubStart() {
if (!mPassedInitialSlop && mIsDeferredDownTarget && mInteractionHandler == null) {
// If we deferred starting the window animation on touch down, then
// start tracking now
@@ -323,20 +337,16 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
mPassedInitialSlop = true;
}
mTouchInteractionLog.startQuickScrub();
if (mInteractionHandler != null) {
mInteractionHandler.updateInteractionType(interactionType);
mInteractionHandler.onQuickScrubStart();
}
notifyGestureStarted();
}
@Override
public Choreographer getIntrimChoreographer(MotionEventQueue queue) {
mEventQueue = queue;
return mBackgroundThreadChoreographer;
}
@Override
public void onQuickScrubEnd() {
mTouchInteractionLog.endQuickScrub("onQuickScrubEnd");
if (mInteractionHandler != null) {
mInteractionHandler.onQuickScrubEnd();
}
@@ -344,6 +354,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
@Override
public void onQuickScrubProgress(float progress) {
mTouchInteractionLog.setQuickScrubProgress(progress);
if (mInteractionHandler != null) {
mInteractionHandler.onQuickScrubProgress(progress);
}
@@ -351,6 +362,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
@Override
public void onQuickStep(MotionEvent ev) {
mTouchInteractionLog.startQuickStep();
if (mIsDeferredDownTarget) {
// Deferred gesture, start the animation and gesture tracking once we pass the actual
// touch slop
@@ -280,7 +280,6 @@ public class OverviewCommandHelper {
factory.createActivityController(RECENTS_LAUNCH_DURATION, INTERACTION_NORMAL);
mActivity = activity;
mRecentsView = mActivity.getOverviewPanel();
mRecentsView.setRunningTaskIconScaledDown(true);
if (!mUserEventLogged) {
activity.getUserEventDispatcher().logActionCommand(Action.Command.RECENTS_BUTTON,
mHelper.getContainerType(), ContainerType.TASKSWITCHER);
@@ -298,6 +297,9 @@ public class OverviewCommandHelper {
if (mListener != null) {
mListener.unregister();
}
if (mRecentsView != null) {
mRecentsView.setRunningTaskIconScaledDown(true);
}
AnimatorSet anim = new AnimatorSet();
anim.addListener(new AnimationSuccessListener() {
@Override
@@ -67,8 +67,10 @@ public class QuickScrubController implements OnAlarmListener {
private int mQuickScrubSection;
private boolean mStartedFromHome;
private boolean mFinishedTransitionToQuickScrub;
private int mLaunchingTaskId;
private Runnable mOnFinishedTransitionToQuickScrubRunnable;
private ActivityControlHelper mActivityControlHelper;
private TouchInteractionLog mTouchInteractionLog;
public QuickScrubController(BaseActivity activity, RecentsView recentsView) {
mActivity = activity;
@@ -79,13 +81,15 @@ public class QuickScrubController implements OnAlarmListener {
}
}
public void onQuickScrubStart(boolean startingFromHome, ActivityControlHelper controlHelper) {
public void onQuickScrubStart(boolean startingFromHome, ActivityControlHelper controlHelper,
TouchInteractionLog touchInteractionLog) {
prepareQuickScrub(TAG);
mInQuickScrub = true;
mStartedFromHome = startingFromHome;
mQuickScrubSection = 0;
mFinishedTransitionToQuickScrub = false;
mActivityControlHelper = controlHelper;
mTouchInteractionLog = touchInteractionLog;
snapToNextTaskIfAvailable();
mActivity.getUserEventDispatcher().resetActionDurationMillis();
@@ -101,7 +105,10 @@ public class QuickScrubController implements OnAlarmListener {
TaskView taskView = mRecentsView.getTaskViewAt(page);
if (taskView != null) {
mWaitingForTaskLaunch = true;
mTouchInteractionLog.launchTaskStart();
mLaunchingTaskId = taskView.getTask().key.id;
taskView.launchTask(true, (result) -> {
mTouchInteractionLog.launchTaskEnd(result);
if (!result) {
taskView.notifyTaskLaunchFailed(TAG);
breakOutOfQuickScrub();
@@ -141,6 +148,7 @@ public class QuickScrubController implements OnAlarmListener {
mActivityControlHelper = null;
mOnFinishedTransitionToQuickScrubRunnable = null;
mRecentsView.setNextPageSwitchRunnable(null);
mLaunchingTaskId = 0;
}
/**
@@ -206,6 +214,18 @@ public class QuickScrubController implements OnAlarmListener {
}
}
public void onTaskRemoved(int taskId) {
if (mLaunchingTaskId == taskId) {
// The task has been removed mid-launch, break out of quickscrub and return the user
// to where they were before (and notify the launch failed)
TaskView taskView = mRecentsView.getTaskView(taskId);
if (taskView != null) {
taskView.notifyTaskLaunchFailed(TAG);
}
breakOutOfQuickScrub();
}
}
public void snapToNextTaskIfAvailable() {
if (mInQuickScrub && mRecentsView.getChildCount() > 0) {
int duration = mStartedFromHome ? QUICK_SCRUB_FROM_HOME_START_DURATION
@@ -43,7 +43,7 @@ public interface TouchConsumer extends Consumer<MotionEvent> {
default void reset() { }
default void updateTouchTracking(@InteractionType int interactionType) { }
default void onQuickScrubStart() { }
default void onQuickScrubEnd() { }
@@ -0,0 +1,108 @@
/*
* 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.quickstep;
import android.view.MotionEvent;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.LinkedList;
/**
* Keeps track of debugging logs for a particular quickstep/scrub gesture.
*/
public class TouchInteractionLog {
// The number of gestures to log
private static final int MAX_NUM_LOG_GESTURES = 5;
private final Calendar mCalendar = Calendar.getInstance();
private final SimpleDateFormat mDateFormat = new SimpleDateFormat("MMM dd - kk:mm:ss:SSS");
private final LinkedList<ArrayList<String>> mGestureLogs = new LinkedList<>();
public void prepareForNewGesture() {
mGestureLogs.add(new ArrayList<>());
while (mGestureLogs.size() > MAX_NUM_LOG_GESTURES) {
mGestureLogs.pop();
}
getCurrentLog().add("[" + mDateFormat.format(mCalendar.getTime()) + "]");
}
public void setTouchConsumer(TouchConsumer consumer) {
getCurrentLog().add("tc=" + consumer.getClass().getSimpleName());
}
public void addMotionEvent(MotionEvent event) {
getCurrentLog().add("ev=" + event.getActionMasked());
}
public void startQuickStep() {
getCurrentLog().add("qstStart");
}
public void startQuickScrub() {
getCurrentLog().add("qsStart");
}
public void setQuickScrubProgress(float progress) {
getCurrentLog().add("qsP=" + progress);
}
public void endQuickScrub(String reason) {
getCurrentLog().add("qsEnd=" + reason);
}
public void startRecentsAnimation() {
getCurrentLog().add("raStart");
}
public void startRecentsAnimationCallback(int numTargets) {
getCurrentLog().add("raStartCb=" + numTargets);
}
public void cancelRecentsAnimation() {
getCurrentLog().add("raCancel");
}
public void finishRecentsAnimation(boolean toHome) {
getCurrentLog().add("raFinish=" + toHome);
}
public void launchTaskStart() {
getCurrentLog().add("launchStart");
}
public void launchTaskEnd(boolean result) {
getCurrentLog().add("launchEnd=" + result);
}
public void dump(PrintWriter pw) {
pw.println("TouchInteractionLog {");
for (ArrayList<String> gesture : mGestureLogs) {
pw.print(" ");
for (String log : gesture) {
pw.print(log + " ");
}
pw.println();
}
pw.println("}");
}
private ArrayList<String> getCurrentLog() {
return mGestureLogs.getLast();
}
}
@@ -52,6 +52,8 @@ import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ChoreographerCompat;
import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
import java.io.FileDescriptor;
import java.io.PrintWriter;
/**
* Service connected by system-UI for handling touch interaction.
@@ -81,6 +83,8 @@ public class TouchInteractionService extends Service {
@Override
public void onPreMotionEvent(@HitTarget int downHitTarget) {
mTouchInteractionLog.prepareForNewGesture();
TraceHelper.beginSection("SysUiBinder");
setupTouchConsumer(downHitTarget);
TraceHelper.partitionSection("SysUiBinder", "Down target " + downHitTarget);
@@ -179,6 +183,7 @@ public class TouchInteractionService extends Service {
private OverviewInteractionState mOverviewInteractionState;
private OverviewCallbacks mOverviewCallbacks;
private TaskOverlayFactory mTaskOverlayFactory;
private TouchInteractionLog mTouchInteractionLog;
private Choreographer mMainThreadChoreographer;
private Choreographer mBackgroundThreadChoreographer;
@@ -196,6 +201,7 @@ public class TouchInteractionService extends Service {
mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(this);
mOverviewCallbacks = OverviewCallbacks.get(this);
mTaskOverlayFactory = TaskOverlayFactory.get(this);
mTouchInteractionLog = new TouchInteractionLog();
sConnected = true;
@@ -240,7 +246,7 @@ public class TouchInteractionService extends Service {
} else if (forceToLauncher ||
runningTaskInfo.topActivity.equals(mOverviewCommandHelper.overviewComponent)) {
return OverviewTouchConsumer.newInstance(
mOverviewCommandHelper.getActivityControlHelper(), false);
mOverviewCommandHelper.getActivityControlHelper(), false, mTouchInteractionLog);
} else {
if (tracker == null) {
tracker = VelocityTracker.obtain();
@@ -249,7 +255,7 @@ public class TouchInteractionService extends Service {
mOverviewCommandHelper.overviewIntent,
mOverviewCommandHelper.getActivityControlHelper(), mMainThreadExecutor,
mBackgroundThreadChoreographer, downHitTarget, mOverviewCallbacks,
mTaskOverlayFactory, tracker);
mTaskOverlayFactory, tracker, mTouchInteractionLog);
}
}
@@ -262,6 +268,11 @@ public class TouchInteractionService extends Service {
mBackgroundThreadChoreographer = ChoreographerCompat.getSfInstance());
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mTouchInteractionLog.dump(pw);
}
public static class OverviewTouchConsumer<T extends BaseDraggingActivity>
implements TouchConsumer {
@@ -272,6 +283,7 @@ public class TouchInteractionService extends Service {
private final PointF mDownPos = new PointF();
private final int mTouchSlop;
private final QuickScrubController mQuickScrubController;
private final TouchInteractionLog mTouchInteractionLog;
private final boolean mStartingInActivityBounds;
@@ -283,7 +295,7 @@ public class TouchInteractionService extends Service {
private boolean mEndPending = false;
OverviewTouchConsumer(ActivityControlHelper<T> activityHelper, T activity,
boolean startingInActivityBounds) {
boolean startingInActivityBounds, TouchInteractionLog touchInteractionLog) {
mActivityHelper = activityHelper;
mActivity = activity;
mTarget = activity.getDragLayer();
@@ -292,6 +304,8 @@ public class TouchInteractionService extends Service {
mQuickScrubController = mActivity.<RecentsView>getOverviewPanel()
.getQuickScrubController();
mTouchInteractionLog = touchInteractionLog;
mTouchInteractionLog.setTouchConsumer(this);
}
@Override
@@ -299,6 +313,7 @@ public class TouchInteractionService extends Service {
if (mInvalidated) {
return;
}
mTouchInteractionLog.addMotionEvent(ev);
int action = ev.getActionMasked();
if (action == ACTION_DOWN) {
if (mStartingInActivityBounds) {
@@ -372,44 +387,48 @@ public class TouchInteractionService extends Service {
OverviewCallbacks.get(mActivity).closeAllWindows();
ActivityManagerWrapper.getInstance()
.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
mTouchInteractionLog.startQuickStep();
}
@Override
public void updateTouchTracking(int interactionType) {
public void onQuickScrubStart() {
if (mInvalidated) {
return;
}
if (interactionType == INTERACTION_QUICK_SCRUB) {
mTouchInteractionLog.startQuickScrub();
if (!mQuickScrubController.prepareQuickScrub(TAG)) {
mInvalidated = true;
mTouchInteractionLog.endQuickScrub("onQuickScrubStart");
return;
}
OverviewCallbacks.get(mActivity).closeAllWindows();
ActivityManagerWrapper.getInstance()
.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
mStartPending = true;
Runnable action = () -> {
if (!mQuickScrubController.prepareQuickScrub(TAG)) {
mInvalidated = true;
mTouchInteractionLog.endQuickScrub("onQuickScrubStart");
return;
}
OverviewCallbacks.get(mActivity).closeAllWindows();
ActivityManagerWrapper.getInstance()
.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
mActivityHelper.onQuickInteractionStart(mActivity, null, true,
mTouchInteractionLog);
mQuickScrubController.onQuickScrubProgress(mLastProgress);
mStartPending = false;
mStartPending = true;
Runnable action = () -> {
if (!mQuickScrubController.prepareQuickScrub(TAG)) {
mInvalidated = true;
return;
}
mActivityHelper.onQuickInteractionStart(mActivity, null, true);
mQuickScrubController.onQuickScrubProgress(mLastProgress);
mStartPending = false;
if (mEndPending) {
mQuickScrubController.onQuickScrubEnd();
mEndPending = false;
}
};
if (mEndPending) {
mQuickScrubController.onQuickScrubEnd();
mEndPending = false;
}
};
mActivityHelper.executeOnWindowAvailable(mActivity, action);
}
mActivityHelper.executeOnWindowAvailable(mActivity, action);
}
@Override
public void onQuickScrubEnd() {
mTouchInteractionLog.endQuickScrub("onQuickScrubEnd");
if (mInvalidated) {
return;
}
@@ -422,6 +441,7 @@ public class TouchInteractionService extends Service {
@Override
public void onQuickScrubProgress(float progress) {
mTouchInteractionLog.setQuickScrubProgress(progress);
mLastProgress = progress;
if (mInvalidated || mStartPending) {
return;
@@ -429,13 +449,14 @@ public class TouchInteractionService extends Service {
mQuickScrubController.onQuickScrubProgress(progress);
}
public static TouchConsumer newInstance(
ActivityControlHelper activityHelper, boolean startingInActivityBounds) {
public static TouchConsumer newInstance(ActivityControlHelper activityHelper,
boolean startingInActivityBounds, TouchInteractionLog touchInteractionLog) {
BaseDraggingActivity activity = activityHelper.getCreatedActivity();
if (activity == null) {
return TouchConsumer.NO_OP;
}
return new OverviewTouchConsumer(activityHelper, activity, startingInActivityBounds);
return new OverviewTouchConsumer(activityHelper, activity, startingInActivityBounds,
touchInteractionLog);
}
}
}
@@ -198,6 +198,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
private final Context mContext;
private final ActivityControlHelper<T> mActivityControlHelper;
private final ActivityInitListener mActivityInitListener;
private final TouchInteractionLog mTouchInteractionLog;
private final int mRunningTaskId;
private final RunningTaskInfo mRunningTaskInfo;
@@ -239,7 +240,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
private Bundle mAssistData;
WindowTransformSwipeHandler(int id, RunningTaskInfo runningTaskInfo, Context context,
long touchTimeMs, ActivityControlHelper<T> controller) {
long touchTimeMs, ActivityControlHelper<T> controller,
TouchInteractionLog touchInteractionLog) {
this.id = id;
mContext = context;
mRunningTaskInfo = runningTaskInfo;
@@ -248,6 +250,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
mActivityControlHelper = controller;
mActivityInitListener = mActivityControlHelper
.createActivityInitListener(this::onActivityInit);
mTouchInteractionLog = touchInteractionLog;
initStateCallbacks();
}
@@ -319,7 +322,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
this::notifyTransitionCancelled);
mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_QUICK_SCRUB_START
| STATE_APP_CONTROLLER_RECEIVED, this::onQuickScrubStart);
| STATE_APP_CONTROLLER_RECEIVED, this::onQuickScrubStartUi);
mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_QUICK_SCRUB_START
| STATE_SCALED_CONTROLLER_RECENTS, this::onFinishedTransitionToQuickScrub);
mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_CURRENT_TASK_FINISHED
@@ -503,25 +506,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
.getHighResThumbnailLoader().setVisible(true);
}
public void updateInteractionType(@InteractionType int interactionType) {
if (mInteractionType != INTERACTION_NORMAL) {
throw new IllegalArgumentException(
"Can't change interaction type from " + mInteractionType);
}
if (interactionType != INTERACTION_QUICK_SCRUB) {
throw new IllegalArgumentException(
"Can't change interaction type to " + interactionType);
}
mInteractionType = interactionType;
mRecentsAnimationWrapper.runOnInit(this::shiftAnimationDestinationForQuickscrub);
setStateOnUiThread(STATE_QUICK_SCRUB_START | STATE_GESTURE_COMPLETED);
// Start the window animation without waiting for launcher.
animateToProgress(mCurrentShift.value, 1f, QUICK_SCRUB_FROM_APP_START_DURATION, LINEAR,
true /* goingToHome */);
}
private void shiftAnimationDestinationForQuickscrub() {
TransformedRect tempRect = new TransformedRect();
mActivityControlHelper
@@ -669,6 +653,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
initTransitionEndpoints(dp);
mRecentsAnimationWrapper.setController(controller, targets);
mTouchInteractionLog.startRecentsAnimationCallback(targets.apps.length);
setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
mPassedOverviewThreshold = false;
@@ -678,6 +663,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
mRecentsAnimationWrapper.setController(null, null);
mActivityInitListener.unregister();
setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
mTouchInteractionLog.cancelRecentsAnimation();
}
public void onGestureStarted() {
@@ -729,7 +715,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
// Hide the task view, if not already hidden
setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha);
return OverviewTouchConsumer.newInstance(mActivityControlHelper, true);
return OverviewTouchConsumer.newInstance(mActivityControlHelper, true,
mTouchInteractionLog);
}
private void handleNormalGestureEnd(float endVelocity, boolean isFling) {
@@ -854,6 +841,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
@UiThread
private void resumeLastTask() {
mRecentsAnimationWrapper.finish(false /* toHome */, null);
mTouchInteractionLog.finishRecentsAnimation(false);
}
public void reset() {
@@ -950,6 +938,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
mRecentsAnimationWrapper.finish(true /* toHome */,
() -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
}
mTouchInteractionLog.finishRecentsAnimation(true);
}
private void setupLauncherUiAfterSwipeUpAnimation() {
@@ -969,7 +958,22 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
reset();
}
private void onQuickScrubStart() {
public void onQuickScrubStart() {
if (mInteractionType != INTERACTION_NORMAL) {
throw new IllegalArgumentException(
"Can't change interaction type from " + mInteractionType);
}
mInteractionType = INTERACTION_QUICK_SCRUB;
mRecentsAnimationWrapper.runOnInit(this::shiftAnimationDestinationForQuickscrub);
setStateOnUiThread(STATE_QUICK_SCRUB_START | STATE_GESTURE_COMPLETED);
// Start the window animation without waiting for launcher.
animateToProgress(mCurrentShift.value, 1f, QUICK_SCRUB_FROM_APP_START_DURATION, LINEAR,
true /* goingToHome */);
}
private void onQuickScrubStartUi() {
if (!mQuickScrubController.prepareQuickScrub(TAG)) {
mQuickScrubBlocked = true;
setStateOnUiThread(STATE_RESUME_LAST_TASK | STATE_HANDLER_INVALIDATED);
@@ -980,7 +984,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
mLauncherTransitionController = null;
}
mActivityControlHelper.onQuickInteractionStart(mActivity, mRunningTaskInfo, false);
mActivityControlHelper.onQuickInteractionStart(mActivity, mRunningTaskInfo, false,
mTouchInteractionLog);
// Inform the last progress in case we skipped before.
mQuickScrubController.onQuickScrubProgress(mCurrentQuickScrubProgress);
@@ -183,6 +183,10 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
if (!mHandleTaskStackChanges) {
return;
}
// Notify the quick scrub controller that a particular task has been removed
mQuickScrubController.onTaskRemoved(taskId);
BackgroundExecutor.get().submit(() -> {
TaskView taskView = getTaskView(taskId);
if (taskView == null) {
@@ -57,6 +57,7 @@ import com.android.systemui.shared.recents.model.Task.TaskCallbacks;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import java.util.function.Consumer;
/**
@@ -204,11 +205,26 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback
if (animate) {
opts = ((BaseDraggingActivity) fromContext(getContext()))
.getActivityLaunchOptions(this);
ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(mTask.key,
opts, resultCallback, resultCallbackHandler);
} else {
opts = ActivityOptions.makeCustomAnimation(getContext(), 0, 0);
opts = ActivityOptionsCompat.makeCustomAnimation(getContext(), 0, 0, () -> {
if (resultCallback != null) {
// Only post the animation start after the system has indicated that the
// transition has started
resultCallbackHandler.post(() -> resultCallback.accept(true));
}
}, resultCallbackHandler);
ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(mTask.key,
opts, (success) -> {
if (resultCallback != null && !success) {
// If the call to start activity failed, then post the result
// immediately, otherwise, wait for the animation start callback
// from the activity options above
resultCallbackHandler.post(() -> resultCallback.accept(false));
}
}, resultCallbackHandler);
}
ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(mTask.key,
opts, resultCallback, resultCallbackHandler);
}
}
@@ -17,6 +17,6 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/legacy_icon_background"/>
<foreground>
<com.android.launcher3.graphics.FixedScaleDrawable />
<com.android.launcher3.icons.FixedScaleDrawable />
</foreground>
</adaptive-icon>
@@ -39,7 +39,7 @@ import android.util.Patterns;
import com.android.launcher3.LauncherProvider.SqlArguments;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.util.Thunk;
import org.xmlpull.v1.XmlPullParser;
@@ -272,8 +272,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
/**
* Overrides the default long press timeout.
*/
public void setLongPressTimeout(int longPressTimeout) {
mLongPressHelper.setLongPressTimeout(longPressTimeout);
public void setLongPressTimeoutFactor(float longPressTimeoutFactor) {
mLongPressHelper.setLongPressTimeoutFactor(longPressTimeoutFactor);
}
@Override
@@ -17,17 +17,18 @@
package com.android.launcher3;
import android.view.View;
import android.view.ViewConfiguration;
import com.android.launcher3.util.Thunk;
public class CheckLongPressHelper {
public static final int DEFAULT_LONG_PRESS_TIMEOUT = 300;
public static final float DEFAULT_LONG_PRESS_TIMEOUT_FACTOR = 0.75f;
@Thunk View mView;
@Thunk View.OnLongClickListener mListener;
@Thunk boolean mHasPerformedLongPress;
private int mLongPressTimeout = DEFAULT_LONG_PRESS_TIMEOUT;
private float mLongPressTimeoutFactor = DEFAULT_LONG_PRESS_TIMEOUT_FACTOR;
private CheckForLongPress mPendingCheckForLongPress;
class CheckForLongPress implements Runnable {
@@ -60,8 +61,8 @@ public class CheckLongPressHelper {
/**
* Overrides the default long press timeout.
*/
public void setLongPressTimeout(int longPressTimeout) {
mLongPressTimeout = longPressTimeout;
public void setLongPressTimeoutFactor(float longPressTimeoutFactor) {
mLongPressTimeoutFactor = longPressTimeoutFactor;
}
public void postCheckForLongPress() {
@@ -70,7 +71,8 @@ public class CheckLongPressHelper {
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
mView.postDelayed(mPendingCheckForLongPress, mLongPressTimeout);
mView.postDelayed(mPendingCheckForLongPress,
(long) (ViewConfiguration.getLongPressTimeout() * mLongPressTimeoutFactor));
}
public void cancelLongPress() {
+2 -2
View File
@@ -30,7 +30,7 @@ import android.view.WindowManager;
import com.android.launcher3.CellLayout.ContainerType;
import com.android.launcher3.badge.BadgeRenderer;
import com.android.launcher3.graphics.IconNormalizer;
import com.android.launcher3.icons.IconNormalizer;
public class DeviceProfile {
@@ -373,7 +373,7 @@ public class DeviceProfile {
updateFolderCellSize(1f, dm, res);
// Don't let the folder get too close to the edges of the screen.
int folderMargin = edgeMarginPx;
int folderMargin = edgeMarginPx * 2;
Point totalWorkspacePadding = getTotalWorkspacePadding();
// Check if the icons fit within the available height.
@@ -34,7 +34,7 @@ import android.graphics.drawable.Drawable;
import android.util.Property;
import android.util.SparseArray;
import com.android.launcher3.graphics.BitmapInfo;
import com.android.launcher3.icons.BitmapInfo;
public class FastBitmapDrawable extends Drawable {
@@ -40,8 +40,8 @@ import android.util.Pair;
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.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.shortcuts.ShortcutKey;
@@ -16,7 +16,7 @@
package com.android.launcher3;
import static com.android.launcher3.graphics.BitmapInfo.LOW_RES_ICON;
import static com.android.launcher3.icons.BitmapInfo.LOW_RES_ICON;
import android.graphics.Bitmap;
@@ -2,11 +2,15 @@ package com.android.launcher3;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.UserHandle;
import com.android.launcher3.icons.ComponentWithLabel;
/**
* This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords
@@ -14,7 +18,8 @@ import android.os.Parcel;
* (who's implementation is owned by the launcher). This object represents a widget type / class,
* as opposed to a widget instance, and so should not be confused with {@link LauncherAppWidgetInfo}
*/
public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo
implements ComponentWithLabel {
public static final String CLS_CUSTOM_WIDGET_PREFIX = "#custom-widget-";
@@ -102,4 +107,14 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
return 0;
}
}
}
@Override
public final ComponentName getComponent() {
return provider;
}
@Override
public final UserHandle getUser() {
return getProfile();
}
}
+14 -1
View File
@@ -38,7 +38,7 @@ import android.util.Pair;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.AddWorkspaceItemsTask;
import com.android.launcher3.model.BaseModelUpdateTask;
@@ -599,6 +599,19 @@ public class LauncherModel extends BroadcastReceiver
CacheDataUpdatedTask.OP_CACHE_UPDATE, user, updatedPackages));
}
/**
* Called when the labels for the widgets has updated in the icon cache.
*/
public void onWidgetLabelsUpdated(HashSet<String> updatedPackages, UserHandle user) {
enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
dataModel.widgetsModel.onPackageIconsUpdated(updatedPackages, user, app);
bindUpdatedWidgets(dataModel);
}
});
}
public void enqueueModelUpdateTask(ModelUpdateTask task) {
task.init(mApp, this, sBgDataModel, mBgAllAppsList, mUiExecutor);
runOnWorkerThread(task);
@@ -31,8 +31,8 @@ import android.util.LongSparseArray;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.ShortcutConfigActivityInfo;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.graphics.ShadowGenerator;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.ShadowGenerator;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.util.ComponentKey;
@@ -22,7 +22,6 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnFocusChangeListener;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.TextView;
@@ -252,7 +251,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
R.layout.all_apps_icon, parent, false);
icon.setOnClickListener(ItemClickHandler.INSTANCE);
icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_ALL_APPS);
icon.setLongPressTimeout(ViewConfiguration.getLongPressTimeout());
icon.setLongPressTimeoutFactor(1f);
icon.setOnFocusChangeListener(mIconFocusListener);
// Ensure the all apps icon height matches the workspace icons in portrait mode.
@@ -19,7 +19,7 @@ import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.getSize;
import static android.view.View.MeasureSpec.makeMeasureSpec;
import static com.android.launcher3.graphics.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
import android.content.Context;
import android.graphics.Rect;
@@ -27,7 +27,7 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.util.Log;
import com.android.launcher3.graphics.ShadowGenerator;
import com.android.launcher3.icons.ShadowGenerator;
/**
* Contains parameters necessary to draw a badge for an icon (e.g. the size of the badge).
@@ -200,7 +200,7 @@ public class LauncherAppsCompatVL extends LauncherAppsCompat {
pm.queryIntentActivities(new Intent(Intent.ACTION_CREATE_SHORTCUT), 0)) {
if (packageUser == null || packageUser.mPackageName
.equals(info.activityInfo.packageName)) {
result.add(new ShortcutConfigActivityInfoVL(info.activityInfo, pm));
result.add(new ShortcutConfigActivityInfoVL(info.activityInfo));
}
}
return result;
@@ -33,7 +33,7 @@ import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVO;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.PackageUserKey;
@@ -32,6 +32,7 @@ import android.os.UserHandle;
import android.util.Log;
import android.widget.Toast;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
@@ -40,7 +41,7 @@ import com.android.launcher3.ShortcutInfo;
/**
* Wrapper class for representing a shortcut configure activity.
*/
public abstract class ShortcutConfigActivityInfo {
public abstract class ShortcutConfigActivityInfo implements ComponentWithLabel {
private static final String TAG = "SCActivityInfo";
@@ -52,10 +53,12 @@ public abstract class ShortcutConfigActivityInfo {
mUser = user;
}
@Override
public ComponentName getComponent() {
return mCn;
}
@Override
public UserHandle getUser() {
return mUser;
}
@@ -64,8 +67,6 @@ public abstract class ShortcutConfigActivityInfo {
return LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
}
public abstract CharSequence getLabel();
public abstract Drawable getFullResIcon(IconCache cache);
/**
@@ -94,8 +95,8 @@ public abstract class ShortcutConfigActivityInfo {
}
/**
* Returns true if various properties ({@link #getLabel()}, {@link #getFullResIcon}) can
* be safely persisted.
* Returns true if various properties ({@link #getLabel(PackageManager)},
* {@link #getFullResIcon}) can be safely persisted.
*/
public boolean isPersistable() {
return true;
@@ -104,18 +105,15 @@ public abstract class ShortcutConfigActivityInfo {
static class ShortcutConfigActivityInfoVL extends ShortcutConfigActivityInfo {
private final ActivityInfo mInfo;
private final PackageManager mPm;
public ShortcutConfigActivityInfoVL(ActivityInfo info, PackageManager pm) {
public ShortcutConfigActivityInfoVL(ActivityInfo info) {
super(new ComponentName(info.packageName, info.name), Process.myUserHandle());
mInfo = info;
mPm = pm;
}
@Override
public CharSequence getLabel() {
return mInfo.loadLabel(mPm);
public CharSequence getLabel(PackageManager pm) {
return mInfo.loadLabel(pm);
}
@Override
@@ -135,7 +133,7 @@ public abstract class ShortcutConfigActivityInfo {
}
@Override
public CharSequence getLabel() {
public CharSequence getLabel(PackageManager pm) {
return mInfo.getLabel();
}
@@ -32,6 +32,7 @@ import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.view.MotionEvent;
@@ -46,6 +47,7 @@ import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetHost;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.R;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompatVO;
@@ -54,6 +56,8 @@ import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.Provider;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetHostViewLoader;
@@ -78,7 +82,6 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
// Widget request specific options.
private LauncherAppWidgetHost mAppWidgetHost;
private AppWidgetManagerCompat mAppWidgetManager;
private PendingAddWidgetInfo mPendingWidgetInfo;
private int mPendingBindWidgetId;
private Bundle mWidgetOptions;
@@ -189,10 +192,9 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
private void setupShortcut() {
PinShortcutRequestActivityInfo shortcutInfo =
new PinShortcutRequestActivityInfo(mRequest, this);
WidgetItem item = new WidgetItem(shortcutInfo);
mWidgetCell.getWidgetView().setTag(new PendingAddShortcutInfo(shortcutInfo));
mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache());
mWidgetCell.ensurePreview();
applyWidgetItemAsync(
() -> new WidgetItem(shortcutInfo, mApp.getIconCache(), getPackageManager()));
}
private boolean setupWidget() {
@@ -207,18 +209,32 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this);
mPendingWidgetInfo = new PendingAddWidgetInfo(widgetInfo);
mPendingWidgetInfo.spanX = Math.min(mIdp.numColumns, widgetInfo.spanX);
mPendingWidgetInfo.spanY = Math.min(mIdp.numRows, widgetInfo.spanY);
mWidgetOptions = WidgetHostViewLoader.getDefaultOptionsForWidget(this, mPendingWidgetInfo);
PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(widgetInfo);
pendingInfo.spanX = Math.min(mIdp.numColumns, widgetInfo.spanX);
pendingInfo.spanY = Math.min(mIdp.numRows, widgetInfo.spanY);
mWidgetOptions = WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
mWidgetCell.getWidgetView().setTag(pendingInfo);
WidgetItem item = new WidgetItem(widgetInfo, getPackageManager(), mIdp);
mWidgetCell.getWidgetView().setTag(mPendingWidgetInfo);
mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache());
mWidgetCell.ensurePreview();
applyWidgetItemAsync(() -> new WidgetItem(widgetInfo, mIdp, mApp.getIconCache()));
return true;
}
private void applyWidgetItemAsync(final Provider<WidgetItem> itemProvider) {
new AsyncTask<Void, Void, WidgetItem>() {
@Override
protected WidgetItem doInBackground(Void... voids) {
return itemProvider.get();
}
@Override
protected void onPostExecute(WidgetItem item) {
mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache());
mWidgetCell.ensurePreview();
}
}.executeOnExecutor(new LooperExecutor(LauncherModel.getWorkerLooper()));
// TODO: Create a worker looper executor and reuse that everywhere.
}
/**
* Called when the cancel button is clicked.
*/
@@ -56,7 +56,7 @@ import com.android.launcher3.FirstFrameAnimatorHelper;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.ShortcutConfigActivityInfo;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.shortcuts.ShortcutKey;
@@ -22,6 +22,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.PinItemRequest;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -65,7 +66,7 @@ class PinShortcutRequestActivityInfo extends ShortcutConfigActivityInfo {
}
@Override
public CharSequence getLabel() {
public CharSequence getLabel(PackageManager pm) {
return mInfo.getShortLabel();
}
@@ -16,8 +16,6 @@
package com.android.launcher3.graphics;
import static com.android.launcher3.graphics.BitmapInfo.LOW_RES_ICON;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
@@ -37,6 +35,7 @@ import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
@@ -66,7 +65,7 @@ public class DrawableFactory implements ResourceBasedOverride {
* Returns a FastBitmapDrawable with the icon.
*/
public FastBitmapDrawable newIcon(Context context, ItemInfoWithIcon info) {
FastBitmapDrawable drawable = info.iconBitmap == LOW_RES_ICON
FastBitmapDrawable drawable = info.usingLowResIcon()
? new PlaceHolderIconDrawable(info, getPreloadProgressPath(), context)
: new FastBitmapDrawable(info);
drawable.setIsDisabled(info.isDisabled());
@@ -74,7 +73,7 @@ public class DrawableFactory implements ResourceBasedOverride {
}
public FastBitmapDrawable newIcon(Context context, BitmapInfo info, ActivityInfo target) {
return info.icon == LOW_RES_ICON
return info.isLowRes()
? new PlaceHolderIconDrawable(info, getPreloadProgressPath(), context)
: new FastBitmapDrawable(info);
}
@@ -24,6 +24,7 @@ import android.graphics.Rect;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.R;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.util.Themes;
/**
@@ -0,0 +1,538 @@
/*
* 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.launcher3.icons;
import static com.android.launcher3.icons.BitmapInfo.LOW_RES_ICON;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.os.Build.VERSION;
import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import com.android.launcher3.IconProvider;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherFiles;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.graphics.BitmapRenderer;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Provider;
import com.android.launcher3.util.SQLiteCacheHelper;
import java.util.HashMap;
import java.util.HashSet;
import androidx.annotation.NonNull;
import androidx.core.graphics.ColorUtils;
public class BaseIconCache {
private static final String TAG = "BaseIconCache";
private static final boolean DEBUG = false;
private static final boolean DEBUG_IGNORE_CACHE = false;
private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
// Empty class name is used for storing package default entry.
public static final String EMPTY_CLASS_NAME = ".";
public static class CacheEntry extends BitmapInfo {
public CharSequence title = "";
public CharSequence contentDescription = "";
}
private final HashMap<UserHandle, BitmapInfo> mDefaultIcons = new HashMap<>();
final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
final Context mContext;
final PackageManager mPackageManager;
final IconProvider mIconProvider;
final UserManagerCompat mUserManager;
final LauncherAppsCompat mLauncherApps;
private final HashMap<ComponentKey, CacheEntry> mCache =
new HashMap<>(INITIAL_ICON_CACHE_CAPACITY);
private final InstantAppResolver mInstantAppResolver;
final int mIconDpi;
final IconDB mIconDb;
final Handler mWorkerHandler;
private final BitmapFactory.Options mDecodeOptions;
public BaseIconCache(Context context, int iconDpi, int iconPixelSize) {
mContext = context;
mPackageManager = context.getPackageManager();
mUserManager = UserManagerCompat.getInstance(mContext);
mLauncherApps = LauncherAppsCompat.getInstance(mContext);
mInstantAppResolver = InstantAppResolver.newInstance(mContext);
mIconDpi = iconDpi;
mIconDb = new IconDB(context, iconPixelSize);
mIconProvider = IconProvider.newInstance(context);
mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
if (BitmapRenderer.USE_HARDWARE_BITMAP) {
mDecodeOptions = new BitmapFactory.Options();
mDecodeOptions.inPreferredConfig = Bitmap.Config.HARDWARE;
} else {
mDecodeOptions = null;
}
}
private Drawable getFullResDefaultActivityIcon() {
return Resources.getSystem().getDrawableForDensity(Utilities.ATLEAST_OREO
? android.R.drawable.sym_def_app_icon : android.R.mipmap.sym_def_app_icon,
mIconDpi);
}
private Drawable getFullResIcon(Resources resources, int iconId) {
if (resources != null && iconId != 0) {
try {
return resources.getDrawableForDensity(iconId, mIconDpi);
} catch (Resources.NotFoundException e) { }
}
return getFullResDefaultActivityIcon();
}
public Drawable getFullResIcon(String packageName, int iconId) {
try {
return getFullResIcon(mPackageManager.getResourcesForApplication(packageName), iconId);
} catch (PackageManager.NameNotFoundException e) { }
return getFullResDefaultActivityIcon();
}
public Drawable getFullResIcon(ActivityInfo info) {
try {
return getFullResIcon(mPackageManager.getResourcesForApplication(info.applicationInfo),
info.getIconResource());
} catch (PackageManager.NameNotFoundException e) { }
return getFullResDefaultActivityIcon();
}
public Drawable getFullResIcon(LauncherActivityInfo info) {
return getFullResIcon(info, true);
}
public Drawable getFullResIcon(LauncherActivityInfo info, boolean flattenDrawable) {
return mIconProvider.getIcon(info, mIconDpi, flattenDrawable);
}
protected BitmapInfo makeDefaultIcon(UserHandle user) {
try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
return li.createBadgedIconBitmap(
getFullResDefaultActivityIcon(), user, VERSION.SDK_INT);
}
}
/**
* Remove any records for the supplied ComponentName.
*/
public synchronized void remove(ComponentName componentName, UserHandle user) {
mCache.remove(new ComponentKey(componentName, user));
}
/**
* Remove any records for the supplied package name from memory.
*/
private void removeFromMemCacheLocked(String packageName, UserHandle user) {
HashSet<ComponentKey> forDeletion = new HashSet<>();
for (ComponentKey key: mCache.keySet()) {
if (key.componentName.getPackageName().equals(packageName)
&& key.user.equals(user)) {
forDeletion.add(key);
}
}
for (ComponentKey condemned: forDeletion) {
mCache.remove(condemned);
}
}
/**
* Removes the entries related to the given package in memory and persistent DB.
*/
public synchronized void removeIconsForPkg(String packageName, UserHandle user) {
removeFromMemCacheLocked(packageName, user);
long userSerial = mUserManager.getSerialNumberForUser(user);
mIconDb.delete(
IconDB.COLUMN_COMPONENT + " LIKE ? AND " + IconDB.COLUMN_USER + " = ?",
new String[]{packageName + "/%", Long.toString(userSerial)});
}
public IconCacheUpdateHandler getUpdateHandler() {
mIconProvider.updateSystemStateString(mContext);
return new IconCacheUpdateHandler(this);
}
/**
* Adds an entry into the DB and the in-memory cache.
* @param replaceExisting if true, it will recreate the bitmap even if it already exists in
* the memory. This is useful then the previous bitmap was created using
* old data.
* package private
*/
synchronized <T> void addIconToDBAndMemCache(T object, CachingLogic<T> cachingLogic,
PackageInfo info, long userSerial, boolean replaceExisting) {
UserHandle user = cachingLogic.getUser(object);
ComponentName componentName = cachingLogic.getComponent(object);
final ComponentKey key = new ComponentKey(componentName, user);
CacheEntry entry = null;
if (!replaceExisting) {
entry = mCache.get(key);
// We can't reuse the entry if the high-res icon is not present.
if (entry == null || entry.icon == null || entry.isLowRes()) {
entry = null;
}
}
if (entry == null) {
entry = new CacheEntry();
cachingLogic.loadIcon(mContext, this, object, entry);
}
entry.title = cachingLogic.getLabel(object, mPackageManager);
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
mCache.put(key, entry);
ContentValues values = newContentValues(entry, entry.title.toString(),
componentName.getPackageName());
addIconToDB(values, componentName, info, userSerial);
}
/**
* Updates {@param values} to contain versioning information and adds it to the DB.
* @param values {@link ContentValues} containing icon & title
*/
private void addIconToDB(ContentValues values, ComponentName key,
PackageInfo info, long userSerial) {
values.put(IconDB.COLUMN_COMPONENT, key.flattenToString());
values.put(IconDB.COLUMN_USER, userSerial);
values.put(IconDB.COLUMN_LAST_UPDATED, info.lastUpdateTime);
values.put(IconDB.COLUMN_VERSION, info.versionCode);
mIconDb.insertOrReplace(values);
}
/**
* Fill in {@param infoInOut} with the corresponding icon and label.
*/
public synchronized void getTitleAndIconForApp(
PackageItemInfo infoInOut, boolean useLowResIcon) {
CacheEntry entry = getEntryForPackageLocked(
infoInOut.packageName, infoInOut.user, useLowResIcon);
applyCacheEntry(entry, infoInOut);
}
protected void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) {
info.title = Utilities.trim(entry.title);
info.contentDescription = entry.contentDescription;
((entry.icon == null) ? getDefaultIcon(info.user) : entry).applyTo(info);
}
public synchronized BitmapInfo getDefaultIcon(UserHandle user) {
if (!mDefaultIcons.containsKey(user)) {
mDefaultIcons.put(user, makeDefaultIcon(user));
}
return mDefaultIcons.get(user);
}
public boolean isDefaultIcon(Bitmap icon, UserHandle user) {
return getDefaultIcon(user).icon == icon;
}
/**
* Retrieves the entry from the cache. If the entry is not present, it creates a new entry.
* This method is not thread safe, it must be called from a synchronized method.
*/
protected <T> CacheEntry cacheLocked(
@NonNull ComponentName componentName, @NonNull UserHandle user,
@NonNull Provider<T> infoProvider, @NonNull CachingLogic<T> cachingLogic,
boolean usePackageIcon, boolean useLowResIcon) {
return cacheLocked(componentName, user, infoProvider, cachingLogic, usePackageIcon,
useLowResIcon, true);
}
protected <T> CacheEntry cacheLocked(
@NonNull ComponentName componentName, @NonNull UserHandle user,
@NonNull Provider<T> infoProvider, @NonNull CachingLogic<T> cachingLogic,
boolean usePackageIcon, boolean useLowResIcon, boolean addToMemCache) {
Preconditions.assertWorkerThread();
ComponentKey cacheKey = new ComponentKey(componentName, user);
CacheEntry entry = mCache.get(cacheKey);
if (entry == null || (entry.isLowRes() && !useLowResIcon)) {
entry = new CacheEntry();
if (addToMemCache) {
mCache.put(cacheKey, entry);
}
// Check the DB first.
T object = null;
boolean providerFetchedOnce = false;
if (!getEntryFromDB(cacheKey, entry, useLowResIcon) || DEBUG_IGNORE_CACHE) {
object = infoProvider.get();
providerFetchedOnce = true;
if (object != null) {
cachingLogic.loadIcon(mContext, this, object, entry);
} else {
if (usePackageIcon) {
CacheEntry packageEntry = getEntryForPackageLocked(
componentName.getPackageName(), user, false);
if (packageEntry != null) {
if (DEBUG) Log.d(TAG, "using package default icon for " +
componentName.toShortString());
packageEntry.applyTo(entry);
entry.title = packageEntry.title;
entry.contentDescription = packageEntry.contentDescription;
}
}
if (entry.icon == null) {
if (DEBUG) Log.d(TAG, "using default icon for " +
componentName.toShortString());
getDefaultIcon(user).applyTo(entry);
}
}
}
if (TextUtils.isEmpty(entry.title)) {
if (object == null && !providerFetchedOnce) {
object = infoProvider.get();
providerFetchedOnce = true;
}
if (object != null) {
entry.title = cachingLogic.getLabel(object, mPackageManager);
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
}
}
}
return entry;
}
public synchronized void clear() {
Preconditions.assertWorkerThread();
mIconDb.clear();
}
/**
* Adds a default package entry in the cache. This entry is not persisted and will be removed
* when the cache is flushed.
*/
public synchronized void cachePackageInstallInfo(String packageName, UserHandle user,
Bitmap icon, CharSequence title) {
removeFromMemCacheLocked(packageName, user);
ComponentKey cacheKey = getPackageKey(packageName, user);
CacheEntry entry = mCache.get(cacheKey);
// For icon caching, do not go through DB. Just update the in-memory entry.
if (entry == null) {
entry = new CacheEntry();
}
if (!TextUtils.isEmpty(title)) {
entry.title = title;
}
if (icon != null) {
LauncherIcons li = LauncherIcons.obtain(mContext);
li.createIconBitmap(icon).applyTo(entry);
li.recycle();
}
if (!TextUtils.isEmpty(title) && entry.icon != null) {
mCache.put(cacheKey, entry);
}
}
private static ComponentKey getPackageKey(String packageName, UserHandle user) {
ComponentName cn = new ComponentName(packageName, packageName + EMPTY_CLASS_NAME);
return new ComponentKey(cn, user);
}
/**
* Gets an entry for the package, which can be used as a fallback entry for various components.
* This method is not thread safe, it must be called from a synchronized method.
*/
private CacheEntry getEntryForPackageLocked(String packageName, UserHandle user,
boolean useLowResIcon) {
Preconditions.assertWorkerThread();
ComponentKey cacheKey = getPackageKey(packageName, user);
CacheEntry entry = mCache.get(cacheKey);
if (entry == null || (entry.isLowRes() && !useLowResIcon)) {
entry = new CacheEntry();
boolean entryUpdated = true;
// Check the DB first.
if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) {
try {
int flags = Process.myUserHandle().equals(user) ? 0 :
PackageManager.GET_UNINSTALLED_PACKAGES;
PackageInfo info = mPackageManager.getPackageInfo(packageName, flags);
ApplicationInfo appInfo = info.applicationInfo;
if (appInfo == null) {
throw new NameNotFoundException("ApplicationInfo is null");
}
LauncherIcons li = LauncherIcons.obtain(mContext);
// 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
BitmapInfo iconInfo = li.createBadgedIconBitmap(
appInfo.loadIcon(mPackageManager), user, appInfo.targetSdkVersion,
mInstantAppResolver.isInstantApp(appInfo));
li.recycle();
entry.title = appInfo.loadLabel(mPackageManager);
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
entry.icon = useLowResIcon ? LOW_RES_ICON : iconInfo.icon;
entry.color = iconInfo.color;
// Add the icon in the DB here, since these do not get written during
// package updates.
ContentValues values = newContentValues(
iconInfo, entry.title.toString(), packageName);
addIconToDB(values, cacheKey.componentName, info,
mUserManager.getSerialNumberForUser(user));
} catch (NameNotFoundException e) {
if (DEBUG) Log.d(TAG, "Application not installed " + packageName);
entryUpdated = false;
}
}
// Only add a filled-out entry to the cache
if (entryUpdated) {
mCache.put(cacheKey, entry);
}
}
return entry;
}
private boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) {
Cursor c = null;
try {
c = mIconDb.query(
lowRes ? IconDB.COLUMNS_LOW_RES : IconDB.COLUMNS_HIGH_RES,
IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
new String[]{
cacheKey.componentName.flattenToString(),
Long.toString(mUserManager.getSerialNumberForUser(cacheKey.user))});
if (c.moveToNext()) {
// Set the alpha to be 255, so that we never have a wrong color
entry.color = ColorUtils.setAlphaComponent(c.getInt(0), 255);
entry.title = c.getString(1);
if (entry.title == null) {
entry.title = "";
entry.contentDescription = "";
} else {
entry.contentDescription = mUserManager.getBadgedLabelForUser(
entry.title, cacheKey.user);
}
if (lowRes) {
entry.icon = LOW_RES_ICON;
} else {
byte[] data = c.getBlob(2);
try {
entry.icon = BitmapFactory.decodeByteArray(data, 0, data.length,
mDecodeOptions);
} catch (Exception e) { }
}
return true;
}
} catch (SQLiteException e) {
Log.d(TAG, "Error reading icon cache", e);
} finally {
if (c != null) {
c.close();
}
}
return false;
}
static final class IconDB extends SQLiteCacheHelper {
private final static int RELEASE_VERSION = 25;
public final static String TABLE_NAME = "icons";
public final static String COLUMN_ROWID = "rowid";
public final static String COLUMN_COMPONENT = "componentName";
public final static String COLUMN_USER = "profileId";
public final static String COLUMN_LAST_UPDATED = "lastUpdated";
public final static String COLUMN_VERSION = "version";
public final static String COLUMN_ICON = "icon";
public final static String COLUMN_ICON_COLOR = "icon_color";
public final static String COLUMN_LABEL = "label";
public final static String COLUMN_SYSTEM_STATE = "system_state";
public final static String[] COLUMNS_HIGH_RES = new String[] {
IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL, IconDB.COLUMN_ICON };
public final static String[] COLUMNS_LOW_RES = new String[] {
IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL };
public IconDB(Context context, int iconPixelSize) {
super(context, LauncherFiles.APP_ICONS_DB,
(RELEASE_VERSION << 16) + iconPixelSize,
TABLE_NAME);
}
@Override
protected void onCreateTable(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
COLUMN_COMPONENT + " TEXT NOT NULL, " +
COLUMN_USER + " INTEGER NOT NULL, " +
COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, " +
COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " +
COLUMN_ICON + " BLOB, " +
COLUMN_ICON_COLOR + " INTEGER NOT NULL DEFAULT 0, " +
COLUMN_LABEL + " TEXT, " +
COLUMN_SYSTEM_STATE + " TEXT, " +
"PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " +
");");
}
}
private ContentValues newContentValues(BitmapInfo bitmapInfo, String label, String packageName) {
ContentValues values = new ContentValues();
values.put(IconDB.COLUMN_ICON,
bitmapInfo.isLowRes() ? null : Utilities.flattenBitmap(bitmapInfo.icon));
values.put(IconDB.COLUMN_ICON_COLOR, bitmapInfo.color);
values.put(IconDB.COLUMN_LABEL, label);
values.put(IconDB.COLUMN_SYSTEM_STATE, mIconProvider.getIconSystemState(packageName));
return values;
}
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.graphics;
package com.android.launcher3.icons;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
@@ -37,6 +37,10 @@ public class BitmapInfo {
info.color = color;
}
public final boolean isLowRes() {
return LOW_RES_ICON == icon;
}
public static BitmapInfo fromBitmap(Bitmap bitmap) {
return fromBitmap(bitmap, null);
}
@@ -0,0 +1,87 @@
/*
* 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.launcher3.icons;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageManager;
import android.os.UserHandle;
public interface CachingLogic<T> {
ComponentName getComponent(T object);
UserHandle getUser(T object);
CharSequence getLabel(T object, PackageManager pm);
void loadIcon(Context context, BaseIconCache cache, T object, BitmapInfo target);
CachingLogic<LauncherActivityInfo> LAUNCHER_ACTIVITY_INFO =
new CachingLogic<LauncherActivityInfo>() {
@Override
public ComponentName getComponent(LauncherActivityInfo object) {
return object.getComponentName();
}
@Override
public UserHandle getUser(LauncherActivityInfo object) {
return object.getUser();
}
@Override
public CharSequence getLabel(LauncherActivityInfo object, PackageManager pm) {
return object.getLabel();
}
@Override
public void loadIcon(Context context, BaseIconCache cache, LauncherActivityInfo object,
BitmapInfo target) {
LauncherIcons li = LauncherIcons.obtain(context);
li.createBadgedIconBitmap(cache.getFullResIcon(object), object.getUser(),
object.getApplicationInfo().targetSdkVersion).applyTo(target);
li.recycle();
}
};
CachingLogic<ComponentWithLabel> COMPONENT_WITH_LABEL =
new CachingLogic<ComponentWithLabel>() {
@Override
public ComponentName getComponent(ComponentWithLabel object) {
return object.getComponent();
}
@Override
public UserHandle getUser(ComponentWithLabel object) {
return object.getUser();
}
@Override
public CharSequence getLabel(ComponentWithLabel object, PackageManager pm) {
return object.getLabel(pm);
}
@Override
public void loadIcon(Context context, BaseIconCache cache,
ComponentWithLabel object, BitmapInfo target) {
// Do not load icon.
target.icon = BitmapInfo.LOW_RES_ICON;
}
};
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.graphics;
package com.android.launcher3.icons;
import android.graphics.Bitmap;
import android.graphics.Color;
@@ -0,0 +1,29 @@
/*
* 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.launcher3.icons;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.os.UserHandle;
public interface ComponentWithLabel {
ComponentName getComponent();
UserHandle getUser();
CharSequence getLabel(PackageManager pm);
}
@@ -1,4 +1,4 @@
package com.android.launcher3.graphics;
package com.android.launcher3.icons;
import android.annotation.TargetApi;
import android.content.res.Resources;
+18 -519
View File
@@ -16,209 +16,43 @@
package com.android.launcher3.icons;
import static com.android.launcher3.graphics.BitmapInfo.LOW_RES_ICON;
import static com.android.launcher3.icons.CachingLogic.COMPONENT_WITH_LABEL;
import static com.android.launcher3.icons.CachingLogic.LAUNCHER_ACTIVITY_INFO;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.os.Build.VERSION;
import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import com.android.launcher3.AppInfo;
import com.android.launcher3.IconProvider;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherFiles;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.graphics.BitmapInfo;
import com.android.launcher3.graphics.BitmapRenderer;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Provider;
import com.android.launcher3.util.SQLiteCacheHelper;
import java.util.HashMap;
import java.util.HashSet;
import androidx.annotation.NonNull;
import androidx.core.graphics.ColorUtils;
/**
* Cache of application icons. Icons can be made from any thread.
*/
public class IconCache {
public class IconCache extends BaseIconCache {
private static final String TAG = "Launcher.IconCache";
private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
// Empty class name is used for storing package default entry.
public static final String EMPTY_CLASS_NAME = ".";
private static final boolean DEBUG = false;
private static final boolean DEBUG_IGNORE_CACHE = false;
public static class CacheEntry extends BitmapInfo {
public CharSequence title = "";
public CharSequence contentDescription = "";
public boolean isLowRes() {
return LOW_RES_ICON == icon;
}
}
private final HashMap<UserHandle, BitmapInfo> mDefaultIcons = new HashMap<>();
final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
final Context mContext;
final PackageManager mPackageManager;
final IconProvider mIconProvider;
final UserManagerCompat mUserManager;
final LauncherAppsCompat mLauncherApps;
private final HashMap<ComponentKey, CacheEntry> mCache =
new HashMap<>(INITIAL_ICON_CACHE_CAPACITY);
private final InstantAppResolver mInstantAppResolver;
private final int mIconDpi;
final IconDB mIconDb;
final Handler mWorkerHandler;
private final BitmapFactory.Options mDecodeOptions;
private int mPendingIconRequestCount = 0;
public IconCache(Context context, InvariantDeviceProfile inv) {
mContext = context;
mPackageManager = context.getPackageManager();
mUserManager = UserManagerCompat.getInstance(mContext);
mLauncherApps = LauncherAppsCompat.getInstance(mContext);
mInstantAppResolver = InstantAppResolver.newInstance(mContext);
mIconDpi = inv.fillResIconDpi;
mIconDb = new IconDB(context, inv.iconBitmapSize);
mIconProvider = IconProvider.newInstance(context);
mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
if (BitmapRenderer.USE_HARDWARE_BITMAP) {
mDecodeOptions = new BitmapFactory.Options();
mDecodeOptions.inPreferredConfig = Bitmap.Config.HARDWARE;
} else {
mDecodeOptions = null;
}
}
private Drawable getFullResDefaultActivityIcon() {
return getFullResIcon(Resources.getSystem(), Utilities.ATLEAST_OREO ?
android.R.drawable.sym_def_app_icon : android.R.mipmap.sym_def_app_icon);
}
private Drawable getFullResIcon(Resources resources, int iconId) {
Drawable d;
try {
d = resources.getDrawableForDensity(iconId, mIconDpi);
} catch (Resources.NotFoundException e) {
d = null;
}
return (d != null) ? d : getFullResDefaultActivityIcon();
}
public Drawable getFullResIcon(String packageName, int iconId) {
Resources resources;
try {
resources = mPackageManager.getResourcesForApplication(packageName);
} catch (PackageManager.NameNotFoundException e) {
resources = null;
}
if (resources != null) {
if (iconId != 0) {
return getFullResIcon(resources, iconId);
}
}
return getFullResDefaultActivityIcon();
}
public Drawable getFullResIcon(ActivityInfo info) {
Resources resources;
try {
resources = mPackageManager.getResourcesForApplication(
info.applicationInfo);
} catch (PackageManager.NameNotFoundException e) {
resources = null;
}
if (resources != null) {
int iconId = info.getIconResource();
if (iconId != 0) {
return getFullResIcon(resources, iconId);
}
}
return getFullResDefaultActivityIcon();
}
public Drawable getFullResIcon(LauncherActivityInfo info) {
return getFullResIcon(info, true);
}
public Drawable getFullResIcon(LauncherActivityInfo info, boolean flattenDrawable) {
return mIconProvider.getIcon(info, mIconDpi, flattenDrawable);
}
protected BitmapInfo makeDefaultIcon(UserHandle user) {
try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
return li.createBadgedIconBitmap(
getFullResDefaultActivityIcon(), user, VERSION.SDK_INT);
}
}
/**
* Remove any records for the supplied ComponentName.
*/
public synchronized void remove(ComponentName componentName, UserHandle user) {
mCache.remove(new ComponentKey(componentName, user));
}
/**
* Remove any records for the supplied package name from memory.
*/
private void removeFromMemCacheLocked(String packageName, UserHandle user) {
HashSet<ComponentKey> forDeletion = new HashSet<>();
for (ComponentKey key: mCache.keySet()) {
if (key.componentName.getPackageName().equals(packageName)
&& key.user.equals(user)) {
forDeletion.add(key);
}
}
for (ComponentKey condemned: forDeletion) {
mCache.remove(condemned);
}
super(context, inv.fillResIconDpi, inv.iconBitmapSize);
}
/**
@@ -231,76 +65,14 @@ public class IconCache {
PackageManager.GET_UNINSTALLED_PACKAGES);
long userSerial = mUserManager.getSerialNumberForUser(user);
for (LauncherActivityInfo app : mLauncherApps.getActivityList(packageName, user)) {
addIconToDBAndMemCache(app, info, userSerial, false /*replace existing*/);
addIconToDBAndMemCache(app, LAUNCHER_ACTIVITY_INFO, info, userSerial,
false /*replace existing*/);
}
} catch (NameNotFoundException e) {
Log.d(TAG, "Package not found", e);
}
}
/**
* Removes the entries related to the given package in memory and persistent DB.
*/
public synchronized void removeIconsForPkg(String packageName, UserHandle user) {
removeFromMemCacheLocked(packageName, user);
long userSerial = mUserManager.getSerialNumberForUser(user);
mIconDb.delete(
IconDB.COLUMN_COMPONENT + " LIKE ? AND " + IconDB.COLUMN_USER + " = ?",
new String[]{packageName + "/%", Long.toString(userSerial)});
}
public IconCacheUpdateHandler getUpdateHandler() {
mIconProvider.updateSystemStateString(mContext);
return new IconCacheUpdateHandler(this);
}
/**
* Adds an entry into the DB and the in-memory cache.
* @param replaceExisting if true, it will recreate the bitmap even if it already exists in
* the memory. This is useful then the previous bitmap was created using
* old data.
* package private
*/
synchronized void addIconToDBAndMemCache(LauncherActivityInfo app,
PackageInfo info, long userSerial, boolean replaceExisting) {
final ComponentKey key = new ComponentKey(app.getComponentName(), app.getUser());
CacheEntry entry = null;
if (!replaceExisting) {
entry = mCache.get(key);
// We can't reuse the entry if the high-res icon is not present.
if (entry == null || entry.icon == null || entry.isLowRes()) {
entry = null;
}
}
if (entry == null) {
entry = new CacheEntry();
LauncherIcons li = LauncherIcons.obtain(mContext);
li.createBadgedIconBitmap(getFullResIcon(app), app.getUser(),
app.getApplicationInfo().targetSdkVersion).applyTo(entry);
li.recycle();
}
entry.title = app.getLabel();
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
mCache.put(key, entry);
ContentValues values = newContentValues(entry.icon, entry.color,
entry.title.toString(), app.getApplicationInfo().packageName);
addIconToDB(values, app.getComponentName(), info, userSerial);
}
/**
* Updates {@param values} to contain versioning information and adds it to the DB.
* @param values {@link ContentValues} containing icon & title
*/
private void addIconToDB(ContentValues values, ComponentName key,
PackageInfo info, long userSerial) {
values.put(IconDB.COLUMN_COMPONENT, key.flattenToString());
values.put(IconDB.COLUMN_USER, userSerial);
values.put(IconDB.COLUMN_LAST_UPDATED, info.lastUpdateTime);
values.put(IconDB.COLUMN_VERSION, info.versionCode);
mIconDb.insertOrReplace(values);
}
/**
* Fetches high-res icon for the provided ItemInfo and updates the caller when done.
* @return a request ID that can be used to cancel the request.
@@ -343,8 +115,8 @@ public class IconCache {
*/
public synchronized void updateTitleAndIcon(AppInfo application) {
CacheEntry entry = cacheLocked(application.componentName,
Provider.<LauncherActivityInfo>of(null),
application.user, false, application.usingLowResIcon());
application.user, Provider.of(null), LAUNCHER_ACTIVITY_INFO,
false, application.usingLowResIcon());
if (entry.icon != null && !isDefaultIcon(entry.icon, application.user)) {
applyCacheEntry(entry, application);
}
@@ -377,6 +149,13 @@ public class IconCache {
}
}
public synchronized String getTitleNoCache(ComponentWithLabel info) {
CacheEntry entry = cacheLocked(info.getComponent(), info.getUser(), Provider.of(info),
COMPONENT_WITH_LABEL, false /* usePackageIcon */, true /* useLowResIcon */,
false /* addToMemCache */);
return Utilities.trim(entry.title);
}
/**
* Fill in {@param shortcutInfo} with the icon and label for {@param info}
*/
@@ -384,239 +163,12 @@ public class IconCache {
@NonNull ItemInfoWithIcon infoInOut,
@NonNull Provider<LauncherActivityInfo> activityInfoProvider,
boolean usePkgIcon, boolean useLowResIcon) {
CacheEntry entry = cacheLocked(infoInOut.getTargetComponent(), activityInfoProvider,
infoInOut.user, usePkgIcon, useLowResIcon);
CacheEntry entry = cacheLocked(infoInOut.getTargetComponent(), infoInOut.user,
activityInfoProvider,
LAUNCHER_ACTIVITY_INFO, usePkgIcon, useLowResIcon);
applyCacheEntry(entry, infoInOut);
}
/**
* Fill in {@param infoInOut} with the corresponding icon and label.
*/
public synchronized void getTitleAndIconForApp(
PackageItemInfo infoInOut, boolean useLowResIcon) {
CacheEntry entry = getEntryForPackageLocked(
infoInOut.packageName, infoInOut.user, useLowResIcon);
applyCacheEntry(entry, infoInOut);
}
private void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) {
info.title = Utilities.trim(entry.title);
info.contentDescription = entry.contentDescription;
((entry.icon == null) ? getDefaultIcon(info.user) : entry).applyTo(info);
}
public synchronized BitmapInfo getDefaultIcon(UserHandle user) {
if (!mDefaultIcons.containsKey(user)) {
mDefaultIcons.put(user, makeDefaultIcon(user));
}
return mDefaultIcons.get(user);
}
public boolean isDefaultIcon(Bitmap icon, UserHandle user) {
return getDefaultIcon(user).icon == icon;
}
/**
* Retrieves the entry from the cache. If the entry is not present, it creates a new entry.
* This method is not thread safe, it must be called from a synchronized method.
*/
protected CacheEntry cacheLocked(
@NonNull ComponentName componentName,
@NonNull Provider<LauncherActivityInfo> infoProvider,
UserHandle user, boolean usePackageIcon, boolean useLowResIcon) {
Preconditions.assertWorkerThread();
ComponentKey cacheKey = new ComponentKey(componentName, user);
CacheEntry entry = mCache.get(cacheKey);
if (entry == null || (entry.isLowRes() && !useLowResIcon)) {
entry = new CacheEntry();
mCache.put(cacheKey, entry);
// Check the DB first.
LauncherActivityInfo info = null;
boolean providerFetchedOnce = false;
if (!getEntryFromDB(cacheKey, entry, useLowResIcon) || DEBUG_IGNORE_CACHE) {
info = infoProvider.get();
providerFetchedOnce = true;
if (info != null) {
LauncherIcons li = LauncherIcons.obtain(mContext);
li.createBadgedIconBitmap(getFullResIcon(info), info.getUser(),
info.getApplicationInfo().targetSdkVersion).applyTo(entry);
li.recycle();
} else {
if (usePackageIcon) {
CacheEntry packageEntry = getEntryForPackageLocked(
componentName.getPackageName(), user, false);
if (packageEntry != null) {
if (DEBUG) Log.d(TAG, "using package default icon for " +
componentName.toShortString());
packageEntry.applyTo(entry);
entry.title = packageEntry.title;
entry.contentDescription = packageEntry.contentDescription;
}
}
if (entry.icon == null) {
if (DEBUG) Log.d(TAG, "using default icon for " +
componentName.toShortString());
getDefaultIcon(user).applyTo(entry);
}
}
}
if (TextUtils.isEmpty(entry.title)) {
if (info == null && !providerFetchedOnce) {
info = infoProvider.get();
providerFetchedOnce = true;
}
if (info != null) {
entry.title = info.getLabel();
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
}
}
}
return entry;
}
public synchronized void clear() {
Preconditions.assertWorkerThread();
mIconDb.clear();
}
/**
* Adds a default package entry in the cache. This entry is not persisted and will be removed
* when the cache is flushed.
*/
public synchronized void cachePackageInstallInfo(String packageName, UserHandle user,
Bitmap icon, CharSequence title) {
removeFromMemCacheLocked(packageName, user);
ComponentKey cacheKey = getPackageKey(packageName, user);
CacheEntry entry = mCache.get(cacheKey);
// For icon caching, do not go through DB. Just update the in-memory entry.
if (entry == null) {
entry = new CacheEntry();
}
if (!TextUtils.isEmpty(title)) {
entry.title = title;
}
if (icon != null) {
LauncherIcons li = LauncherIcons.obtain(mContext);
li.createIconBitmap(icon).applyTo(entry);
li.recycle();
}
if (!TextUtils.isEmpty(title) && entry.icon != null) {
mCache.put(cacheKey, entry);
}
}
private static ComponentKey getPackageKey(String packageName, UserHandle user) {
ComponentName cn = new ComponentName(packageName, packageName + EMPTY_CLASS_NAME);
return new ComponentKey(cn, user);
}
/**
* Gets an entry for the package, which can be used as a fallback entry for various components.
* This method is not thread safe, it must be called from a synchronized method.
*/
private CacheEntry getEntryForPackageLocked(String packageName, UserHandle user,
boolean useLowResIcon) {
Preconditions.assertWorkerThread();
ComponentKey cacheKey = getPackageKey(packageName, user);
CacheEntry entry = mCache.get(cacheKey);
if (entry == null || (entry.isLowRes() && !useLowResIcon)) {
entry = new CacheEntry();
boolean entryUpdated = true;
// Check the DB first.
if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) {
try {
int flags = Process.myUserHandle().equals(user) ? 0 :
PackageManager.GET_UNINSTALLED_PACKAGES;
PackageInfo info = mPackageManager.getPackageInfo(packageName, flags);
ApplicationInfo appInfo = info.applicationInfo;
if (appInfo == null) {
throw new NameNotFoundException("ApplicationInfo is null");
}
LauncherIcons li = LauncherIcons.obtain(mContext);
// 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
BitmapInfo iconInfo = li.createBadgedIconBitmap(
appInfo.loadIcon(mPackageManager), user, appInfo.targetSdkVersion,
mInstantAppResolver.isInstantApp(appInfo));
li.recycle();
entry.title = appInfo.loadLabel(mPackageManager);
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
entry.icon = useLowResIcon ? LOW_RES_ICON : iconInfo.icon;
entry.color = iconInfo.color;
// Add the icon in the DB here, since these do not get written during
// package updates.
ContentValues values = newContentValues(iconInfo.icon, entry.color,
entry.title.toString(), packageName);
addIconToDB(values, cacheKey.componentName, info,
mUserManager.getSerialNumberForUser(user));
} catch (NameNotFoundException e) {
if (DEBUG) Log.d(TAG, "Application not installed " + packageName);
entryUpdated = false;
}
}
// Only add a filled-out entry to the cache
if (entryUpdated) {
mCache.put(cacheKey, entry);
}
}
return entry;
}
private boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) {
Cursor c = null;
try {
c = mIconDb.query(
lowRes ? IconDB.COLUMNS_LOW_RES : IconDB.COLUMNS_HIGH_RES,
IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
new String[]{
cacheKey.componentName.flattenToString(),
Long.toString(mUserManager.getSerialNumberForUser(cacheKey.user))});
if (c.moveToNext()) {
// Set the alpha to be 255, so that we never have a wrong color
entry.color = ColorUtils.setAlphaComponent(c.getInt(0), 255);
entry.title = c.getString(1);
if (entry.title == null) {
entry.title = "";
entry.contentDescription = "";
} else {
entry.contentDescription = mUserManager.getBadgedLabelForUser(
entry.title, cacheKey.user);
}
if (lowRes) {
entry.icon = LOW_RES_ICON;
} else {
byte[] data = c.getBlob(2);
try {
entry.icon = BitmapFactory.decodeByteArray(data, 0, data.length,
mDecodeOptions);
} catch (Exception e) { }
}
return true;
}
} catch (SQLiteException e) {
Log.d(TAG, "Error reading icon cache", e);
} finally {
if (c != null) {
c.close();
}
}
return false;
}
public static abstract class IconLoadRequest implements Runnable {
private final Handler mHandler;
private final Runnable mEndRunnable;
@@ -641,59 +193,6 @@ public class IconCache {
}
}
static final class IconDB extends SQLiteCacheHelper {
private final static int RELEASE_VERSION = 25;
public final static String TABLE_NAME = "icons";
public final static String COLUMN_ROWID = "rowid";
public final static String COLUMN_COMPONENT = "componentName";
public final static String COLUMN_USER = "profileId";
public final static String COLUMN_LAST_UPDATED = "lastUpdated";
public final static String COLUMN_VERSION = "version";
public final static String COLUMN_ICON = "icon";
public final static String COLUMN_ICON_COLOR = "icon_color";
public final static String COLUMN_LABEL = "label";
public final static String COLUMN_SYSTEM_STATE = "system_state";
public final static String[] COLUMNS_HIGH_RES = new String[] {
IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL, IconDB.COLUMN_ICON };
public final static String[] COLUMNS_LOW_RES = new String[] {
IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL };
public IconDB(Context context, int iconPixelSize) {
super(context, LauncherFiles.APP_ICONS_DB,
(RELEASE_VERSION << 16) + iconPixelSize,
TABLE_NAME);
}
@Override
protected void onCreateTable(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
COLUMN_COMPONENT + " TEXT NOT NULL, " +
COLUMN_USER + " INTEGER NOT NULL, " +
COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, " +
COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " +
COLUMN_ICON + " BLOB, " +
COLUMN_ICON_COLOR + " INTEGER NOT NULL DEFAULT 0, " +
COLUMN_LABEL + " TEXT, " +
COLUMN_SYSTEM_STATE + " TEXT, " +
"PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " +
");");
}
}
private ContentValues newContentValues(Bitmap icon, int iconColor, String label,
String packageName) {
ContentValues values = new ContentValues();
values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(icon));
values.put(IconDB.COLUMN_ICON_COLOR, iconColor);
values.put(IconDB.COLUMN_LABEL, label);
values.put(IconDB.COLUMN_SYSTEM_STATE, mIconProvider.getIconSystemState(packageName));
return values;
}
/**
* Interface for receiving itemInfo with high-res icon.
*/
@@ -17,7 +17,6 @@ package com.android.launcher3.icons;
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
@@ -26,15 +25,17 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseBooleanArray;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.IconCache.IconDB;
import com.android.launcher3.icons.BaseIconCache.IconDB;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
@@ -45,13 +46,29 @@ public class IconCacheUpdateHandler {
private static final String TAG = "IconCacheUpdateHandler";
/**
* In this mode, all invalid icons are marked as to-be-deleted in {@link #mItemsToDelete}.
* This mode is used for the first run.
*/
private static final boolean MODE_SET_INVALID_ITEMS = true;
/**
* In this mode, any valid icon is removed from {@link #mItemsToDelete}. This is used for all
* subsequent runs, which essentially acts as set-union of all valid items.
*/
private static final boolean MODE_CLEAR_VALID_ITEMS = false;
private static final Object ICON_UPDATE_TOKEN = new Object();
private final HashMap<String, PackageInfo> mPkgInfoMap;
private final IconCache mIconCache;
private final BaseIconCache mIconCache;
private final HashMap<UserHandle, Set<String>> mPackagesToIgnore = new HashMap<>();
IconCacheUpdateHandler(IconCache cache) {
private final SparseBooleanArray mItemsToDelete = new SparseBooleanArray();
private boolean mFilterMode = MODE_SET_INVALID_ITEMS;
IconCacheUpdateHandler(BaseIconCache cache) {
mIconCache = cache;
mPkgInfoMap = new HashMap<>();
@@ -68,9 +85,8 @@ public class IconCacheUpdateHandler {
private void createPackageInfoMap() {
PackageManager pm = mIconCache.mPackageManager;
HashMap<String, PackageInfo> pkgInfoMap = new HashMap<>();
for (PackageInfo info : pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
pkgInfoMap.put(info.packageName, info);
mPkgInfoMap.put(info.packageName, info);
}
}
@@ -79,25 +95,44 @@ public class IconCacheUpdateHandler {
* the DB and are updated.
* @return The set of packages for which icons have updated.
*/
public void updateIcons(List<LauncherActivityInfo> apps) {
if (apps.isEmpty()) {
return;
public <T> void updateIcons(List<T> apps, CachingLogic<T> cachingLogic,
OnUpdateCallback onUpdateCallback) {
// Filter the list per user
HashMap<UserHandle, HashMap<ComponentName, T>> userComponentMap = new HashMap<>();
int count = apps.size();
for (int i = 0; i < count; i++) {
T app = apps.get(i);
UserHandle userHandle = cachingLogic.getUser(app);
HashMap<ComponentName, T> componentMap = userComponentMap.get(userHandle);
if (componentMap == null) {
componentMap = new HashMap<>();
userComponentMap.put(userHandle, componentMap);
}
componentMap.put(cachingLogic.getComponent(app), app);
}
UserHandle user = apps.get(0).getUser();
for (Entry<UserHandle, HashMap<ComponentName, T>> entry : userComponentMap.entrySet()) {
updateIconsPerUser(entry.getKey(), entry.getValue(), cachingLogic, onUpdateCallback);
}
// From now on, clear every valid item from the global valid map.
mFilterMode = MODE_CLEAR_VALID_ITEMS;
}
/**
* Updates the persistent DB, such that only entries corresponding to {@param apps} remain in
* the DB and are updated.
* @return The set of packages for which icons have updated.
*/
private <T> void updateIconsPerUser(UserHandle user, HashMap<ComponentName, T> componentMap,
CachingLogic<T> cachingLogic, OnUpdateCallback onUpdateCallback) {
Set<String> ignorePackages = mPackagesToIgnore.get(user);
if (ignorePackages == null) {
ignorePackages = Collections.emptySet();
}
long userSerial = mIconCache.mUserManager.getSerialNumberForUser(user);
HashMap<ComponentName, LauncherActivityInfo> componentMap = new HashMap<>();
for (LauncherActivityInfo app : apps) {
componentMap.put(app.getComponentName(), app);
}
HashSet<Integer> itemsToRemove = new HashSet<>();
Stack<LauncherActivityInfo> appsToUpdate = new Stack<>();
Stack<T> appsToUpdate = new Stack<>();
try (Cursor c = mIconCache.mIconDb.query(
new String[]{IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT,
@@ -116,10 +151,15 @@ public class IconCacheUpdateHandler {
String cn = c.getString(indexComponent);
ComponentName component = ComponentName.unflattenFromString(cn);
PackageInfo info = mPkgInfoMap.get(component.getPackageName());
int rowId = c.getInt(rowIndex);
if (info == null) {
if (!ignorePackages.contains(component.getPackageName())) {
mIconCache.remove(component, user);
itemsToRemove.add(c.getInt(rowIndex));
if (mFilterMode == MODE_SET_INVALID_ITEMS) {
mIconCache.remove(component, user);
mItemsToDelete.put(rowId, true);
}
}
continue;
}
@@ -130,15 +170,21 @@ public class IconCacheUpdateHandler {
long updateTime = c.getLong(indexLastUpdate);
int version = c.getInt(indexVersion);
LauncherActivityInfo app = componentMap.remove(component);
T app = componentMap.remove(component);
if (version == info.versionCode && updateTime == info.lastUpdateTime &&
TextUtils.equals(c.getString(systemStateIndex),
mIconCache.mIconProvider.getIconSystemState(info.packageName))) {
if (mFilterMode == MODE_CLEAR_VALID_ITEMS) {
mItemsToDelete.put(rowId, false);
}
continue;
}
if (app == null) {
mIconCache.remove(component, user);
itemsToRemove.add(c.getInt(rowIndex));
if (mFilterMode == MODE_SET_INVALID_ITEMS) {
mIconCache.remove(component, user);
mItemsToDelete.put(rowId, true);
}
} else {
appsToUpdate.add(app);
}
@@ -147,16 +193,28 @@ public class IconCacheUpdateHandler {
Log.d(TAG, "Error reading icon cache", e);
// Continue updating whatever we have read so far
}
if (!itemsToRemove.isEmpty()) {
mIconCache.mIconDb.delete(
Utilities.createDbSelectionQuery(IconDB.COLUMN_ROWID, itemsToRemove), null);
}
// Insert remaining apps.
if (!componentMap.isEmpty() || !appsToUpdate.isEmpty()) {
Stack<LauncherActivityInfo> appsToAdd = new Stack<>();
Stack<T> appsToAdd = new Stack<>();
appsToAdd.addAll(componentMap.values());
new SerializedIconUpdateTask(userSerial, user, appsToAdd, appsToUpdate).scheduleNext();
new SerializedIconUpdateTask(userSerial, user, appsToAdd, appsToUpdate, cachingLogic,
onUpdateCallback).scheduleNext();
}
}
public void finish() {
// Commit all deletes
ArrayList<Integer> deleteIds = new ArrayList<>();
int count = mItemsToDelete.size();
for (int i = 0; i < count; i++) {
if (mItemsToDelete.valueAt(i)) {
deleteIds.add(mItemsToDelete.keyAt(i));
}
}
if (!deleteIds.isEmpty()) {
mIconCache.mIconDb.delete(
Utilities.createDbSelectionQuery(IconDB.COLUMN_ROWID, deleteIds), null);
}
}
@@ -166,47 +224,51 @@ public class IconCacheUpdateHandler {
* LauncherActivityInfo list. Items are updated/added one at a time, so that the
* worker thread doesn't get blocked.
*/
private class SerializedIconUpdateTask implements Runnable {
private class SerializedIconUpdateTask<T> implements Runnable {
private final long mUserSerial;
private final UserHandle mUserHandle;
private final Stack<LauncherActivityInfo> mAppsToAdd;
private final Stack<LauncherActivityInfo> mAppsToUpdate;
private final Stack<T> mAppsToAdd;
private final Stack<T> mAppsToUpdate;
private final CachingLogic<T> mCachingLogic;
private final HashSet<String> mUpdatedPackages = new HashSet<>();
private final OnUpdateCallback mOnUpdateCallback;
SerializedIconUpdateTask(long userSerial, UserHandle userHandle,
Stack<LauncherActivityInfo> appsToAdd, Stack<LauncherActivityInfo> appsToUpdate) {
Stack<T> appsToAdd, Stack<T> appsToUpdate, CachingLogic<T> cachingLogic,
OnUpdateCallback onUpdateCallback) {
mUserHandle = userHandle;
mUserSerial = userSerial;
mAppsToAdd = appsToAdd;
mAppsToUpdate = appsToUpdate;
mCachingLogic = cachingLogic;
mOnUpdateCallback = onUpdateCallback;
}
@Override
public void run() {
if (!mAppsToUpdate.isEmpty()) {
LauncherActivityInfo app = mAppsToUpdate.pop();
String pkg = app.getComponentName().getPackageName();
T app = mAppsToUpdate.pop();
String pkg = mCachingLogic.getComponent(app).getPackageName();
PackageInfo info = mPkgInfoMap.get(pkg);
mIconCache.addIconToDBAndMemCache(
app, info, mUserSerial, true /*replace existing*/);
app, mCachingLogic, info, mUserSerial, true /*replace existing*/);
mUpdatedPackages.add(pkg);
if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) {
// No more app to update. Notify model.
LauncherAppState.getInstance(mIconCache.mContext).getModel()
.onPackageIconsUpdated(mUpdatedPackages, mUserHandle);
// No more app to update. Notify callback.
mOnUpdateCallback.onPackageIconsUpdated(mUpdatedPackages, mUserHandle);
}
// Let it run one more time.
scheduleNext();
} else if (!mAppsToAdd.isEmpty()) {
LauncherActivityInfo app = mAppsToAdd.pop();
PackageInfo info = mPkgInfoMap.get(app.getComponentName().getPackageName());
T app = mAppsToAdd.pop();
PackageInfo info = mPkgInfoMap.get(mCachingLogic.getComponent(app).getPackageName());
// We do not check the mPkgInfoMap when generating the mAppsToAdd. Although every
// app should have package info, this is not guaranteed by the api
if (info != null) {
mIconCache.addIconToDBAndMemCache(
app, info, mUserSerial, false /*replace existing*/);
mIconCache.addIconToDBAndMemCache(app, mCachingLogic, info,
mUserSerial, false /*replace existing*/);
}
if (!mAppsToAdd.isEmpty()) {
@@ -220,4 +282,9 @@ public class IconCacheUpdateHandler {
SystemClock.uptimeMillis() + 1);
}
}
public interface OnUpdateCallback {
void onPackageIconsUpdated(HashSet<String> updatedPackages, UserHandle user);
}
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.launcher3.graphics;
package com.android.launcher3.icons;
import android.content.Context;
import android.graphics.Bitmap;
@@ -14,12 +14,12 @@
* limitations under the License.
*/
package com.android.launcher3.graphics;
package com.android.launcher3.icons;
import static android.graphics.Paint.DITHER_FLAG;
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
import static com.android.launcher3.graphics.ShadowGenerator.BLUR_FACTOR;
import static com.android.launcher3.icons.ShadowGenerator.BLUR_FACTOR;
import android.content.ComponentName;
import android.content.Context;
@@ -44,7 +44,7 @@ import android.os.UserHandle;
import com.android.launcher3.AppInfo;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.graphics.BitmapRenderer;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherAppState;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.launcher3.graphics;
package com.android.launcher3.icons;
import android.content.Context;
import android.graphics.Bitmap;
@@ -43,8 +43,8 @@ import com.android.launcher3.Workspace;
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.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.GridOccupancy;
+22 -17
View File
@@ -20,6 +20,8 @@ import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
import static com.android.launcher3.icons.CachingLogic.COMPONENT_WITH_LABEL;
import static com.android.launcher3.icons.CachingLogic.LAUNCHER_ACTIVITY_INFO;
import static com.android.launcher3.model.LoaderResults.filterCurrentWorkspaceItems;
import android.appwidget.AppWidgetProviderInfo;
@@ -43,6 +45,7 @@ import android.util.MutableInt;
import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppInfo;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.IconCacheUpdateHandler;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.InstallShortcutReceiver;
@@ -60,7 +63,7 @@ import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIconPreviewVerifier;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.provider.ImportDataTask;
import com.android.launcher3.shortcuts.DeepShortcutManager;
@@ -183,7 +186,7 @@ public class LoaderTask implements Runnable {
// second step
TraceHelper.partitionSection(TAG, "step 2.1: loading all apps");
List<List<LauncherActivityInfo>> activityListPerUser = loadAllApps();
List<LauncherActivityInfo> allActivityList = loadAllApps();
TraceHelper.partitionSection(TAG, "step 2.2: Binding all apps");
verifyNotStopped();
@@ -193,7 +196,8 @@ public class LoaderTask implements Runnable {
TraceHelper.partitionSection(TAG, "step 2.3: Update icon cache");
IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
setIgnorePackages(updateHandler);
updateIconCacheForApps(updateHandler, activityListPerUser);
updateHandler.updateIcons(allActivityList, LAUNCHER_ACTIVITY_INFO,
mApp.getModel()::onPackageIconsUpdated);
// Take a break
TraceHelper.partitionSection(TAG, "step 2 completed, wait for idle");
@@ -215,12 +219,21 @@ public class LoaderTask implements Runnable {
// fourth step
TraceHelper.partitionSection(TAG, "step 4.1: loading widgets");
mBgDataModel.widgetsModel.update(mApp, null);
List<ComponentWithLabel> allWidgetsList = mBgDataModel.widgetsModel.update(mApp, null);
verifyNotStopped();
TraceHelper.partitionSection(TAG, "step 4.2: Binding widgets");
mResults.bindWidgets();
verifyNotStopped();
TraceHelper.partitionSection(TAG, "step 4.3: Update icon cache");
updateHandler.updateIcons(allWidgetsList, COMPONENT_WITH_LABEL,
mApp.getModel()::onWidgetLabelsUpdated);
verifyNotStopped();
TraceHelper.partitionSection(TAG, "step 5: Finish icon cache update");
updateHandler.finish();
transaction.commit();
} catch (CancellationException e) {
// Loader stopped, ignore
@@ -798,17 +811,9 @@ public class LoaderTask implements Runnable {
updateHandler.setPackagesToIgnore(Process.myUserHandle(), packagesToIgnore);
}
private void updateIconCacheForApps(IconCacheUpdateHandler updateHandler,
List<List<LauncherActivityInfo>> activityListPerUser) {
int userCount = activityListPerUser.size();
for (int i = 0; i < userCount; i++) {
updateHandler.updateIcons(activityListPerUser.get(i));
}
}
private List<List<LauncherActivityInfo>> loadAllApps() {
private List<LauncherActivityInfo> loadAllApps() {
final List<UserHandle> profiles = mUserManager.getUserProfiles();
List<List<LauncherActivityInfo>> activityListPerUser = new ArrayList<>();
List<LauncherActivityInfo> allActivityList = new ArrayList<>();
// Clear the list of apps
mBgAllAppsList.clear();
for (UserHandle user : profiles) {
@@ -817,7 +822,7 @@ public class LoaderTask implements Runnable {
// Fail if we don't have any apps
// TODO: Fix this. Only fail for the current user.
if (apps == null || apps.isEmpty()) {
return activityListPerUser;
return allActivityList;
}
boolean quietMode = mUserManager.isQuietModeEnabled(user);
// Create the ApplicationInfos
@@ -826,7 +831,7 @@ public class LoaderTask implements Runnable {
// This builds the icon bitmaps.
mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
}
activityListPerUser.add(apps);
allActivityList.addAll(apps);
}
if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
@@ -839,7 +844,7 @@ public class LoaderTask implements Runnable {
}
mBgAllAppsList.added = new ArrayList<>();
return activityListPerUser;
return allActivityList;
}
private void loadDeepShortcuts() {
@@ -38,8 +38,8 @@ import com.android.launcher3.Utilities;
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.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
@@ -23,7 +23,7 @@ import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.shortcuts.ShortcutKey;
@@ -26,7 +26,7 @@ import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.shortcuts.ShortcutKey;
@@ -9,6 +9,7 @@ import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.ShortcutConfigActivityInfo;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.util.ComponentKey;
import java.text.Collator;
@@ -29,11 +30,11 @@ public class WidgetItem extends ComponentKey implements Comparable<WidgetItem> {
public final String label;
public final int spanX, spanY;
public WidgetItem(LauncherAppWidgetProviderInfo info, PackageManager pm,
InvariantDeviceProfile idp) {
public WidgetItem(LauncherAppWidgetProviderInfo info,
InvariantDeviceProfile idp, IconCache iconCache) {
super(info.provider, info.getProfile());
label = Utilities.trim(info.getLabel(pm));
label = iconCache.getTitleNoCache(info);
widgetInfo = info;
activityInfo = null;
@@ -41,9 +42,10 @@ public class WidgetItem extends ComponentKey implements Comparable<WidgetItem> {
spanY = Math.min(info.spanY, idp.numRows);
}
public WidgetItem(ShortcutConfigActivityInfo info) {
public WidgetItem(ShortcutConfigActivityInfo info, IconCache iconCache, PackageManager pm) {
super(info.getComponent(), info.getUser());
label = Utilities.trim(info.getLabel());
label = info.isPersistable() ? iconCache.getTitleNoCache(info) :
Utilities.trim(info.getLabel(pm));
widgetInfo = null;
activityInfo = info;
spanX = spanY = 1;
@@ -11,6 +11,7 @@ import android.os.UserHandle;
import android.util.Log;
import com.android.launcher3.AppFilter;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
@@ -31,7 +32,10 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import androidx.annotation.Nullable;
@@ -76,26 +80,32 @@ public class WidgetsModel {
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
*/
public void update(LauncherAppState app, @Nullable PackageUserKey packageUser) {
public List<ComponentWithLabel> update(LauncherAppState app, @Nullable PackageUserKey packageUser) {
Preconditions.assertWorkerThread();
Context context = app.getContext();
final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
List<ComponentWithLabel> updatedItems = new ArrayList<>();
try {
PackageManager pm = context.getPackageManager();
InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
PackageManager pm = app.getContext().getPackageManager();
// Widgets
AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(context);
for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(packageUser)) {
widgetsAndShortcuts.add(new WidgetItem(LauncherAppWidgetProviderInfo
.fromProviderInfo(context, widgetInfo), pm, idp));
LauncherAppWidgetProviderInfo launcherWidgetInfo =
LauncherAppWidgetProviderInfo.fromProviderInfo(context, widgetInfo);
widgetsAndShortcuts.add(new WidgetItem(
launcherWidgetInfo, idp, app.getIconCache()));
updatedItems.add(launcherWidgetInfo);
}
// Shortcuts
for (ShortcutConfigActivityInfo info : LauncherAppsCompat.getInstance(context)
.getCustomShortcutActivityList(packageUser)) {
widgetsAndShortcuts.add(new WidgetItem(info));
widgetsAndShortcuts.add(new WidgetItem(info, app.getIconCache(), pm));
updatedItems.add(info);
}
setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
} catch (Exception e) {
@@ -110,6 +120,7 @@ public class WidgetsModel {
}
app.getWidgetCache().removeObsoletePreviews(widgetsAndShortcuts, packageUser);
return updatedItems;
}
private synchronized void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
@@ -204,4 +215,26 @@ public class WidgetsModel {
iconCache.getTitleAndIconForApp(p, true /* userLowResIcon */);
}
}
public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user,
LauncherAppState app) {
for (Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : mWidgetsList.entrySet()) {
if (packageNames.contains(entry.getKey().packageName)) {
ArrayList<WidgetItem> items = entry.getValue();
int count = items.size();
for (int i = 0; i < count; i++) {
WidgetItem item = items.get(i);
if (item.user.equals(user)) {
if (item.activityInfo != null) {
items.set(i, new WidgetItem(item.activityInfo, app.getIconCache(),
app.getContext().getPackageManager()));
} else {
items.set(i, new WidgetItem(item.widgetInfo,
app.getInvariantDeviceProfile(), app.getIconCache()));
}
}
}
}
}
}
}
@@ -179,7 +179,7 @@ public class NotificationMainView extends FrameLayout implements SwipeDetector.L
@Override
public boolean onDrag(float displacement, float velocity) {
public boolean onDrag(float displacement) {
setContentTranslation(canChildBeDismissed()
? displacement : OverScroll.dampedScroll(displacement, getWidth()));
mContentTranslateAnimator.cancel();
@@ -24,7 +24,7 @@ import android.service.notification.StatusBarNotification;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.notification.NotificationInfo;
import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.shortcuts.DeepShortcutManager;
@@ -256,7 +256,7 @@ public abstract class AbstractStateChangeTouchController
}
@Override
public boolean onDrag(float displacement, float velocity) {
public boolean onDrag(float displacement) {
float deltaProgress = mProgressMultiplier * (displacement - mDisplacementShift);
float progress = deltaProgress + mStartProgress;
updateProgress(progress);
@@ -21,6 +21,7 @@ import android.content.Context;
import android.graphics.PointF;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import androidx.annotation.NonNull;
@@ -52,12 +53,6 @@ public class SwipeDetector {
*/
public static final float RELEASE_VELOCITY_PX_MS = 1.0f;
/**
* The time constant used to calculate dampening in the low-pass filter of scroll velocity.
* Cutoff frequency is set at 10 Hz.
*/
public static final float SCROLL_VELOCITY_DAMPENING_RC = 1000f / (2f * (float) Math.PI * 10);
/* Scroll state, this is set to true during dragging and animation. */
private ScrollState mState = ScrollState.IDLE;
@@ -75,6 +70,8 @@ public class SwipeDetector {
* Distance in pixels a touch can wander before we think the user is scrolling.
*/
abstract float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos);
abstract float getVelocity(VelocityTracker tracker);
}
public static final Direction VERTICAL = new Direction() {
@@ -88,6 +85,11 @@ public class SwipeDetector {
float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
return Math.abs(ev.getX(pointerIndex) - downPos.x);
}
@Override
float getVelocity(VelocityTracker tracker) {
return tracker.getYVelocity();
}
};
public static final Direction HORIZONTAL = new Direction() {
@@ -101,6 +103,11 @@ public class SwipeDetector {
float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
return Math.abs(ev.getY(pointerIndex) - downPos.y);
}
@Override
float getVelocity(VelocityTracker tracker) {
return tracker.getXVelocity();
}
};
//------------------- ScrollState transition diagram -----------------------------------
@@ -151,16 +158,16 @@ public class SwipeDetector {
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
private Direction mDir;
private final Direction mDir;
private final float mTouchSlop;
private final float mMaxVelocity;
/* Client of this gesture detector can register a callback. */
private final Listener mListener;
private long mCurrentMillis;
private VelocityTracker mVelocityTracker;
private float mVelocity;
private float mLastDisplacement;
private float mDisplacement;
@@ -170,24 +177,22 @@ public class SwipeDetector {
public interface Listener {
void onDragStart(boolean start);
boolean onDrag(float displacement, float velocity);
boolean onDrag(float displacement);
void onDragEnd(float velocity, boolean fling);
}
public SwipeDetector(@NonNull Context context, @NonNull Listener l, @NonNull Direction dir) {
this(ViewConfiguration.get(context).getScaledTouchSlop(), l, dir);
this(ViewConfiguration.get(context), l, dir);
}
@VisibleForTesting
protected SwipeDetector(float touchSlope, @NonNull Listener l, @NonNull Direction dir) {
mTouchSlop = touchSlope;
protected SwipeDetector(@NonNull ViewConfiguration config, @NonNull Listener l,
@NonNull Direction dir) {
mListener = l;
mDir = dir;
}
public void updateDirection(Direction dir) {
mDir = dir;
mTouchSlop = config.getScaledTouchSlop();
mMaxVelocity = config.getScaledMaximumFlingVelocity();
}
public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
@@ -215,14 +220,22 @@ public class SwipeDetector {
}
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getActionMasked()) {
int actionMasked = ev.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN && mVelocityTracker != null) {
mVelocityTracker.clear();
}
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
switch (actionMasked) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = ev.getPointerId(0);
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
mLastDisplacement = 0;
mDisplacement = 0;
mVelocity = 0;
if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
setState(ScrollState.DRAGGING);
@@ -247,8 +260,6 @@ public class SwipeDetector {
break;
}
mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos);
computeVelocity(mDir.getDisplacement(ev, pointerIndex, mLastPos),
ev.getEventTime());
// handle state and listener calls.
if (mState != ScrollState.DRAGGING && shouldScrollStart(ev, pointerIndex)) {
@@ -265,6 +276,8 @@ public class SwipeDetector {
if (mState == ScrollState.DRAGGING) {
setState(ScrollState.SETTLING);
}
mVelocityTracker.recycle();
mVelocityTracker = null;
break;
default:
break;
@@ -308,55 +321,24 @@ public class SwipeDetector {
private boolean reportDragging() {
if (mDisplacement != mLastDisplacement) {
if (DBG) {
Log.d(TAG, String.format("onDrag disp=%.1f, velocity=%.1f",
mDisplacement, mVelocity));
Log.d(TAG, String.format("onDrag disp=%.1f", mDisplacement));
}
mLastDisplacement = mDisplacement;
return mListener.onDrag(mDisplacement - mSubtractDisplacement, mVelocity);
return mListener.onDrag(mDisplacement - mSubtractDisplacement);
}
return true;
}
private void reportDragEnd() {
mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
float velocity = mDir.getVelocity(mVelocityTracker) / 1000;
if (DBG) {
Log.d(TAG, String.format("onScrollEnd disp=%.1f, velocity=%.1f",
mDisplacement, mVelocity));
mDisplacement, velocity));
}
mListener.onDragEnd(mVelocity, Math.abs(mVelocity) > RELEASE_VELOCITY_PX_MS);
}
/**
* Computes the damped velocity.
*/
public float computeVelocity(float delta, long currentMillis) {
long previousMillis = mCurrentMillis;
mCurrentMillis = currentMillis;
float deltaTimeMillis = mCurrentMillis - previousMillis;
float velocity = (deltaTimeMillis > 0) ? (delta / deltaTimeMillis) : 0;
if (Math.abs(mVelocity) < 0.001f) {
mVelocity = velocity;
} else {
float alpha = computeDampeningFactor(deltaTimeMillis);
mVelocity = interpolate(mVelocity, velocity, alpha);
}
return mVelocity;
}
/**
* Returns a time-dependent dampening factor using delta time.
*/
private static float computeDampeningFactor(float deltaTime) {
return deltaTime / (SCROLL_VELOCITY_DAMPENING_RC + deltaTime);
}
/**
* Returns the linear interpolation between two values
*/
public static float interpolate(float from, float to, float alpha) {
return (1.0f - alpha) * from + alpha * to;
mListener.onDragEnd(velocity, Math.abs(velocity) > RELEASE_VELOCITY_PX_MS);
}
public static long calculateDuration(float velocity, float progressNeeded) {
@@ -19,7 +19,6 @@ import android.graphics.PointF;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.util.SparseLongArray;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
@@ -37,13 +36,15 @@ public class TouchEventTranslator {
private class DownState {
long timeStamp;
float downX;
float downY;
public DownState(long timeStamp, float downY) {
public DownState(long timeStamp, float downX, float downY) {
this.timeStamp = timeStamp;
this.downX = downX;
this.downY = downY;
}
};
private final DownState ZERO = new DownState(0, 0f);
private final DownState ZERO = new DownState(0, 0f, 0f);
private final Consumer<MotionEvent> mListener;
@@ -65,12 +66,16 @@ public class TouchEventTranslator {
mFingers.clear();
}
public float getDownX() {
return mDownEvents.get(0).downX;
}
public float getDownY() {
return mDownEvents.get(0).downY;
}
public void setDownParameters(int idx, MotionEvent e) {
DownState ev = new DownState(e.getEventTime(), e.getY(idx));
DownState ev = new DownState(e.getEventTime(), e.getX(idx), e.getY(idx));
mDownEvents.append(idx, ev);
}
@@ -128,7 +128,7 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
public void onDragStart(boolean start) { }
@Override
public boolean onDrag(float displacement, float velocity) {
public boolean onDrag(float displacement) {
float range = mContent.getHeight();
displacement = Utilities.boundToRange(displacement, 0, range);
setTranslationShift(displacement / range);
@@ -175,6 +175,7 @@ public class RecyclerViewFastScroller extends View {
if (mThumbOffsetY == y) {
return;
}
updatePopupY((int) y);
mThumbOffsetY = y;
invalidate();
}
@@ -281,7 +282,6 @@ public class RecyclerViewFastScroller extends View {
mPopupView.setText(sectionName);
}
animatePopupVisibility(!sectionName.isEmpty());
updatePopupY(lastY);
mLastTouchY = boundedY;
setThumbOffsetY((int) mLastTouchY);
}
@@ -299,11 +299,14 @@ public class RecyclerViewFastScroller extends View {
canvas.translate(0, mThumbOffsetY);
halfW += mThumbPadding;
float r = mWidth + mThumbPadding + mThumbPadding;
float r = getScrollThumbRadius();
canvas.drawRoundRect(-halfW, 0, halfW, mThumbHeight, r, r, mThumbPaint);
canvas.restoreToCount(saveCount);
}
private float getScrollThumbRadius() {
return mWidth + mThumbPadding + mThumbPadding;
}
/**
* Animates the width of the scrollbar.
@@ -352,11 +355,15 @@ public class RecyclerViewFastScroller extends View {
}
private void updatePopupY(int lastTouchY) {
if (!mPopupVisible) {
return;
}
int height = mPopupView.getHeight();
float top = lastTouchY - (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * height)
+ mRv.getScrollBarTop();
top = Utilities.boundToRange(top,
mMaxWidth, mRv.getScrollbarTrackHeight() - mMaxWidth - height);
// Aligns the rounded corner of the pop up with the top of the thumb.
float top = mRv.getScrollBarTop() + lastTouchY + (getScrollThumbRadius() / 2f)
- (height / 2f);
top = Utilities.boundToRange(top, 0,
getTop() + mRv.getScrollBarTop() + mRv.getScrollbarTrackHeight() - height);
mPopupView.setTranslationY(top);
}
@@ -34,7 +34,7 @@ import com.android.launcher3.R;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.LivePreviewWidgetCell;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.icons.LauncherIcons;
/**
* Extension of {@link DragPreviewProvider} with logic specific to pending widgets/shortcuts
@@ -45,7 +45,7 @@ import android.util.Log;
import android.util.Pair;
import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.ColorExtractor;
import com.android.launcher3.icons.ColorExtractor;
import java.io.IOException;
import java.util.ArrayList;
@@ -11,7 +11,6 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.pm.LauncherActivityInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
@@ -24,6 +23,7 @@ import androidx.test.rule.provider.ProviderTestRule;
import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppFilter;
import com.android.launcher3.AppInfo;
import com.android.launcher3.icons.CachingLogic;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
@@ -32,7 +32,7 @@ import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherProvider;
import com.android.launcher3.graphics.BitmapInfo;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.Provider;
import com.android.launcher3.util.TestLauncherProvider;
@@ -203,10 +203,11 @@ public class BaseModelUpdateTaskTestCase {
}
@Override
protected CacheEntry cacheLocked(
protected <T> CacheEntry cacheLocked(
@NonNull ComponentName componentName,
@NonNull Provider<LauncherActivityInfo> infoProvider,
UserHandle user, boolean usePackageIcon, boolean useLowResIcon) {
UserHandle user, @NonNull Provider<T> infoProvider,
@NonNull CachingLogic<T> cachingLogic,
boolean usePackageIcon, boolean useLowResIcon) {
CacheEntry entry = mCache.get(new ComponentKey(componentName, user));
if (entry == null) {
entry = new CacheEntry();
@@ -17,7 +17,7 @@ import com.android.launcher3.LauncherAppState;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.graphics.BitmapInfo;
import com.android.launcher3.icons.BitmapInfo;
import org.junit.Before;
import org.junit.Test;
@@ -19,7 +19,6 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import com.android.launcher3.testcomponent.TouchEventGenerator;
@@ -32,6 +31,7 @@ import org.mockito.MockitoAnnotations;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyFloat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -51,25 +51,28 @@ public class SwipeDetectorTest {
@Mock
private SwipeDetector.Listener mMockListener;
@Mock
private ViewConfiguration mMockConfig;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mGenerator = new TouchEventGenerator(new TouchEventGenerator.Listener() {
@Override
public void onTouchEvent(MotionEvent event) {
mDetector.onTouchEvent(event);
}
});
mGenerator = new TouchEventGenerator((ev) -> mDetector.onTouchEvent(ev));
ViewConfiguration orgConfig = ViewConfiguration
.get(InstrumentationRegistry.getTargetContext());
doReturn(orgConfig.getScaledMaximumFlingVelocity()).when(mMockConfig)
.getScaledMaximumFlingVelocity();
mDetector = new SwipeDetector(mTouchSlop, mMockListener, SwipeDetector.VERTICAL);
mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL);
mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false);
mTouchSlop = ViewConfiguration.get(InstrumentationRegistry.getTargetContext())
.getScaledTouchSlop();
mTouchSlop = orgConfig.getScaledTouchSlop();
doReturn(mTouchSlop).when(mMockConfig).getScaledTouchSlop();
L("mTouchSlop=", mTouchSlop);
}
@Test
public void testDragStart_vertical() throws Exception {
public void testDragStart_vertical() {
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100, 100 + mTouchSlop);
// TODO: actually calculate the following parameters and do exact value checks.
@@ -77,7 +80,7 @@ public class SwipeDetectorTest {
}
@Test
public void testDragStart_failed() throws Exception {
public void testDragStart_failed() {
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100 + mTouchSlop, 100);
// TODO: actually calculate the following parameters and do exact value checks.
@@ -85,8 +88,8 @@ public class SwipeDetectorTest {
}
@Test
public void testDragStart_horizontal() throws Exception {
mDetector = new SwipeDetector(mTouchSlop, mMockListener, SwipeDetector.HORIZONTAL);
public void testDragStart_horizontal() {
mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL);
mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false);
mGenerator.put(0, 100, 100);
@@ -96,15 +99,15 @@ public class SwipeDetectorTest {
}
@Test
public void testDrag() throws Exception {
public void testDrag() {
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100, 100 + mTouchSlop);
// TODO: actually calculate the following parameters and do exact value checks.
verify(mMockListener).onDrag(anyFloat(), anyFloat());
verify(mMockListener).onDrag(anyFloat());
}
@Test
public void testDragEnd() throws Exception {
public void testDragEnd() {
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100, 100 + mTouchSlop);
mGenerator.move(0, 100, 100 + mTouchSlop * 2);
@@ -17,6 +17,7 @@ package com.android.launcher3.ui;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import android.app.Instrumentation;
import android.content.BroadcastReceiver;
@@ -74,7 +75,7 @@ public abstract class AbstractLauncherUiTest {
public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5;
public static final long SHORT_UI_TIMEOUT= 300;
public static final long DEFAULT_UI_TIMEOUT = 3000;
public static final long DEFAULT_UI_TIMEOUT = 10000;
public static final long DEFAULT_WORKER_TIMEOUT_SECS = 5;
protected MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
@@ -82,12 +83,17 @@ public abstract class AbstractLauncherUiTest {
protected final LauncherInstrumentation mLauncher;
protected Context mTargetContext;
protected String mTargetPackage;
protected final boolean mIsInLauncherProcess;
private static final String TAG = "AbstractLauncherUiTest";
protected AbstractLauncherUiTest() {
mDevice = UiDevice.getInstance(getInstrumentation());
mLauncher = new LauncherInstrumentation(getInstrumentation());
final Instrumentation instrumentation = getInstrumentation();
mDevice = UiDevice.getInstance(instrumentation);
mLauncher = new LauncherInstrumentation(instrumentation);
mIsInLauncherProcess = instrumentation.getTargetContext().getPackageName().equals(
mDevice.getLauncherPackageName());
}
@Rule
@@ -146,16 +152,20 @@ public abstract class AbstractLauncherUiTest {
* @return the matching object.
*/
protected UiObject2 scrollAndFind(UiObject2 container, BySelector condition) {
do {
container.setGestureMargins(0, 0, 0, 200);
int i = 0;
for (; ; ) {
// findObject can only execute after spring settles.
mDevice.wait(Until.findObject(condition), SHORT_UI_TIMEOUT);
UiObject2 widget = container.findObject(condition);
if (widget != null && widget.getVisibleBounds().intersects(
0, 0, mDevice.getDisplayWidth(), mDevice.getDisplayHeight())) {
0, 0, mDevice.getDisplayWidth(), mDevice.getDisplayHeight() - 200)) {
return widget;
}
} while (container.scroll(Direction.DOWN, 1f));
return container.findObject(condition);
if (++i > 40) fail("Too many attempts");
container.scroll(Direction.DOWN, 1f);
}
}
/**
@@ -261,6 +271,7 @@ public abstract class AbstractLauncherUiTest {
}
protected <T> T getFromLauncher(Function<Launcher, T> f) {
if (!mIsInLauncherProcess) return null;
return getOnUiThread(() -> f.apply(mActivityMonitor.getActivity()));
}
@@ -287,6 +298,7 @@ public abstract class AbstractLauncherUiTest {
// flakiness.
protected boolean waitForLauncherCondition(
Function<Launcher, Boolean> condition, long timeout) {
if (!mIsInLauncherProcess) return true;
return Wait.atMost(() -> getFromLauncher(condition), timeout);
}
@@ -42,7 +42,8 @@ public class AllAppsIconToHomeTest extends AbstractLauncherUiTest {
LauncherActivityInfo settingsApp = getSettingsApp();
clearHomescreen();
mActivityMonitor.startLauncher();
mDevice.pressHome();
mDevice.waitForIdle();
// Open all apps and wait for load complete.
final UiObject2 appsContainer = openAllApps();
@@ -17,6 +17,7 @@ import com.android.launcher3.util.Condition;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.ShellCommandRule;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,12 +32,14 @@ public class ShortcutsToHomeTest extends AbstractLauncherUiTest {
@Rule public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher();
@Test
@Ignore
public void testDragIcon_portrait() throws Throwable {
lockRotation(true);
performTest();
}
@Test
@Ignore
public void testDragIcon_landscape() throws Throwable {
lockRotation(false);
performTest();
@@ -33,6 +33,7 @@ import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.widget.WidgetCell;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,12 +48,14 @@ public class AddWidgetTest extends AbstractLauncherUiTest {
@Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grandWidgetBind();
@Test
@Ignore
public void testDragIcon_portrait() throws Throwable {
lockRotation(true);
performTest();
}
@Test
@Ignore
public void testDragIcon_landscape() throws Throwable {
lockRotation(false);
performTest();
@@ -22,7 +22,6 @@ import static org.mockito.Mockito.verify;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -129,11 +128,10 @@ public class WidgetsListAdapterTest {
if (num <= 0) return result;
MultiHashMap<PackageItemInfo, WidgetItem> newMap = new MultiHashMap();
PackageManager pm = mContext.getPackageManager();
AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(mContext);
for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(null)) {
WidgetItem wi = new WidgetItem(LauncherAppWidgetProviderInfo
.fromProviderInfo(mContext, widgetInfo), pm, mTestProfile);
.fromProviderInfo(mContext, widgetInfo), mTestProfile, mIconCache);
PackageItemInfo pInfo = new PackageItemInfo(wi.componentName.getPackageName());
pInfo.title = pInfo.packageName;