Snap for 9014491 from 5166c5da3f to tm-qpr1-release
Change-Id: I73f604154630bb1a387605fafa9971307181dfdd
This commit is contained in:
@@ -25,4 +25,6 @@
|
||||
|
||||
<string name="model_delegate_class" translatable="false">com.android.launcher3.model.QuickstepModelDelegate</string>
|
||||
|
||||
<string name="secondary_display_predictions_class" translatable="false">com.android.launcher3.secondarydisplay.SecondaryDisplayPredictionsImpl</string>
|
||||
|
||||
</resources>
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.secondarydisplay;
|
||||
|
||||
import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.launcher3.appprediction.AppsDividerView;
|
||||
import com.android.launcher3.appprediction.PredictionRowView;
|
||||
import com.android.launcher3.model.BgDataModel;
|
||||
import com.android.launcher3.util.OnboardingPrefs;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
|
||||
/**
|
||||
* Implementation of SecondaryDisplayPredictions.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public final class SecondaryDisplayPredictionsImpl extends SecondaryDisplayPredictions {
|
||||
private final ActivityContext mActivityContext;
|
||||
|
||||
public SecondaryDisplayPredictionsImpl(Context context) {
|
||||
mActivityContext = ActivityContext.lookupContext(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateAppDivider() {
|
||||
OnboardingPrefs<?> onboardingPrefs = mActivityContext.getOnboardingPrefs();
|
||||
mActivityContext.getAppsView().getFloatingHeaderView()
|
||||
.findFixedRowByType(AppsDividerView.class)
|
||||
.setShowAllAppsLabel(!onboardingPrefs.hasReachedMaxCount(ALL_APPS_VISITED_COUNT));
|
||||
onboardingPrefs.incrementEventCount(ALL_APPS_VISITED_COUNT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPredictedApps(BgDataModel.FixedContainerItems item) {
|
||||
mActivityContext.getAppsView().getFloatingHeaderView()
|
||||
.findFixedRowByType(PredictionRowView.class)
|
||||
.setPredictedApps(item.items);
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,10 @@ import static com.android.quickstep.GestureState.STATE_END_TARGET_SET;
|
||||
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_CANCELED;
|
||||
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
|
||||
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION;
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FINISH_RECENTS_ANIMATION;
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION;
|
||||
import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
|
||||
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
|
||||
@@ -100,6 +104,7 @@ import com.android.launcher3.util.WindowBounds;
|
||||
import com.android.quickstep.BaseActivityInterface.AnimationFactory;
|
||||
import com.android.quickstep.GestureState.GestureEndTarget;
|
||||
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
|
||||
import com.android.quickstep.util.ActiveGestureErrorDetector;
|
||||
import com.android.quickstep.util.ActiveGestureLog;
|
||||
import com.android.quickstep.util.ActivityInitListener;
|
||||
import com.android.quickstep.util.AnimatorControllerWithResistance;
|
||||
@@ -320,8 +325,21 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
initStateCallbacks();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ActiveGestureErrorDetector.GestureEvent getTrackedEventForState(int stateFlag) {
|
||||
if (stateFlag == STATE_GESTURE_STARTED) {
|
||||
return ActiveGestureErrorDetector.GestureEvent.STATE_GESTURE_STARTED;
|
||||
} else if (stateFlag == STATE_GESTURE_COMPLETED) {
|
||||
return ActiveGestureErrorDetector.GestureEvent.STATE_GESTURE_COMPLETED;
|
||||
} else if (stateFlag == STATE_GESTURE_CANCELLED) {
|
||||
return ActiveGestureErrorDetector.GestureEvent.STATE_GESTURE_CANCELLED;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void initStateCallbacks() {
|
||||
mStateCallback = new MultiStateCallback(STATE_NAMES.toArray(new String[0]));
|
||||
mStateCallback = new MultiStateCallback(
|
||||
STATE_NAMES.toArray(new String[0]), AbsSwipeUpHandler::getTrackedEventForState);
|
||||
|
||||
mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED,
|
||||
this::onLauncherPresentAndGestureStarted);
|
||||
@@ -800,7 +818,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
public void onRecentsAnimationStart(RecentsAnimationController controller,
|
||||
RecentsAnimationTargets targets) {
|
||||
super.onRecentsAnimationStart(controller, targets);
|
||||
ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length);
|
||||
ActiveGestureLog.INSTANCE.addLog(
|
||||
/* event= */ "startRecentsAnimationCallback",
|
||||
/* extras= */ targets.apps.length,
|
||||
/* gestureEvent= */ START_RECENTS_ANIMATION);
|
||||
mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(mContext, targets);
|
||||
mRecentsAnimationController = controller;
|
||||
mRecentsAnimationTargets = targets;
|
||||
@@ -843,7 +864,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
|
||||
@Override
|
||||
public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
|
||||
ActiveGestureLog.INSTANCE.addLog("cancelRecentsAnimation");
|
||||
ActiveGestureLog.INSTANCE.addLog(
|
||||
/* event= */ "cancelRecentsAnimation",
|
||||
/* gestureEvent= */ CANCEL_RECENTS_ANIMATION);
|
||||
mActivityInitListener.unregister();
|
||||
// Cache the recents animation controller so we can defer its cleanup to after having
|
||||
// properly cleaned up the screenshot without accidentally using it.
|
||||
@@ -1010,7 +1033,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
}
|
||||
break;
|
||||
}
|
||||
ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + endTarget);
|
||||
ActiveGestureLog.INSTANCE.addLog(
|
||||
/* event= */ "onSettledOnEndTarget " + endTarget,
|
||||
/* gestureEvent= */ ON_SETTLED_ON_END_TARGET);
|
||||
}
|
||||
|
||||
/** @return Whether this was the task we were waiting to appear, and thus handled it. */
|
||||
@@ -1027,77 +1052,90 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
return false;
|
||||
}
|
||||
|
||||
private GestureEndTarget calculateEndTarget(PointF velocity, float endVelocity,
|
||||
boolean isFlingY, boolean isCancel) {
|
||||
private GestureEndTarget calculateEndTarget(
|
||||
PointF velocity, float endVelocity, boolean isFlingY, boolean isCancel) {
|
||||
|
||||
if (mGestureState.isHandlingAtomicEvent()) {
|
||||
// Button mode, this is only used to go to recents
|
||||
// Button mode, this is only used to go to recents.
|
||||
return RECENTS;
|
||||
}
|
||||
final GestureEndTarget endTarget;
|
||||
final boolean goingToNewTask;
|
||||
if (mRecentsView != null) {
|
||||
if (!hasTargets()) {
|
||||
// If there are no running tasks, then we can assume that this is a continuation of
|
||||
// the last gesture, but after the recents animation has finished
|
||||
goingToNewTask = true;
|
||||
} else {
|
||||
final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
|
||||
final int taskToLaunch = mRecentsView.getNextPage();
|
||||
goingToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex;
|
||||
}
|
||||
} else {
|
||||
goingToNewTask = false;
|
||||
}
|
||||
final boolean reachedOverviewThreshold = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
|
||||
final boolean isFlingX = Math.abs(velocity.x) > mContext.getResources()
|
||||
.getDimension(R.dimen.quickstep_fling_threshold_speed);
|
||||
if (!isFlingY) {
|
||||
if (isCancel) {
|
||||
endTarget = LAST_TASK;
|
||||
} else if (mDeviceState.isFullyGesturalNavMode()) {
|
||||
if (goingToNewTask && isFlingX) {
|
||||
// Flinging towards new task takes precedence over mIsMotionPaused (which only
|
||||
// checks y-velocity).
|
||||
endTarget = NEW_TASK;
|
||||
} else if (mIsMotionPaused) {
|
||||
endTarget = RECENTS;
|
||||
} else if (goingToNewTask) {
|
||||
endTarget = NEW_TASK;
|
||||
} else {
|
||||
endTarget = !reachedOverviewThreshold ? LAST_TASK : HOME;
|
||||
}
|
||||
} else {
|
||||
endTarget = reachedOverviewThreshold && mGestureStarted
|
||||
? RECENTS
|
||||
: goingToNewTask
|
||||
? NEW_TASK
|
||||
: LAST_TASK;
|
||||
}
|
||||
} else {
|
||||
// If swiping at a diagonal, base end target on the faster velocity.
|
||||
boolean isSwipeUp = endVelocity < 0;
|
||||
boolean willGoToNewTaskOnSwipeUp =
|
||||
goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity);
|
||||
|
||||
if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !willGoToNewTaskOnSwipeUp) {
|
||||
endTarget = HOME;
|
||||
} else if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp) {
|
||||
// If swiping at a diagonal, base end target on the faster velocity.
|
||||
endTarget = NEW_TASK;
|
||||
} else if (isSwipeUp) {
|
||||
endTarget = !reachedOverviewThreshold && willGoToNewTaskOnSwipeUp
|
||||
? NEW_TASK : RECENTS;
|
||||
} else {
|
||||
endTarget = goingToNewTask ? NEW_TASK : LAST_TASK;
|
||||
}
|
||||
GestureEndTarget endTarget;
|
||||
if (isCancel) {
|
||||
endTarget = LAST_TASK;
|
||||
} else if (isFlingY) {
|
||||
endTarget = calculateEndTargetForFlingY(velocity, endVelocity);
|
||||
} else {
|
||||
endTarget = calculateEndTargetForNonFling(velocity);
|
||||
}
|
||||
|
||||
if (mDeviceState.isOverviewDisabled() && (endTarget == RECENTS || endTarget == LAST_TASK)) {
|
||||
if (mDeviceState.isOverviewDisabled() && endTarget == RECENTS) {
|
||||
return LAST_TASK;
|
||||
}
|
||||
|
||||
return endTarget;
|
||||
}
|
||||
|
||||
private GestureEndTarget calculateEndTargetForFlingY(PointF velocity, float endVelocity) {
|
||||
// If swiping at a diagonal, base end target on the faster velocity direction.
|
||||
final boolean willGoToNewTask =
|
||||
isScrollingToNewTask() && Math.abs(velocity.x) > Math.abs(endVelocity);
|
||||
final boolean isSwipeUp = endVelocity < 0;
|
||||
if (!isSwipeUp) {
|
||||
final boolean isCenteredOnNewTask =
|
||||
mRecentsView.getDestinationPage() != mRecentsView.getRunningTaskIndex();
|
||||
return willGoToNewTask || isCenteredOnNewTask ? NEW_TASK : LAST_TASK;
|
||||
}
|
||||
|
||||
if (!mDeviceState.isFullyGesturalNavMode()) {
|
||||
return (!hasReachedOverviewThreshold() && willGoToNewTask) ? NEW_TASK : RECENTS;
|
||||
}
|
||||
return willGoToNewTask ? NEW_TASK : HOME;
|
||||
}
|
||||
|
||||
private GestureEndTarget calculateEndTargetForNonFling(PointF velocity) {
|
||||
final boolean isScrollingToNewTask = isScrollingToNewTask();
|
||||
final boolean reachedOverviewThreshold = hasReachedOverviewThreshold();
|
||||
if (!mDeviceState.isFullyGesturalNavMode()) {
|
||||
return reachedOverviewThreshold && mGestureStarted
|
||||
? RECENTS
|
||||
: (isScrollingToNewTask ? NEW_TASK : LAST_TASK);
|
||||
}
|
||||
|
||||
// Fully gestural mode.
|
||||
final boolean isFlingX = Math.abs(velocity.x) > mContext.getResources()
|
||||
.getDimension(R.dimen.quickstep_fling_threshold_speed);
|
||||
if (isScrollingToNewTask && isFlingX) {
|
||||
// Flinging towards new task takes precedence over mIsMotionPaused (which only
|
||||
// checks y-velocity).
|
||||
return NEW_TASK;
|
||||
} else if (mIsMotionPaused) {
|
||||
return RECENTS;
|
||||
} else if (isScrollingToNewTask) {
|
||||
return NEW_TASK;
|
||||
} else if (reachedOverviewThreshold) {
|
||||
return HOME;
|
||||
}
|
||||
return LAST_TASK;
|
||||
}
|
||||
|
||||
private boolean isScrollingToNewTask() {
|
||||
if (mRecentsView == null) {
|
||||
return false;
|
||||
}
|
||||
if (!hasTargets()) {
|
||||
// If there are no running tasks, then we can assume that this is a continuation of
|
||||
// the last gesture, but after the recents animation has finished.
|
||||
return true;
|
||||
}
|
||||
int runningTaskIndex = mRecentsView.getRunningTaskIndex();
|
||||
return runningTaskIndex >= 0 && mRecentsView.getNextPage() != runningTaskIndex;
|
||||
}
|
||||
|
||||
private boolean hasReachedOverviewThreshold() {
|
||||
return mCurrentShift.value > MIN_PROGRESS_FOR_OVERVIEW;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void handleNormalGestureEnd(float endVelocity, boolean isFling, PointF velocity,
|
||||
boolean isCancel) {
|
||||
@@ -1175,6 +1213,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
duration = Math.max(duration, mRecentsView.getScroller().getDuration());
|
||||
}
|
||||
}
|
||||
} else if (endTarget == LAST_TASK && mRecentsView != null
|
||||
&& mRecentsView.getNextPage() != mRecentsView.getRunningTaskIndex()) {
|
||||
mRecentsView.snapToPage(mRecentsView.getRunningTaskIndex(), Math.toIntExact(duration));
|
||||
}
|
||||
|
||||
// Let RecentsView handle the scrolling to the task, which we launch in startNewTask()
|
||||
@@ -1575,7 +1616,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
private void resumeLastTask() {
|
||||
if (mRecentsAnimationController != null) {
|
||||
mRecentsAnimationController.finish(false /* toRecents */, null);
|
||||
ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
|
||||
ActiveGestureLog.INSTANCE.addLog(
|
||||
/* event= */ "finishRecentsAnimation",
|
||||
/* extras= */ false,
|
||||
/* gestureEvent= */ FINISH_RECENTS_ANIMATION);
|
||||
}
|
||||
doLogGesture(LAST_TASK, null);
|
||||
reset();
|
||||
@@ -1779,7 +1823,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
mRecentsAnimationController.finish(true /* toRecents */,
|
||||
() -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
|
||||
}
|
||||
ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
|
||||
ActiveGestureLog.INSTANCE.addLog(
|
||||
/* event= */ "finishRecentsAnimation",
|
||||
/* extras= */ true,
|
||||
/* gestureEvent= */ FINISH_RECENTS_ANIMATION);
|
||||
}
|
||||
|
||||
private void finishCurrentTransitionToHome() {
|
||||
@@ -1791,7 +1838,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
finishRecentsControllerToHome(
|
||||
() -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
|
||||
}
|
||||
ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
|
||||
ActiveGestureLog.INSTANCE.addLog(
|
||||
/* event= */ "finishRecentsAnimation",
|
||||
/* extras= */ true,
|
||||
/* gestureEvent= */ FINISH_RECENTS_ANIMATION);
|
||||
doLogGesture(HOME, mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView());
|
||||
}
|
||||
|
||||
@@ -1979,7 +2029,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
mRecentsAnimationController.finish(false /* toRecents */,
|
||||
null /* onFinishComplete */);
|
||||
mActivityInterface.onLaunchTaskSuccess();
|
||||
ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
|
||||
ActiveGestureLog.INSTANCE.addLog(
|
||||
/* event= */ "finishRecentsAnimation",
|
||||
/* extras= */ false,
|
||||
/* gestureEvent= */ FINISH_RECENTS_ANIMATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKG
|
||||
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
|
||||
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.TargetApi;
|
||||
@@ -30,6 +31,7 @@ import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.tracing.GestureStateProto;
|
||||
import com.android.launcher3.tracing.SwipeHandlerProto;
|
||||
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
|
||||
import com.android.quickstep.util.ActiveGestureErrorDetector;
|
||||
import com.android.quickstep.util.ActiveGestureLog;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
@@ -153,7 +155,8 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
|
||||
mHomeIntent = componentObserver.getHomeIntent();
|
||||
mOverviewIntent = componentObserver.getOverviewIntent();
|
||||
mActivityInterface = componentObserver.getActivityInterface();
|
||||
mStateCallback = new MultiStateCallback(STATE_NAMES.toArray(new String[0]));
|
||||
mStateCallback = new MultiStateCallback(
|
||||
STATE_NAMES.toArray(new String[0]), GestureState::getTrackedEventForState);
|
||||
mGestureId = gestureId;
|
||||
}
|
||||
|
||||
@@ -175,10 +178,21 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
|
||||
mHomeIntent = new Intent();
|
||||
mOverviewIntent = new Intent();
|
||||
mActivityInterface = null;
|
||||
mStateCallback = new MultiStateCallback(STATE_NAMES.toArray(new String[0]));
|
||||
mStateCallback = new MultiStateCallback(
|
||||
STATE_NAMES.toArray(new String[0]), GestureState::getTrackedEventForState);
|
||||
mGestureId = -1;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ActiveGestureErrorDetector.GestureEvent getTrackedEventForState(int stateFlag) {
|
||||
if (stateFlag == STATE_END_TARGET_ANIMATION_FINISHED) {
|
||||
return ActiveGestureErrorDetector.GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED;
|
||||
} else if (stateFlag == STATE_RECENTS_SCROLLING_FINISHED) {
|
||||
return ActiveGestureErrorDetector.GestureEvent.STATE_RECENTS_SCROLLING_FINISHED;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the gesture state has the provided {@param stateMask} flags set.
|
||||
*/
|
||||
@@ -311,7 +325,9 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
|
||||
public void setEndTarget(GestureEndTarget target, boolean isAtomic) {
|
||||
mEndTarget = target;
|
||||
mStateCallback.setState(STATE_END_TARGET_SET);
|
||||
ActiveGestureLog.INSTANCE.addLog("setEndTarget " + mEndTarget);
|
||||
ActiveGestureLog.INSTANCE.addLog(
|
||||
/* event= */ "setEndTarget " + mEndTarget,
|
||||
/* gestureEvent= */ SET_END_TARGET);
|
||||
if (isAtomic) {
|
||||
mStateCallback.setState(STATE_END_TARGET_ANIMATION_FINISHED);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,12 @@ import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.quickstep.util.ActiveGestureErrorDetector;
|
||||
import com.android.quickstep.util.ActiveGestureLog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
@@ -41,17 +46,29 @@ public class MultiStateCallback {
|
||||
private final SparseArray<ArrayList<Consumer<Boolean>>> mStateChangeListeners =
|
||||
new SparseArray<>();
|
||||
|
||||
@NonNull private final TrackedEventsMapper mTrackedEventsMapper;
|
||||
|
||||
private final String[] mStateNames;
|
||||
|
||||
private int mState = 0;
|
||||
|
||||
public MultiStateCallback(String[] stateNames) {
|
||||
this(stateNames, stateFlag -> null);
|
||||
}
|
||||
|
||||
public MultiStateCallback(
|
||||
String[] stateNames,
|
||||
@NonNull TrackedEventsMapper trackedEventsMapper) {
|
||||
mStateNames = DEBUG_STATES ? stateNames : null;
|
||||
mTrackedEventsMapper = trackedEventsMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the provided state flags to the global state on the UI thread and executes any callbacks
|
||||
* as a result.
|
||||
*
|
||||
* Also tracks the provided gesture events for error detection. Each provided event must be
|
||||
* associated with one provided state flag.
|
||||
*/
|
||||
public void setStateOnUiThread(int stateFlag) {
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
@@ -69,7 +86,9 @@ public class MultiStateCallback {
|
||||
Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding "
|
||||
+ convertToFlagNames(stateFlag) + " to " + convertToFlagNames(mState));
|
||||
}
|
||||
|
||||
if (FeatureFlags.ENABLE_GESTURE_ERROR_DETECTION.get()) {
|
||||
trackGestureEvents(stateFlag);
|
||||
}
|
||||
final int oldState = mState;
|
||||
mState = mState | stateFlag;
|
||||
|
||||
@@ -87,6 +106,20 @@ public class MultiStateCallback {
|
||||
notifyStateChangeListeners(oldState);
|
||||
}
|
||||
|
||||
private void trackGestureEvents(int stateFlags) {
|
||||
for (int index = 0; (stateFlags >> index) != 0; index++) {
|
||||
if ((stateFlags & (1 << index)) == 0) {
|
||||
continue;
|
||||
}
|
||||
ActiveGestureErrorDetector.GestureEvent gestureEvent =
|
||||
mTrackedEventsMapper.getTrackedEventForState(1 << index);
|
||||
if (gestureEvent == null) {
|
||||
continue;
|
||||
}
|
||||
ActiveGestureLog.INSTANCE.trackEvent(gestureEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the provided state flags to the global state and executes any change handlers
|
||||
* as a result.
|
||||
@@ -174,4 +207,7 @@ public class MultiStateCallback {
|
||||
return joiner.toString();
|
||||
}
|
||||
|
||||
public interface TrackedEventsMapper {
|
||||
@Nullable ActiveGestureErrorDetector.GestureEvent getTrackedEventForState(int stateflag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.android.quickstep;
|
||||
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.util.ArraySet;
|
||||
@@ -27,6 +28,7 @@ import androidx.annotation.UiThread;
|
||||
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.quickstep.util.ActiveGestureLog;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
@@ -122,6 +124,9 @@ public class RecentsAnimationCallbacks implements
|
||||
@Override
|
||||
public final void onAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
|
||||
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
|
||||
ActiveGestureLog.INSTANCE.addLog(
|
||||
/* event= */ "onRecentsAnimationCancelled",
|
||||
/* gestureEvent= */ CANCEL_RECENTS_ANIMATION);
|
||||
for (RecentsAnimationListener listener : getListeners()) {
|
||||
listener.onRecentsAnimationCanceled(thumbnailDatas);
|
||||
}
|
||||
@@ -132,6 +137,7 @@ public class RecentsAnimationCallbacks implements
|
||||
@Override
|
||||
public void onTasksAppeared(RemoteAnimationTargetCompat[] apps) {
|
||||
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
|
||||
ActiveGestureLog.INSTANCE.addLog("onTasksAppeared");
|
||||
for (RecentsAnimationListener listener : getListeners()) {
|
||||
listener.onTasksAppeared(apps);
|
||||
}
|
||||
|
||||
@@ -580,12 +580,15 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener {
|
||||
&& ((mSystemUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0);
|
||||
}
|
||||
|
||||
public String getSystemUiStateString() {
|
||||
return QuickStepContract.getSystemUiStateString(mSystemUiStateFlags);
|
||||
}
|
||||
|
||||
public void dump(PrintWriter pw) {
|
||||
pw.println("DeviceState:");
|
||||
pw.println(" canStartSystemGesture=" + canStartSystemGesture());
|
||||
pw.println(" systemUiFlags=" + mSystemUiStateFlags);
|
||||
pw.println(" systemUiFlagsDesc="
|
||||
+ QuickStepContract.getSystemUiStateString(mSystemUiStateFlags));
|
||||
pw.println(" systemUiFlagsDesc=" + getSystemUiStateString());
|
||||
pw.println(" assistantAvailable=" + mAssistantAvailable);
|
||||
pw.println(" assistantDisabled="
|
||||
+ QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.android.quickstep;
|
||||
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
@@ -25,6 +27,8 @@ import android.content.pm.PackageManager;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.pm.UserCache;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
@@ -47,16 +51,32 @@ public final class TaskUtils {
|
||||
* TODO: remove this once we switch to getting the icon and label from IconCache.
|
||||
*/
|
||||
public static CharSequence getTitle(Context context, Task task) {
|
||||
UserHandle user = UserHandle.of(task.key.userId);
|
||||
return getTitle(context, task.key.userId, task.getTopComponent().getPackageName());
|
||||
}
|
||||
|
||||
public static CharSequence getTitle(
|
||||
@NonNull Context context,
|
||||
@UserIdInt @Nullable Integer userId,
|
||||
@Nullable String packageName) {
|
||||
if (userId == null || packageName == null) {
|
||||
if (userId == null) {
|
||||
Log.e(TAG, "Failed to get title; missing userId");
|
||||
}
|
||||
if (packageName == null) {
|
||||
Log.e(TAG, "Failed to get title; missing packageName");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
UserHandle user = UserHandle.of(userId);
|
||||
ApplicationInfo applicationInfo = new PackageManagerHelper(context)
|
||||
.getApplicationInfo(task.getTopComponent().getPackageName(), user, 0);
|
||||
.getApplicationInfo(packageName, user, 0);
|
||||
if (applicationInfo == null) {
|
||||
Log.e(TAG, "Failed to get title for task " + task);
|
||||
Log.e(TAG, "Failed to get title for userId=" + userId + ", packageName=" + packageName);
|
||||
return "";
|
||||
}
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
return packageManager.getUserBadgedLabel(
|
||||
applicationInfo.loadLabel(packageManager), user);
|
||||
applicationInfo.loadLabel(packageManager), user);
|
||||
}
|
||||
|
||||
public static ComponentKey getLaunchComponentKeyForTask(Task.TaskKey taskKey) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
|
||||
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.content.Context;
|
||||
|
||||
@@ -282,5 +283,16 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@UserIdInt
|
||||
@Nullable
|
||||
public Integer getUserId() {
|
||||
return mTopTask == null ? null : mTopTask.userId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getPackageName() {
|
||||
return mTopTask == null ? null : mTopTask.baseActivity.getPackageName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ import static com.android.launcher3.config.FeatureFlags.ASSISTANT_GIVES_LAUNCHER
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.quickstep.GestureState.DEFAULT_STATE;
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN;
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_UP;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION;
|
||||
@@ -650,12 +652,17 @@ public class TouchInteractionService extends Service
|
||||
switch (event.getActionMasked()) {
|
||||
case ACTION_DOWN:
|
||||
case ACTION_UP:
|
||||
ActiveGestureLog.INSTANCE.addLog("onMotionEvent("
|
||||
+ (int) event.getRawX() + ", " + (int) event.getRawY() + ")",
|
||||
event.getActionMasked());
|
||||
ActiveGestureLog.INSTANCE.addLog(
|
||||
/* event= */ "onMotionEvent(" + (int) event.getRawX() + ", "
|
||||
+ (int) event.getRawY() + "): "
|
||||
+ MotionEvent.actionToString(event.getActionMasked()),
|
||||
/* gestureEvent= */ event.getActionMasked() == ACTION_DOWN
|
||||
? MOTION_DOWN
|
||||
: MOTION_UP);
|
||||
break;
|
||||
default:
|
||||
ActiveGestureLog.INSTANCE.addLog("onMotionEvent", event.getActionMasked());
|
||||
ActiveGestureLog.INSTANCE.addLog("onMotionEvent: "
|
||||
+ MotionEvent.actionToString(event.getActionMasked()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -702,15 +709,20 @@ public class TouchInteractionService extends Service
|
||||
public GestureState createGestureState(GestureState previousGestureState) {
|
||||
GestureState gestureState = new GestureState(mOverviewComponentObserver,
|
||||
ActiveGestureLog.INSTANCE.incrementLogId());
|
||||
TopTaskTracker.CachedTaskInfo taskInfo;
|
||||
if (mTaskAnimationManager.isRecentsAnimationRunning()) {
|
||||
gestureState.updateRunningTask(previousGestureState.getRunningTask());
|
||||
taskInfo = previousGestureState.getRunningTask();
|
||||
gestureState.updateRunningTask(taskInfo);
|
||||
gestureState.updateLastStartedTaskId(previousGestureState.getLastStartedTaskId());
|
||||
gestureState.updatePreviouslyAppearedTaskIds(
|
||||
previousGestureState.getPreviouslyAppearedTaskIds());
|
||||
} else {
|
||||
gestureState.updateRunningTask(
|
||||
TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false));
|
||||
taskInfo = TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false);
|
||||
gestureState.updateRunningTask(taskInfo);
|
||||
}
|
||||
// Log initial state for the gesture.
|
||||
ActiveGestureLog.INSTANCE.addLog(
|
||||
"Current SystemUi state flags= " + mDeviceState.getSystemUiStateString());
|
||||
return gestureState;
|
||||
}
|
||||
|
||||
@@ -1190,6 +1202,7 @@ public class TouchInteractionService extends Service
|
||||
private void printAvailableCommands(PrintWriter pw) {
|
||||
pw.println("Available commands:");
|
||||
pw.println(" clear-touch-log: Clears the touch interaction log");
|
||||
pw.println(" print-gesture-log: only prints the ActiveGestureLog dump");
|
||||
}
|
||||
|
||||
private void onCommand(PrintWriter pw, LinkedList<String> args) {
|
||||
@@ -1197,6 +1210,8 @@ public class TouchInteractionService extends Service
|
||||
case "clear-touch-log":
|
||||
ActiveGestureLog.INSTANCE.clear();
|
||||
break;
|
||||
case "print-gesture-log":
|
||||
ActiveGestureLog.INSTANCE.dump("", pw);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import static com.android.launcher3.Utilities.squaredHypot;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
|
||||
import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION;
|
||||
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
@@ -359,7 +360,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
||||
}
|
||||
|
||||
private void notifyGestureStarted(boolean isLikelyToStartNewTask) {
|
||||
ActiveGestureLog.INSTANCE.addLog("startQuickstep");
|
||||
if (mInteractionHandler == null) {
|
||||
return;
|
||||
}
|
||||
@@ -373,7 +373,9 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
||||
}
|
||||
|
||||
private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
|
||||
ActiveGestureLog.INSTANCE.addLog("startRecentsAnimation");
|
||||
ActiveGestureLog.INSTANCE.addLog(
|
||||
/* event= */ "startRecentsAnimation",
|
||||
/* gestureEvent= */ START_RECENTS_ANIMATION);
|
||||
|
||||
mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs);
|
||||
mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
|
||||
|
||||
@@ -35,7 +35,6 @@ import com.android.quickstep.BaseActivityInterface;
|
||||
import com.android.quickstep.GestureState;
|
||||
import com.android.quickstep.InputConsumer;
|
||||
import com.android.quickstep.TaskUtils;
|
||||
import com.android.quickstep.util.ActiveGestureLog;
|
||||
import com.android.systemui.shared.system.InputMonitorCompat;
|
||||
|
||||
/**
|
||||
@@ -91,7 +90,6 @@ public class OverviewInputConsumer<S extends BaseState<S>, T extends StatefulAct
|
||||
if (!mStartingInActivityBounds) {
|
||||
mActivityInterface.closeOverlay();
|
||||
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
|
||||
ActiveGestureLog.INSTANCE.addLog("startQuickstep");
|
||||
}
|
||||
if (mInputMonitor != null) {
|
||||
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
|
||||
|
||||
-2
@@ -32,7 +32,6 @@ import com.android.launcher3.testing.shared.TestProtocol;
|
||||
import com.android.quickstep.GestureState;
|
||||
import com.android.quickstep.InputConsumer;
|
||||
import com.android.quickstep.RecentsAnimationDeviceState;
|
||||
import com.android.quickstep.util.ActiveGestureLog;
|
||||
import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
|
||||
import com.android.systemui.shared.system.InputMonitorCompat;
|
||||
|
||||
@@ -79,7 +78,6 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer,
|
||||
@Override
|
||||
public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
|
||||
startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null);
|
||||
ActiveGestureLog.INSTANCE.addLog("startQuickstep");
|
||||
BaseActivity activity = BaseDraggingActivity.fromContext(mContext);
|
||||
int state = (mGestureState != null && mGestureState.getEndTarget() != null)
|
||||
? mGestureState.getEndTarget().containerType
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.util;
|
||||
|
||||
import android.util.ArraySet;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Utility class for tracking gesture navigation events as they happen, then detecting and reporting
|
||||
* known issues at log dump time.
|
||||
*/
|
||||
public class ActiveGestureErrorDetector {
|
||||
|
||||
public enum GestureEvent {
|
||||
MOTION_DOWN, MOTION_UP, SET_END_TARGET, ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION,
|
||||
FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION, STATE_GESTURE_STARTED,
|
||||
STATE_GESTURE_COMPLETED, STATE_GESTURE_CANCELLED, STATE_END_TARGET_ANIMATION_FINISHED,
|
||||
STATE_RECENTS_SCROLLING_FINISHED
|
||||
}
|
||||
|
||||
private ActiveGestureErrorDetector() {}
|
||||
|
||||
protected static void analyseAndDump(
|
||||
@NonNull String prefix,
|
||||
@NonNull PrintWriter writer,
|
||||
List<ActiveGestureLog.EventLog> eventLogs) {
|
||||
writer.println(prefix + "ActiveGestureErrorDetector:");
|
||||
for (int i = 0; i < eventLogs.size(); i++) {
|
||||
ActiveGestureLog.EventLog eventLog = eventLogs.get(i);
|
||||
if (eventLog == null) {
|
||||
continue;
|
||||
}
|
||||
int gestureId = eventLog.logId;
|
||||
writer.println(prefix + "\tError messages for gesture ID: " + gestureId);
|
||||
|
||||
boolean errorDetected = false;
|
||||
// Use a Set since the order is inherently checked in the loop.
|
||||
final Set<GestureEvent> encounteredEvents = new ArraySet<>();
|
||||
// Set flags and check order of operations.
|
||||
for (ActiveGestureLog.EventEntry eventEntry : eventLog.eventEntries) {
|
||||
GestureEvent gestureEvent = eventEntry.getGestureEvent();
|
||||
if (gestureEvent == null) {
|
||||
continue;
|
||||
}
|
||||
encounteredEvents.add(gestureEvent);
|
||||
switch (gestureEvent) {
|
||||
case MOTION_UP:
|
||||
errorDetected |= printErrorIfTrue(
|
||||
!encounteredEvents.contains(GestureEvent.MOTION_DOWN),
|
||||
/* errorMessage= */ prefix + "\t\tMotion up detected before/without"
|
||||
+ " motion down.",
|
||||
writer);
|
||||
break;
|
||||
case ON_SETTLED_ON_END_TARGET:
|
||||
errorDetected |= printErrorIfTrue(
|
||||
!encounteredEvents.contains(GestureEvent.SET_END_TARGET),
|
||||
/* errorMessage= */ prefix + "\t\tonSettledOnEndTarget called "
|
||||
+ "before/without setEndTarget.",
|
||||
writer);
|
||||
break;
|
||||
case FINISH_RECENTS_ANIMATION:
|
||||
errorDetected |= printErrorIfTrue(
|
||||
!encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION),
|
||||
/* errorMessage= */ prefix + "\t\tfinishRecentsAnimation called "
|
||||
+ "before/without startRecentsAnimation.",
|
||||
writer);
|
||||
break;
|
||||
case CANCEL_RECENTS_ANIMATION:
|
||||
errorDetected |= printErrorIfTrue(
|
||||
!encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION),
|
||||
/* errorMessage= */ prefix + "\t\tcancelRecentsAnimation called "
|
||||
+ "before/without startRecentsAnimation.",
|
||||
writer);
|
||||
break;
|
||||
case STATE_GESTURE_COMPLETED:
|
||||
errorDetected |= printErrorIfTrue(
|
||||
!encounteredEvents.contains(GestureEvent.MOTION_UP),
|
||||
/* errorMessage= */ prefix + "\t\tSTATE_GESTURE_COMPLETED set "
|
||||
+ "before/without motion up.",
|
||||
writer);
|
||||
errorDetected |= printErrorIfTrue(
|
||||
!encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED),
|
||||
/* errorMessage= */ prefix + "\t\tSTATE_GESTURE_COMPLETED set "
|
||||
+ "before/without STATE_GESTURE_STARTED.",
|
||||
writer);
|
||||
break;
|
||||
case STATE_GESTURE_CANCELLED:
|
||||
errorDetected |= printErrorIfTrue(
|
||||
!encounteredEvents.contains(GestureEvent.MOTION_UP),
|
||||
/* errorMessage= */ prefix + "\t\tSTATE_GESTURE_CANCELLED set "
|
||||
+ "before/without motion up.",
|
||||
writer);
|
||||
errorDetected |= printErrorIfTrue(
|
||||
!encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED),
|
||||
/* errorMessage= */ prefix + "\t\tSTATE_GESTURE_CANCELLED set "
|
||||
+ "before/without STATE_GESTURE_STARTED.",
|
||||
writer);
|
||||
break;
|
||||
case MOTION_DOWN:
|
||||
case SET_END_TARGET:
|
||||
case START_RECENTS_ANIMATION:
|
||||
case STATE_GESTURE_STARTED:
|
||||
case STATE_END_TARGET_ANIMATION_FINISHED:
|
||||
case STATE_RECENTS_SCROLLING_FINISHED:
|
||||
default:
|
||||
// No-Op
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all required events were found.
|
||||
errorDetected |= printErrorIfTrue(
|
||||
!encounteredEvents.contains(GestureEvent.MOTION_DOWN),
|
||||
/* errorMessage= */ prefix + "\t\tMotion down never detected.",
|
||||
writer);
|
||||
errorDetected |= printErrorIfTrue(
|
||||
!encounteredEvents.contains(GestureEvent.MOTION_UP),
|
||||
/* errorMessage= */ prefix + "\t\tMotion up never detected.",
|
||||
writer);
|
||||
|
||||
errorDetected |= printErrorIfTrue(
|
||||
/* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
|
||||
&& !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET),
|
||||
/* errorMessage= */ prefix + "\t\tsetEndTarget was called, but "
|
||||
+ "onSettledOnEndTarget wasn't.",
|
||||
writer);
|
||||
errorDetected |= printErrorIfTrue(
|
||||
/* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
|
||||
&& !encounteredEvents.contains(
|
||||
GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED),
|
||||
/* errorMessage= */ prefix + "\t\tsetEndTarget was called, but "
|
||||
+ "STATE_END_TARGET_ANIMATION_FINISHED was never set.",
|
||||
writer);
|
||||
errorDetected |= printErrorIfTrue(
|
||||
/* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
|
||||
&& !encounteredEvents.contains(
|
||||
GestureEvent.STATE_RECENTS_SCROLLING_FINISHED),
|
||||
/* errorMessage= */ prefix + "\t\tsetEndTarget was called, but "
|
||||
+ "STATE_RECENTS_SCROLLING_FINISHED was never set.",
|
||||
writer);
|
||||
errorDetected |= printErrorIfTrue(
|
||||
/* condition= */ encounteredEvents.contains(
|
||||
GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED)
|
||||
&& encounteredEvents.contains(
|
||||
GestureEvent.STATE_RECENTS_SCROLLING_FINISHED)
|
||||
&& !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET),
|
||||
/* errorMessage= */ prefix + "\t\tSTATE_END_TARGET_ANIMATION_FINISHED and "
|
||||
+ "STATE_RECENTS_SCROLLING_FINISHED were set, but onSettledOnEndTarget "
|
||||
+ "wasn't called.",
|
||||
writer);
|
||||
|
||||
errorDetected |= printErrorIfTrue(
|
||||
/* condition= */ encounteredEvents.contains(
|
||||
GestureEvent.START_RECENTS_ANIMATION)
|
||||
&& !encounteredEvents.contains(GestureEvent.FINISH_RECENTS_ANIMATION)
|
||||
&& !encounteredEvents.contains(GestureEvent.CANCEL_RECENTS_ANIMATION),
|
||||
/* errorMessage= */ prefix + "\t\tstartRecentsAnimation was called, but "
|
||||
+ "finishRecentsAnimation and cancelRecentsAnimation weren't.",
|
||||
writer);
|
||||
|
||||
errorDetected |= printErrorIfTrue(
|
||||
/* condition= */ encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED)
|
||||
&& !encounteredEvents.contains(GestureEvent.STATE_GESTURE_COMPLETED)
|
||||
&& !encounteredEvents.contains(GestureEvent.STATE_GESTURE_CANCELLED),
|
||||
/* errorMessage= */ prefix + "\t\tSTATE_GESTURE_STARTED was set, but "
|
||||
+ "STATE_GESTURE_COMPLETED and STATE_GESTURE_CANCELLED weren't.",
|
||||
writer);
|
||||
|
||||
if (!errorDetected) {
|
||||
writer.println(prefix + "\t\tNo errors detected.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean printErrorIfTrue(
|
||||
boolean condition, String errorMessage, PrintWriter writer) {
|
||||
if (!condition) {
|
||||
return false;
|
||||
}
|
||||
writer.println(errorMessage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,9 @@
|
||||
package com.android.quickstep.util;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
@@ -47,6 +50,7 @@ public class ActiveGestureLog {
|
||||
private static final int TYPE_BOOL_TRUE = 3;
|
||||
private static final int TYPE_BOOL_FALSE = 4;
|
||||
private static final int TYPE_INPUT_CONSUMER = 5;
|
||||
private static final int TYPE_GESTURE_EVENT = 6;
|
||||
|
||||
private final EventLog[] logs;
|
||||
private int nextIndex;
|
||||
@@ -57,30 +61,73 @@ public class ActiveGestureLog {
|
||||
this.nextIndex = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Track the given event for error detection.
|
||||
*
|
||||
* @param gestureEvent GestureEvent representing an event during the current gesture's
|
||||
* execution.
|
||||
*/
|
||||
public void trackEvent(@Nullable ActiveGestureErrorDetector.GestureEvent gestureEvent) {
|
||||
addLog(TYPE_GESTURE_EVENT, "", 0, CompoundString.NO_OP, gestureEvent);
|
||||
}
|
||||
|
||||
public void addLog(String event) {
|
||||
addLog(TYPE_ONE_OFF, event, 0, CompoundString.NO_OP);
|
||||
addLog(event, null);
|
||||
}
|
||||
|
||||
public void addLog(String event, int extras) {
|
||||
addLog(TYPE_INTEGER, event, extras, CompoundString.NO_OP);
|
||||
addLog(event, extras, null);
|
||||
}
|
||||
|
||||
public void addLog(String event, boolean extras) {
|
||||
addLog(extras ? TYPE_BOOL_TRUE : TYPE_BOOL_FALSE, event, 0, CompoundString.NO_OP);
|
||||
addLog(event, extras, null);
|
||||
}
|
||||
|
||||
public void addLog(CompoundString compoundString) {
|
||||
addLog(TYPE_INPUT_CONSUMER, "", 0, compoundString);
|
||||
addLog(TYPE_INPUT_CONSUMER, "", 0, compoundString, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a log and track the associated event for error detection.
|
||||
*
|
||||
* @param gestureEvent GestureEvent representing the event being logged.
|
||||
*/
|
||||
public void addLog(
|
||||
String event, @Nullable ActiveGestureErrorDetector.GestureEvent gestureEvent) {
|
||||
addLog(TYPE_ONE_OFF, event, 0, CompoundString.NO_OP, gestureEvent);
|
||||
}
|
||||
|
||||
public void addLog(
|
||||
String event,
|
||||
int extras,
|
||||
@Nullable ActiveGestureErrorDetector.GestureEvent gestureEvent) {
|
||||
addLog(TYPE_INTEGER, event, extras, CompoundString.NO_OP, gestureEvent);
|
||||
}
|
||||
|
||||
public void addLog(
|
||||
String event,
|
||||
boolean extras,
|
||||
@Nullable ActiveGestureErrorDetector.GestureEvent gestureEvent) {
|
||||
addLog(
|
||||
extras ? TYPE_BOOL_TRUE : TYPE_BOOL_FALSE,
|
||||
event,
|
||||
0,
|
||||
CompoundString.NO_OP,
|
||||
gestureEvent);
|
||||
}
|
||||
|
||||
private void addLog(
|
||||
int type, String event, float extras, @NonNull CompoundString compoundString) {
|
||||
int type,
|
||||
String event,
|
||||
float extras,
|
||||
CompoundString compoundString,
|
||||
@Nullable ActiveGestureErrorDetector.GestureEvent gestureEvent) {
|
||||
EventLog lastEventLog = logs[(nextIndex + logs.length - 1) % logs.length];
|
||||
if (lastEventLog == null || mCurrentLogId != lastEventLog.logId) {
|
||||
EventLog eventLog = new EventLog(mCurrentLogId);
|
||||
EventEntry eventEntry = new EventEntry();
|
||||
|
||||
eventEntry.update(type, event, extras, compoundString);
|
||||
eventEntry.update(type, event, extras, compoundString, gestureEvent);
|
||||
eventLog.eventEntries.add(eventEntry);
|
||||
logs[nextIndex] = eventLog;
|
||||
nextIndex = (nextIndex + 1) % logs.length;
|
||||
@@ -95,15 +142,15 @@ public class ActiveGestureLog {
|
||||
? lastEventEntries.get(lastEventEntries.size() - 2) : null;
|
||||
|
||||
// Update the last EventEntry if it's a duplicate
|
||||
if (isEntrySame(lastEntry, type, event, compoundString)
|
||||
&& isEntrySame(secondLastEntry, type, event, compoundString)) {
|
||||
lastEntry.update(type, event, extras, compoundString);
|
||||
if (isEntrySame(lastEntry, type, event, compoundString, gestureEvent)
|
||||
&& isEntrySame(secondLastEntry, type, event, compoundString, gestureEvent)) {
|
||||
lastEntry.update(type, event, extras, compoundString, gestureEvent);
|
||||
secondLastEntry.duplicateCount++;
|
||||
return;
|
||||
}
|
||||
EventEntry eventEntry = new EventEntry();
|
||||
|
||||
eventEntry.update(type, event, extras, compoundString);
|
||||
eventEntry.update(type, event, extras, compoundString, gestureEvent);
|
||||
lastEventEntries.add(eventEntry);
|
||||
}
|
||||
|
||||
@@ -115,12 +162,14 @@ public class ActiveGestureLog {
|
||||
writer.println(prefix + "ActiveGestureLog history:");
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSSZ ", Locale.US);
|
||||
Date date = new Date();
|
||||
ArrayList<EventLog> eventLogs = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < logs.length; i++) {
|
||||
EventLog eventLog = logs[(nextIndex + logs.length - i - 1) % logs.length];
|
||||
if (eventLog == null) {
|
||||
continue;
|
||||
}
|
||||
eventLogs.add(eventLog);
|
||||
writer.println(prefix + "\tLogs for logId: " + eventLog.logId);
|
||||
|
||||
List<EventEntry> eventEntries = eventLog.eventEntries;
|
||||
@@ -146,6 +195,8 @@ public class ActiveGestureLog {
|
||||
case TYPE_INPUT_CONSUMER:
|
||||
msg.append(eventEntry.mCompoundString);
|
||||
break;
|
||||
case TYPE_GESTURE_EVENT:
|
||||
continue;
|
||||
default: // fall out
|
||||
}
|
||||
if (eventEntry.duplicateCount > 0) {
|
||||
@@ -154,6 +205,10 @@ public class ActiveGestureLog {
|
||||
writer.println(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (FeatureFlags.ENABLE_GESTURE_ERROR_DETECTION.get()) {
|
||||
ActiveGestureErrorDetector.analyseAndDump(prefix + '\t', writer, eventLogs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,44 +220,59 @@ public class ActiveGestureLog {
|
||||
}
|
||||
|
||||
private boolean isEntrySame(
|
||||
EventEntry entry, int type, String event, CompoundString compoundString) {
|
||||
EventEntry entry,
|
||||
int type,
|
||||
String event,
|
||||
CompoundString compoundString,
|
||||
ActiveGestureErrorDetector.GestureEvent gestureEvent) {
|
||||
return entry != null
|
||||
&& entry.type == type
|
||||
&& entry.event.equals(event)
|
||||
&& entry.mCompoundString.equals(compoundString);
|
||||
&& entry.mCompoundString.equals(compoundString)
|
||||
&& entry.gestureEvent == gestureEvent;
|
||||
}
|
||||
|
||||
/** A single event entry. */
|
||||
private static class EventEntry {
|
||||
protected static class EventEntry {
|
||||
|
||||
private int type;
|
||||
private String event;
|
||||
private float extras;
|
||||
@NonNull private CompoundString mCompoundString;
|
||||
private ActiveGestureErrorDetector.GestureEvent gestureEvent;
|
||||
private long time;
|
||||
private int duplicateCount;
|
||||
|
||||
public void update(
|
||||
private EventEntry() {}
|
||||
|
||||
@Nullable
|
||||
protected ActiveGestureErrorDetector.GestureEvent getGestureEvent() {
|
||||
return gestureEvent;
|
||||
}
|
||||
|
||||
private void update(
|
||||
int type,
|
||||
String event,
|
||||
float extras,
|
||||
@NonNull CompoundString compoundString) {
|
||||
@NonNull CompoundString compoundString,
|
||||
ActiveGestureErrorDetector.GestureEvent gestureEvent) {
|
||||
this.type = type;
|
||||
this.event = event;
|
||||
this.extras = extras;
|
||||
this.mCompoundString = compoundString;
|
||||
this.gestureEvent = gestureEvent;
|
||||
time = System.currentTimeMillis();
|
||||
duplicateCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** An entire log of entries associated with a single log ID */
|
||||
private static class EventLog {
|
||||
protected static class EventLog {
|
||||
|
||||
private final List<EventEntry> eventEntries = new ArrayList<>();
|
||||
private final int logId;
|
||||
protected final List<EventEntry> eventEntries = new ArrayList<>();
|
||||
protected final int logId;
|
||||
|
||||
protected EventLog(int logId) {
|
||||
private EventLog(int logId) {
|
||||
this.logId = logId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +194,11 @@ public class MotionPauseDetector {
|
||||
}
|
||||
if (mIsPaused != isPaused) {
|
||||
mIsPaused = isPaused;
|
||||
Log.d(TAG, "onMotionPauseChanged, paused=" + mIsPaused + " reason=" + reason);
|
||||
String logString = "onMotionPauseChanged, paused=" + mIsPaused + " reason=" + reason;
|
||||
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
|
||||
Log.d(TAG, logString);
|
||||
}
|
||||
ActiveGestureLog.INSTANCE.addLog(logString);
|
||||
boolean isFirstDetectedPause = !mHasEverBeenPaused && mIsPaused;
|
||||
if (mIsPaused) {
|
||||
AccessibilityManagerCompat.sendPauseDetectedEventToTest(mContext);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/drag_layer"
|
||||
android:clipChildren="false"
|
||||
android:padding="@dimen/dynamic_grid_edge_margin">
|
||||
|
||||
<GridView
|
||||
@@ -52,7 +53,6 @@
|
||||
android:saveEnabled="false"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:background="@drawable/round_rect_primary"
|
||||
android:elevation="2dp"
|
||||
android:visibility="invisible" >
|
||||
|
||||
<include
|
||||
@@ -76,35 +76,8 @@
|
||||
android:paddingTop="@dimen/all_apps_header_top_padding"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip
|
||||
android:id="@+id/tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/all_apps_header_pill_height"
|
||||
android:orientation="horizontal"
|
||||
style="@style/TextHeadline">
|
||||
|
||||
<Button
|
||||
android:id="@+id/tab_personal"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:text="@string/all_apps_personal_tab"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/all_apps_tab_text"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/tab_work"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:text="@string/all_apps_work_tab"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/all_apps_tab_text"
|
||||
android:textSize="14sp" />
|
||||
</com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip>
|
||||
<include layout="@layout/floating_header_content" />
|
||||
<include layout="@layout/all_apps_personal_work_tabs" />
|
||||
</com.android.launcher3.allapps.FloatingHeaderView>
|
||||
|
||||
<com.android.launcher3.allapps.search.AppsSearchContainerLayout
|
||||
|
||||
@@ -85,6 +85,7 @@
|
||||
<string name="launcher_activity_logic_class" translatable="false"></string>
|
||||
<string name="model_delegate_class" translatable="false"></string>
|
||||
<string name="window_manager_proxy_class" translatable="false"></string>
|
||||
<string name="secondary_display_predictions_class" translatable="false"></string>
|
||||
|
||||
<!-- View ID to use for QSB widget -->
|
||||
<item type="id" name="qsb_widget" />
|
||||
|
||||
@@ -68,6 +68,11 @@ public final class FeatureFlags {
|
||||
false,
|
||||
"Log the reason why an Input Consumer was selected for a gesture.");
|
||||
|
||||
public static final BooleanFlag ENABLE_GESTURE_ERROR_DETECTION = getDebugFlag(
|
||||
"ENABLE_GESTURE_ERROR_DETECTION",
|
||||
false,
|
||||
"Analyze gesture events and log detected errors");
|
||||
|
||||
// When enabled the promise icon is visible in all apps while installation an app.
|
||||
public static final BooleanFlag PROMISE_APPS_IN_ALL_APPS = getDebugFlag(
|
||||
"PROMISE_APPS_IN_ALL_APPS", false, "Add promise icon in all-apps");
|
||||
|
||||
@@ -29,7 +29,9 @@ import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherModel;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
|
||||
import com.android.launcher3.model.BgDataModel;
|
||||
import com.android.launcher3.model.StringCache;
|
||||
@@ -39,6 +41,8 @@ import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||
import com.android.launcher3.popup.PopupContainerWithArrow;
|
||||
import com.android.launcher3.popup.PopupDataProvider;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.IntSet;
|
||||
import com.android.launcher3.util.OnboardingPrefs;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
|
||||
@@ -61,6 +65,9 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
|
||||
private boolean mAppDrawerShown = false;
|
||||
|
||||
private StringCache mStringCache;
|
||||
private OnboardingPrefs<?> mOnboardingPrefs;
|
||||
private boolean mBindingItems = false;
|
||||
private SecondaryDisplayPredictions mSecondaryDisplayPredictions;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -69,6 +76,8 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
|
||||
if (getWindow().getDecorView().isAttachedToWindow()) {
|
||||
initUi();
|
||||
}
|
||||
mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
|
||||
mSecondaryDisplayPredictions = SecondaryDisplayPredictions.newInstance(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -204,6 +213,7 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
|
||||
mAppDrawerShown = true;
|
||||
mAppsView.setVisibility(View.VISIBLE);
|
||||
mAppsButton.setVisibility(View.INVISIBLE);
|
||||
mSecondaryDisplayPredictions.updateAppDivider();
|
||||
} else {
|
||||
mAppDrawerShown = false;
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@@ -218,6 +228,26 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
|
||||
animator.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OnboardingPrefs<?> getOnboardingPrefs() {
|
||||
return mOnboardingPrefs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startBinding() {
|
||||
mBindingItems = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBindingItems() {
|
||||
return mBindingItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishBindingItems(IntSet pagesBoundFirst) {
|
||||
mBindingItems = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap) {
|
||||
mPopupDataProvider.setDeepShortcutMap(deepShortcutMap);
|
||||
@@ -229,6 +259,13 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
|
||||
PopupContainerWithArrow.dismissInvalidPopup(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindExtraContainerItems(BgDataModel.FixedContainerItems item) {
|
||||
if (item.containerId == LauncherSettings.Favorites.CONTAINER_PREDICTION) {
|
||||
mSecondaryDisplayPredictions.setPredictedApps(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringCache getStringCache() {
|
||||
return mStringCache;
|
||||
@@ -259,7 +296,7 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
|
||||
Intent intent;
|
||||
if (item instanceof ItemInfoWithIcon
|
||||
&& (((ItemInfoWithIcon) item).runtimeStatusFlags
|
||||
& ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
|
||||
& ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
|
||||
ItemInfoWithIcon appInfo = (ItemInfoWithIcon) item;
|
||||
intent = appInfo.getMarketIntent(this);
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.secondarydisplay;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.model.BgDataModel;
|
||||
import com.android.launcher3.util.ResourceBasedOverride;
|
||||
|
||||
/**
|
||||
* Exposes Quickstep app prediction row APIs to {@link SecondaryDisplayLauncher}.
|
||||
*/
|
||||
public class SecondaryDisplayPredictions implements ResourceBasedOverride {
|
||||
/**
|
||||
* Creates a {@link SecondaryDisplayPredictions} instance.
|
||||
*/
|
||||
static SecondaryDisplayPredictions newInstance(Context context) {
|
||||
return Overrides.getObject(
|
||||
SecondaryDisplayPredictions.class, context,
|
||||
R.string.secondary_display_predictions_class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup/update app divider separating app predictions from All Apps.
|
||||
*/
|
||||
void updateAppDivider() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set predicted apps in top of app drawer.
|
||||
*/
|
||||
public void setPredictedApps(BgDataModel.FixedContainerItems item) {
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -32,7 +32,7 @@ import org.junit.runner.RunWith;
|
||||
*/
|
||||
@MediumTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SDLauncherTest {
|
||||
public class SecondaryDisplayLauncherTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Reference in New Issue
Block a user