Merge changes I562abc6c,I7a48d960,I0916f969 into tm-qpr-dev am: 7fb96a5305

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/19517498

Change-Id: I21769aa2d541176fb63faceb55043dfccee1a693
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Schneider Victor-tulias
2022-08-25 21:02:03 +00:00
committed by Automerger Merge Worker
5 changed files with 462 additions and 205 deletions

View File

@@ -94,6 +94,7 @@ import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer;
import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer;
import com.android.quickstep.inputconsumers.TaskbarStashInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActiveGestureLog.CompoundString;
import com.android.quickstep.util.ProtoTracer;
import com.android.quickstep.util.ProxyScreenStatusProvider;
import com.android.quickstep.util.SplitScreenBounds;
@@ -126,6 +127,9 @@ import java.util.function.Function;
public class TouchInteractionService extends Service
implements ProtoTraceable<LauncherTraceProto.Builder> {
private static final String SUBSTRING_PREFIX = "; ";
private static final String NEWLINE_PREFIX = "\n\t\t\t-> ";
private static final String TAG = "TouchInteractionService";
private static final boolean BUBBLES_HOME_GESTURE_ENABLED =
@@ -619,8 +623,6 @@ public class TouchInteractionService extends Service
mConsumer.onConsumerAboutToBeSwitched();
mGestureState = newGestureState;
mConsumer = newConsumer(prevGestureState, mGestureState, event);
ActiveGestureLog.INSTANCE.addLog("setInputConsumer: " + mConsumer.getName());
mUncheckedConsumer = mConsumer;
} else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode()
&& mDeviceState.canTriggerAssistantAction(event)) {
@@ -628,8 +630,7 @@ public class TouchInteractionService extends Service
// Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
// should not interrupt it. QuickSwitch assumes that interruption can only
// happen if the next gesture is also quick switch.
mUncheckedConsumer = tryCreateAssistantInputConsumer(
InputConsumer.NO_OP, mGestureState, event);
mUncheckedConsumer = tryCreateAssistantInputConsumer(mGestureState, event);
} else if (mDeviceState.canTriggerOneHandedAction(event)) {
// Consume gesture event for triggering one handed feature.
mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState,
@@ -676,17 +677,31 @@ public class TouchInteractionService extends Service
ProtoTracer.INSTANCE.get(this).scheduleFrameUpdate();
}
private InputConsumer tryCreateAssistantInputConsumer(InputConsumer base,
private InputConsumer tryCreateAssistantInputConsumer(
GestureState gestureState, MotionEvent motionEvent) {
return mDeviceState.isGestureBlockedTask(gestureState.getRunningTask())
? base
: new AssistantInputConsumer(this, gestureState, base, mInputMonitorCompat,
mDeviceState, motionEvent);
return tryCreateAssistantInputConsumer(
InputConsumer.NO_OP, gestureState, motionEvent, CompoundString.NO_OP);
}
private InputConsumer tryCreateAssistantInputConsumer(
InputConsumer base,
GestureState gestureState,
MotionEvent motionEvent,
CompoundString reasonString) {
if (mDeviceState.isGestureBlockedTask(gestureState.getRunningTask())) {
reasonString.append(SUBSTRING_PREFIX)
.append("is gesture-blocked task, using base input consumer");
return base;
} else {
reasonString.append(SUBSTRING_PREFIX).append("using AssistantInputConsumer");
return new AssistantInputConsumer(
this, gestureState, base, mInputMonitorCompat, mDeviceState, motionEvent);
}
}
public GestureState createGestureState(GestureState previousGestureState) {
GestureState gestureState = new GestureState(mOverviewComponentObserver,
ActiveGestureLog.INSTANCE.generateAndSetLogId());
ActiveGestureLog.INSTANCE.incrementLogId());
if (mTaskAnimationManager.isRecentsAnimationRunning()) {
gestureState.updateRunningTask(previousGestureState.getRunningTask());
gestureState.updateLastStartedTaskId(previousGestureState.getLastStartedTaskId());
@@ -699,50 +714,88 @@ public class TouchInteractionService extends Service
return gestureState;
}
private InputConsumer newConsumer(GestureState previousGestureState,
GestureState newGestureState, MotionEvent event) {
private InputConsumer newConsumer(
GestureState previousGestureState, GestureState newGestureState, MotionEvent event) {
AnimatedFloat progressProxy = mSwipeUpProxyProvider.apply(mGestureState);
if (progressProxy != null) {
return new ProgressDelegateInputConsumer(this, mTaskAnimationManager,
mGestureState, mInputMonitorCompat, progressProxy);
InputConsumer consumer = new ProgressDelegateInputConsumer(
this, mTaskAnimationManager, mGestureState, mInputMonitorCompat, progressProxy);
logInputConsumerSelectionReason(consumer, newCompoundString(
"mSwipeUpProxyProvider has been set, using ProgressDelegateInputConsumer"));
return consumer;
}
boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();
if (!mDeviceState.isUserUnlocked()) {
CompoundString reasonString = newCompoundString("device locked");
InputConsumer consumer;
if (canStartSystemGesture) {
// This handles apps launched in direct boot mode (e.g. dialer) as well as apps
// launched while device is locked even after exiting direct boot mode (e.g. camera).
return createDeviceLockedInputConsumer(newGestureState);
consumer = createDeviceLockedInputConsumer(
newGestureState, reasonString.append(SUBSTRING_PREFIX)
.append("can start system gesture"));
} else {
return getDefaultInputConsumer();
consumer = getDefaultInputConsumer(
reasonString.append(SUBSTRING_PREFIX)
.append("cannot start system gesture"));
}
logInputConsumerSelectionReason(consumer, reasonString);
return consumer;
}
CompoundString reasonString;
InputConsumer base;
// When there is an existing recents animation running, bypass systemState check as this is
// a followup gesture and the first gesture started in a valid system state.
InputConsumer base = canStartSystemGesture
|| previousGestureState.isRecentsAnimationRunning()
? newBaseConsumer(previousGestureState, newGestureState, event)
: getDefaultInputConsumer();
if (canStartSystemGesture || previousGestureState.isRecentsAnimationRunning()) {
reasonString = newCompoundString(canStartSystemGesture
? "can start system gesture" : "recents animation was running")
.append(", trying to use base consumer");
base = newBaseConsumer(previousGestureState, newGestureState, event, reasonString);
} else {
reasonString = newCompoundString(
"cannot start system gesture and recents animation was not running")
.append(", trying to use default input consumer");
base = getDefaultInputConsumer(reasonString);
}
if (mDeviceState.isGesturalNavMode()) {
handleOrientationSetup(base);
}
if (mDeviceState.isFullyGesturalNavMode()) {
String reasonPrefix = "device is in gesture navigation mode";
if (mDeviceState.canTriggerAssistantAction(event)) {
base = tryCreateAssistantInputConsumer(base, newGestureState, event);
reasonString.append(NEWLINE_PREFIX)
.append(reasonPrefix)
.append(SUBSTRING_PREFIX)
.append("gesture can trigger the assistant")
.append(", trying to use assistant input consumer");
base = tryCreateAssistantInputConsumer(base, newGestureState, event, reasonString);
}
// If Taskbar is present, we listen for long press to unstash it.
TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
if (tac != null) {
reasonString.append(NEWLINE_PREFIX)
.append(reasonPrefix)
.append(SUBSTRING_PREFIX)
.append("TaskbarActivityContext != null, using TaskbarStashInputConsumer");
base = new TaskbarStashInputConsumer(this, base, mInputMonitorCompat, tac);
}
if (mDeviceState.isBubblesExpanded()) {
reasonString = newCompoundString(reasonPrefix)
.append(SUBSTRING_PREFIX)
.append("bubbles expanded");
if (BUBBLES_HOME_GESTURE_ENABLED) {
reasonString.append(SUBSTRING_PREFIX)
.append("bubbles can handle the home gesture")
.append(", trying to use default input consumer");
// Bubbles can handle home gesture itself.
base = getDefaultInputConsumer();
base = getDefaultInputConsumer(reasonString);
} else {
// If Bubbles is expanded, use the overlay input consumer, which will close
// Bubbles instead of going all the way home when a swipe up is detected.
@@ -750,6 +803,9 @@ public class TouchInteractionService extends Service
// expanded in the back. Make sure swipe up is not passed to bubbles in this
// case.
if (!mDeviceState.isNotificationPanelExpanded()) {
reasonString = newCompoundString(reasonPrefix)
.append(SUBSTRING_PREFIX)
.append("using SysUiOverlayInputConsumer");
base = new SysUiOverlayInputConsumer(
getBaseContext(), mDeviceState, mInputMonitorCompat);
}
@@ -757,6 +813,9 @@ public class TouchInteractionService extends Service
}
if (mDeviceState.isSystemUiDialogShowing()) {
reasonString = newCompoundString(reasonPrefix)
.append(SUBSTRING_PREFIX)
.append("system dialog is showing, using SysUiOverlayInputConsumer");
base = new SysUiOverlayInputConsumer(
getBaseContext(), mDeviceState, mInputMonitorCompat);
}
@@ -764,44 +823,91 @@ public class TouchInteractionService extends Service
if (mDeviceState.isScreenPinningActive()) {
reasonString = newCompoundString(reasonPrefix)
.append(SUBSTRING_PREFIX)
.append("screen pinning is active, using ScreenPinnedInputConsumer");
// Note: we only allow accessibility to wrap this, and it replaces the previous
// base input consumer (which should be NO_OP anyway since topTaskLocked == true).
base = new ScreenPinnedInputConsumer(this, newGestureState);
}
if (mDeviceState.canTriggerOneHandedAction(event)) {
base = new OneHandedModeInputConsumer(this, mDeviceState, base,
mInputMonitorCompat);
reasonString.append(NEWLINE_PREFIX)
.append(reasonPrefix)
.append(SUBSTRING_PREFIX)
.append("gesture can trigger one handed mode")
.append(", using OneHandedModeInputConsumer");
base = new OneHandedModeInputConsumer(
this, mDeviceState, base, mInputMonitorCompat);
}
if (mDeviceState.isAccessibilityMenuAvailable()) {
base = new AccessibilityInputConsumer(this, mDeviceState, base,
mInputMonitorCompat);
reasonString.append(NEWLINE_PREFIX)
.append(reasonPrefix)
.append(SUBSTRING_PREFIX)
.append("accessibility menu is available")
.append(", using AccessibilityInputConsumer");
base = new AccessibilityInputConsumer(
this, mDeviceState, base, mInputMonitorCompat);
}
} else {
String reasonPrefix = "device is not in gesture navigation mode";
if (mDeviceState.isScreenPinningActive()) {
base = getDefaultInputConsumer();
reasonString = newCompoundString(reasonPrefix)
.append(SUBSTRING_PREFIX)
.append("screen pinning is active, trying to use default input consumer");
base = getDefaultInputConsumer(reasonString);
}
if (mDeviceState.canTriggerOneHandedAction(event)) {
base = new OneHandedModeInputConsumer(this, mDeviceState, base,
mInputMonitorCompat);
reasonString.append(NEWLINE_PREFIX)
.append(reasonPrefix)
.append(SUBSTRING_PREFIX)
.append("gesture can trigger one handed mode")
.append(", using OneHandedModeInputConsumer");
base = new OneHandedModeInputConsumer(
this, mDeviceState, base, mInputMonitorCompat);
}
}
logInputConsumerSelectionReason(base, reasonString);
return base;
}
private CompoundString newCompoundString(String substring) {
return new CompoundString(NEWLINE_PREFIX).append(substring);
}
private void logInputConsumerSelectionReason(
InputConsumer consumer, CompoundString reasonString) {
if (!FeatureFlags.ENABLE_INPUT_CONSUMER_REASON_LOGGING.get()) {
ActiveGestureLog.INSTANCE.addLog("setInputConsumer: " + consumer.getName());
return;
}
ActiveGestureLog.INSTANCE.addLog(new CompoundString("setInputConsumer: ")
.append(consumer.getName())
.append(". reason(s):")
.append(reasonString));
}
private void handleOrientationSetup(InputConsumer baseInputConsumer) {
baseInputConsumer.notifyOrientationSetup();
}
private InputConsumer newBaseConsumer(GestureState previousGestureState,
GestureState gestureState, MotionEvent event) {
private InputConsumer newBaseConsumer(
GestureState previousGestureState,
GestureState gestureState,
MotionEvent event,
CompoundString reasonString) {
if (mDeviceState.isKeyguardShowingOccluded()) {
// This handles apps showing over the lockscreen (e.g. camera)
return createDeviceLockedInputConsumer(gestureState);
return createDeviceLockedInputConsumer(
gestureState,
reasonString.append(SUBSTRING_PREFIX)
.append("keyguard is showing occluded")
.append(", trying to use device locked input consumer"));
}
reasonString.append(SUBSTRING_PREFIX).append("keyguard is not showing occluded");
// Use overview input consumer for sharesheets on top of home.
boolean forceOverviewInputConsumer = gestureState.getActivityInterface().isStarted()
&& gestureState.getRunningTask() != null
@@ -815,23 +921,46 @@ public class TouchInteractionService extends Service
forceOverviewInputConsumer = gestureState.getRunningTask().isHomeTask();
}
boolean previousGestureAnimatedToLauncher =
previousGestureState.isRunningAnimationToLauncher();
// with shell-transitions, home is resumed during recents animation, so
// explicitly check against recents animation too.
boolean launcherResumedThroughShellTransition =
gestureState.getActivityInterface().isResumed()
&& !previousGestureState.isRecentsAnimationRunning();
if (ENABLE_QUICKSTEP_LIVE_TILE.get()
&& gestureState.getActivityInterface().isInLiveTileMode()) {
return createOverviewInputConsumer(
previousGestureState, gestureState, event, forceOverviewInputConsumer);
previousGestureState,
gestureState,
event,
forceOverviewInputConsumer,
reasonString.append(SUBSTRING_PREFIX)
.append("is in live tile mode, trying to use overview input consumer"));
} else if (gestureState.getRunningTask() == null) {
return getDefaultInputConsumer();
} else if (previousGestureState.isRunningAnimationToLauncher()
|| (gestureState.getActivityInterface().isResumed()
// with shell-transitions, home is resumed during recents animation, so
// explicitly check against recents animation too.
&& !previousGestureState.isRecentsAnimationRunning())
return getDefaultInputConsumer(reasonString.append(SUBSTRING_PREFIX)
.append("running task == null"));
} else if (previousGestureAnimatedToLauncher
|| launcherResumedThroughShellTransition
|| forceOverviewInputConsumer) {
return createOverviewInputConsumer(
previousGestureState, gestureState, event, forceOverviewInputConsumer);
previousGestureState,
gestureState,
event,
forceOverviewInputConsumer,
reasonString.append(SUBSTRING_PREFIX)
.append(previousGestureAnimatedToLauncher
? "previous gesture animated to launcher"
: (launcherResumedThroughShellTransition
? "launcher resumed through a shell transition"
: "forceOverviewInputConsumer == true"))
.append(", trying to use overview input consumer"));
} else if (mDeviceState.isGestureBlockedTask(gestureState.getRunningTask())) {
return getDefaultInputConsumer();
return getDefaultInputConsumer(reasonString.append(SUBSTRING_PREFIX)
.append("is gesture-blocked task, trying to use default input consumer"));
} else {
reasonString.append(SUBSTRING_PREFIX)
.append("using OtherActivityInputConsumer");
return createOtherActivityInputConsumer(gestureState, event);
}
}
@@ -853,21 +982,34 @@ public class TouchInteractionService extends Service
mInputMonitorCompat, mInputEventReceiver, disableHorizontalSwipe, factory);
}
private InputConsumer createDeviceLockedInputConsumer(GestureState gestureState) {
private InputConsumer createDeviceLockedInputConsumer(
GestureState gestureState, CompoundString reasonString) {
if (mDeviceState.isFullyGesturalNavMode() && gestureState.getRunningTask() != null) {
return new DeviceLockedInputConsumer(this, mDeviceState, mTaskAnimationManager,
gestureState, mInputMonitorCompat);
reasonString.append(SUBSTRING_PREFIX)
.append("device is in gesture nav mode and running task != null")
.append(", using DeviceLockedInputConsumer");
return new DeviceLockedInputConsumer(
this, mDeviceState, mTaskAnimationManager, gestureState, mInputMonitorCompat);
} else {
return getDefaultInputConsumer();
return getDefaultInputConsumer(reasonString
.append(SUBSTRING_PREFIX)
.append(mDeviceState.isFullyGesturalNavMode()
? "running task == null" : "device is not in gesture nav mode")
.append(", trying to use default input consumer"));
}
}
public InputConsumer createOverviewInputConsumer(GestureState previousGestureState,
GestureState gestureState, MotionEvent event,
boolean forceOverviewInputConsumer) {
public InputConsumer createOverviewInputConsumer(
GestureState previousGestureState,
GestureState gestureState,
MotionEvent event,
boolean forceOverviewInputConsumer,
CompoundString reasonString) {
StatefulActivity activity = gestureState.getActivityInterface().getCreatedActivity();
if (activity == null) {
return getDefaultInputConsumer();
return getDefaultInputConsumer(
reasonString.append(SUBSTRING_PREFIX)
.append("activity == null, trying to use default input consumer"));
}
if (activity.getRootView().hasWindowFocus()
@@ -876,9 +1018,13 @@ public class TouchInteractionService extends Service
&& forceOverviewInputConsumer)
|| (ENABLE_QUICKSTEP_LIVE_TILE.get()
&& gestureState.getActivityInterface().isInLiveTileMode())) {
reasonString.append(SUBSTRING_PREFIX)
.append("overview should have focus, using OverviewInputConsumer");
return new OverviewInputConsumer(gestureState, activity, mInputMonitorCompat,
false /* startingInActivityBounds */);
} else {
reasonString.append(SUBSTRING_PREFIX).append(
"overview shouldn't have focus, using OverviewWithoutFocusInputConsumer");
final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
return new OverviewWithoutFocusInputConsumer(activity, mDeviceState, gestureState,
mInputMonitorCompat, disableHorizontalSwipe);
@@ -906,13 +1052,21 @@ public class TouchInteractionService extends Service
}
}
private @NonNull InputConsumer getDefaultInputConsumer() {
return getDefaultInputConsumer(CompoundString.NO_OP);
}
/**
* Returns the {@link ResetGestureInputConsumer} if user is unlocked, else NO_OP.
*/
private @NonNull InputConsumer getDefaultInputConsumer() {
private @NonNull InputConsumer getDefaultInputConsumer(@NonNull CompoundString reasonString) {
if (mResetGestureInputConsumer != null) {
reasonString.append(SUBSTRING_PREFIX).append(
"mResetGestureInputConsumer initialized, using ResetGestureInputConsumer");
return mResetGestureInputConsumer;
} else {
reasonString.append(SUBSTRING_PREFIX).append(
"mResetGestureInputConsumer not initialized, using no-op input consumer");
// mResetGestureInputConsumer isn't initialized until onUserUnlocked(), so reset to
// NO_OP until then (we never want these to be null).
return InputConsumer.NO_OP;

View File

@@ -15,15 +15,23 @@
*/
package com.android.quickstep.util;
import android.content.Context;
import androidx.annotation.NonNull;
import com.android.launcher3.logging.EventLogArray;
import com.android.launcher3.util.MainThreadInitializedObject;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
/**
* A log to keep track of the active gesture.
*/
public class ActiveGestureLog extends EventLogArray {
public class ActiveGestureLog {
private static final int MAX_GESTURES_TRACKED = 10;
public static final ActiveGestureLog INSTANCE = new ActiveGestureLog();
@@ -33,7 +41,238 @@ public class ActiveGestureLog extends EventLogArray {
*/
public static final String INTENT_EXTRA_LOG_TRACE_ID = "INTENT_EXTRA_LOG_TRACE_ID";
private static final int TYPE_ONE_OFF = 0;
private static final int TYPE_FLOAT = 1;
private static final int TYPE_INTEGER = 2;
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 final EventLog[] logs;
private int nextIndex;
private int mCurrentLogId = 100;
private ActiveGestureLog() {
super("touch_interaction_log", 40);
this.logs = new EventLog[MAX_GESTURES_TRACKED];
this.nextIndex = 0;
}
public void addLog(String event) {
addLog(TYPE_ONE_OFF, event, 0, CompoundString.NO_OP);
}
public void addLog(String event, int extras) {
addLog(TYPE_INTEGER, event, extras, CompoundString.NO_OP);
}
public void addLog(String event, boolean extras) {
addLog(extras ? TYPE_BOOL_TRUE : TYPE_BOOL_FALSE, event, 0, CompoundString.NO_OP);
}
public void addLog(CompoundString compoundString) {
addLog(TYPE_INPUT_CONSUMER, "", 0, compoundString);
}
private void addLog(
int type, String event, float extras, @NonNull CompoundString compoundString) {
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);
eventLog.eventEntries.add(eventEntry);
logs[nextIndex] = eventLog;
nextIndex = (nextIndex + 1) % logs.length;
return;
}
// Update the last EventLog
List<EventEntry> lastEventEntries = lastEventLog.eventEntries;
EventEntry lastEntry = lastEventEntries.size() > 0
? lastEventEntries.get(lastEventEntries.size() - 1) : null;
EventEntry secondLastEntry = lastEventEntries.size() > 1
? 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);
secondLastEntry.duplicateCount++;
return;
}
EventEntry eventEntry = new EventEntry();
eventEntry.update(type, event, extras, compoundString);
lastEventEntries.add(eventEntry);
}
public void clear() {
Arrays.fill(logs, null);
}
public void dump(String prefix, PrintWriter writer) {
writer.println(prefix + "ActiveGestureLog history:");
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSSZ ", Locale.US);
Date date = new Date();
for (int i = 0; i < logs.length; i++) {
EventLog eventLog = logs[(nextIndex + logs.length - i - 1) % logs.length];
if (eventLog == null) {
continue;
}
writer.println(prefix + "\tLogs for logId: " + eventLog.logId);
List<EventEntry> eventEntries = eventLog.eventEntries;
for (int j = eventEntries.size() - 1; j >= 0; j--) {
EventEntry eventEntry = eventEntries.get(j);
date.setTime(eventEntry.time);
StringBuilder msg = new StringBuilder(prefix + "\t\t").append(sdf.format(date))
.append(eventEntry.event);
switch (eventEntry.type) {
case TYPE_BOOL_FALSE:
msg.append(": false");
break;
case TYPE_BOOL_TRUE:
msg.append(": true");
break;
case TYPE_FLOAT:
msg.append(": ").append(eventEntry.extras);
break;
case TYPE_INTEGER:
msg.append(": ").append((int) eventEntry.extras);
break;
case TYPE_INPUT_CONSUMER:
msg.append(eventEntry.mCompoundString);
break;
default: // fall out
}
if (eventEntry.duplicateCount > 0) {
msg.append(" & ").append(eventEntry.duplicateCount).append(" similar events");
}
writer.println(msg);
}
}
}
/**
* Increments and returns the current log ID. This should be used every time a new log trace
* is started.
*/
public int incrementLogId() {
return mCurrentLogId++;
}
private boolean isEntrySame(
EventEntry entry, int type, String event, CompoundString compoundString) {
return entry != null
&& entry.type == type
&& entry.event.equals(event)
&& entry.mCompoundString.equals(compoundString);
}
/** A single event entry. */
private static class EventEntry {
private int type;
private String event;
private float extras;
@NonNull private CompoundString mCompoundString;
private long time;
private int duplicateCount;
public void update(
int type,
String event,
float extras,
@NonNull CompoundString compoundString) {
this.type = type;
this.event = event;
this.extras = extras;
this.mCompoundString = compoundString;
time = System.currentTimeMillis();
duplicateCount = 0;
}
}
/** An entire log of entries associated with a single log ID */
private static class EventLog {
private final List<EventEntry> eventEntries = new ArrayList<>();
private final int logId;
protected EventLog(int logId) {
this.logId = logId;
}
}
/** A buildable string stored as an array for memory efficiency. */
public static class CompoundString {
public static final CompoundString NO_OP = new CompoundString();
private final List<String> mSubstrings;
private final boolean mIsNoOp;
private CompoundString() {
this(null);
}
public CompoundString(String substring) {
mIsNoOp = substring == null;
if (mIsNoOp) {
mSubstrings = null;
return;
}
mSubstrings = new ArrayList<>();
mSubstrings.add(substring);
}
public CompoundString append(CompoundString substring) {
if (mIsNoOp) {
return this;
}
mSubstrings.addAll(substring.mSubstrings);
return this;
}
public CompoundString append(String substring) {
if (mIsNoOp) {
return this;
}
mSubstrings.add(substring);
return this;
}
@Override
public String toString() {
if (mIsNoOp) {
return "ERROR: cannot use No-Op compound string";
}
StringBuilder sb = new StringBuilder();
for (String substring : mSubstrings) {
sb.append(substring);
}
return sb.toString();
}
@Override
public int hashCode() {
return Objects.hash(mIsNoOp, mSubstrings);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof CompoundString)) {
return false;
}
CompoundString other = (CompoundString) obj;
return mIsNoOp && other.mIsNoOp && Objects.equals(mSubstrings, other.mSubstrings);
}
}
}

View File

@@ -75,6 +75,7 @@ import android.widget.LinearLayout;
import androidx.annotation.ChecksSdkIntAtLeast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
@@ -925,4 +926,12 @@ public final class Utilities {
}
return options;
}
public static boolean bothNull(@Nullable Object a, @Nullable Object b) {
return a == null && b == null;
}
public static boolean bothNonNull(@Nullable Object a, @Nullable Object b) {
return a != null && b != null;
}
}

View File

@@ -63,6 +63,11 @@ public final class FeatureFlags {
* Declare a new ToggleableFlag below. Give it a unique key (e.g. "QSB_ON_FIRST_SCREEN"),
* and set a default value for the flag. This will be the default value on Debug builds.
*/
public static final BooleanFlag ENABLE_INPUT_CONSUMER_REASON_LOGGING = getDebugFlag(
"ENABLE_INPUT_CONSUMER_REASON_LOGGING",
false,
"Log the reason why an Input Consumer was selected for a gesture.");
// 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");

View File

@@ -1,150 +0,0 @@
/*
* Copyright (C) 2019 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.logging;
import android.util.Log;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Locale;
import java.util.Random;
/**
* A utility class to record and log events. Events are stored in a fixed size array and old logs
* are purged as new events come.
*/
public class EventLogArray {
private static final int TYPE_ONE_OFF = 0;
private static final int TYPE_FLOAT = 1;
private static final int TYPE_INTEGER = 2;
private static final int TYPE_BOOL_TRUE = 3;
private static final int TYPE_BOOL_FALSE = 4;
private final String name;
private final EventEntry[] logs;
private int nextIndex;
private int mLogId;
public EventLogArray(String name, int size) {
this.name = name;
logs = new EventEntry[size];
nextIndex = 0;
}
public void addLog(String event) {
addLog(TYPE_ONE_OFF, event, 0);
}
public void addLog(String event, int extras) {
addLog(TYPE_INTEGER, event, extras);
}
public void addLog(String event, boolean extras) {
addLog(extras ? TYPE_BOOL_TRUE : TYPE_BOOL_FALSE, event, 0);
}
private void addLog(int type, String event, float extras) {
// Merge the logs if its a duplicate
int last = (nextIndex + logs.length - 1) % logs.length;
int secondLast = (nextIndex + logs.length - 2) % logs.length;
if (isEntrySame(logs[last], type, event) && isEntrySame(logs[secondLast], type, event)) {
logs[last].update(type, event, extras, mLogId);
logs[secondLast].duplicateCount++;
return;
}
if (logs[nextIndex] == null) {
logs[nextIndex] = new EventEntry();
}
logs[nextIndex].update(type, event, extras, mLogId);
nextIndex = (nextIndex + 1) % logs.length;
}
public void clear() {
Arrays.setAll(logs, (i) -> null);
}
public void dump(String prefix, PrintWriter writer) {
writer.println(prefix + "EventLog (" + name + ") history:");
SimpleDateFormat sdf = new SimpleDateFormat(" HH:mm:ss.SSSZ ", Locale.US);
Date date = new Date();
for (int i = 0; i < logs.length; i++) {
EventEntry log = logs[(nextIndex + logs.length - i - 1) % logs.length];
if (log == null) {
continue;
}
date.setTime(log.time);
StringBuilder msg = new StringBuilder(prefix).append(sdf.format(date))
.append(log.event);
switch (log.type) {
case TYPE_BOOL_FALSE:
msg.append(": false");
break;
case TYPE_BOOL_TRUE:
msg.append(": true");
break;
case TYPE_FLOAT:
msg.append(": ").append(log.extras);
break;
case TYPE_INTEGER:
msg.append(": ").append((int) log.extras);
break;
default: // fall out
}
if (log.duplicateCount > 0) {
msg.append(" & ").append(log.duplicateCount).append(" similar events");
}
msg.append(" traceId: ").append(log.traceId);
writer.println(msg);
}
}
/** Returns a 3 digit random number between 100-999 */
public int generateAndSetLogId() {
Random r = new Random();
mLogId = r.nextInt(900) + 100;
return mLogId;
}
private boolean isEntrySame(EventEntry entry, int type, String event) {
return entry != null && entry.type == type && entry.event.equals(event);
}
/** A single event entry. */
private static class EventEntry {
private int type;
private String event;
private float extras;
private long time;
private int duplicateCount;
private int traceId;
public void update(int type, String event, float extras, int traceId) {
this.type = type;
this.event = event;
this.extras = extras;
this.traceId = traceId;
time = System.currentTimeMillis();
duplicateCount = 0;
}
}
}