Snap for 5048123 from c96f0cc323 to qt-release
Change-Id: If6cb7420c7faab11b0eae57f2ad9659d96c6e26d
This commit is contained in:
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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
+1
-1
@@ -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
-1
@@ -1,4 +1,4 @@
|
||||
package com.android.launcher3.graphics;
|
||||
package com.android.launcher3.icons;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.res.Resources;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
+3
-3
@@ -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;
|
||||
+1
-1
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user