Add optional debug logging to on flag changes

FlagDebugUtils.formatFlagChange() utility to always write the set of
updated flags, with a list of actual changes applied. Examples:

[allow_gesture|device_dozing] +[device_dozing]
[] -[state_started]

Additionally, moved the appendFlag utility to the new FlagDebugUtils

Test: manually verifed the output in logcat
Bug: 261418621
Change-Id: Ie4f2cfcd4b34f0a816db7845e1df4331babed07a
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 84bf02e..832adc7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -32,7 +32,7 @@
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
 import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_KEYGUARD;
 import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_SMALL_SCREEN;
-import static com.android.launcher3.taskbar.Utilities.appendFlag;
+import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
 import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
index 1a54576..2517ff6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
@@ -15,7 +15,7 @@
  */
 package com.android.launcher3.taskbar;
 
-import static com.android.launcher3.taskbar.Utilities.appendFlag;
+import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
 
 import androidx.annotation.IntDef;
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 8c91833..4c131ee 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -21,6 +21,7 @@
 
 import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
+import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
 
 import android.content.ComponentCallbacks;
 import android.content.Context;
@@ -32,6 +33,7 @@
 import android.os.Handler;
 import android.os.SystemProperties;
 import android.provider.Settings;
+import android.util.Log;
 import android.view.Display;
 
 import androidx.annotation.NonNull;
@@ -51,6 +53,7 @@
 import com.android.quickstep.RecentsActivity;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TouchInteractionService;
+import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
 
@@ -60,6 +63,8 @@
  * Class to manage taskbar lifecycle
  */
 public class TaskbarManager {
+    private static final String TAG = "TaskbarManager";
+    private static final boolean DEBUG = false;
 
     public static final boolean FLAG_HIDE_NAVBAR_WINDOW =
             SystemProperties.getBoolean("persist.wm.debug.hide_navbar_window", false);
@@ -320,6 +325,10 @@
     }
 
     public void onSystemUiFlagsChanged(int systemUiStateFlags) {
+        if (DEBUG) {
+            Log.d(TAG, "SysUI flags changed: " + formatFlagChange(systemUiStateFlags,
+                    mSharedState.sysuiStateFlags, QuickStepContract::getSystemUiStateString));
+        }
         mSharedState.sysuiStateFlags = systemUiStateFlags;
         if (mTaskbarActivityContext != null) {
             mTaskbarActivityContext.updateSysuiStateFlags(systemUiStateFlags, false /* fromInit */);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index e0acd3e..cbc1672 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -27,7 +27,8 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_HIDE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_SHOW;
-import static com.android.launcher3.taskbar.Utilities.appendFlag;
+import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
+import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
@@ -75,6 +76,8 @@
  * create a cohesive animation between stashed/unstashed states.
  */
 public class TaskbarStashController implements TaskbarControllers.LoggableTaskbarController {
+    private static final String TAG = TaskbarStashController.class.getSimpleName();
+    private static final boolean DEBUG = false;
 
     public static final int FLAG_IN_APP = 1 << 0;
     public static final int FLAG_STASHED_IN_APP_MANUAL = 1 << 1; // long press, persisted
@@ -1095,12 +1098,22 @@
          */
         @Nullable
         public Animator createSetStateAnimator(int flags, long duration) {
+            boolean isStashed = mStashCondition.test(flags);
+
+            if (DEBUG) {
+                String stateString = formatFlagChange(flags, mPrevFlags,
+                            TaskbarStashController::getStateString);
+                Log.d(TAG, "createSetStateAnimator: flags: " + stateString
+                        + ", duration: " + duration
+                        + ", isStashed: " + isStashed
+                        + ", mIsStashed: " + mIsStashed);
+            }
+
             int changedFlags = mPrevFlags ^ flags;
             if (mPrevFlags != flags) {
                 onStateChangeApplied(changedFlags);
                 mPrevFlags = flags;
             }
-            boolean isStashed = mStashCondition.test(flags);
             boolean isHotseatIconOnTopWhenAligned =
                     mControllers.uiController.isHotseatIconOnTopWhenAligned();
             // If an animation has started and mIsHotseatIconOnTopWhenAligned is changed, we need
diff --git a/quickstep/src/com/android/launcher3/taskbar/Utilities.java b/quickstep/src/com/android/launcher3/taskbar/Utilities.java
index a2b3c96..47d6684 100644
--- a/quickstep/src/com/android/launcher3/taskbar/Utilities.java
+++ b/quickstep/src/com/android/launcher3/taskbar/Utilities.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3.taskbar;
 
-import java.util.StringJoiner;
-
 /**
  * Various utilities shared amongst the Taskbar's classes.
  */
@@ -25,12 +23,6 @@
 
     private Utilities() {}
 
-    static void appendFlag(StringJoiner str, int flags, int flag, String flagName) {
-        if ((flags & flag) != 0) {
-            str.add(flagName);
-        }
-    }
-
     /**
      * Sets drag, long-click, and split selection behavior on 1P and 3P launchers with Taskbar
      */
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index e71391f..855983f 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
+import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -26,6 +28,7 @@
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Bundle;
+import android.util.Log;
 import android.window.OnBackInvokedDispatcher;
 
 import androidx.annotation.IntDef;
@@ -43,6 +46,7 @@
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.StringJoiner;
 
 /**
  * Launcher BaseActivity
@@ -50,6 +54,7 @@
 public abstract class BaseActivity extends Activity implements ActivityContext {
 
     private static final String TAG = "BaseActivity";
+    static final boolean DEBUG = false;
 
     public static final int INVISIBLE_BY_STATE_HANDLER = 1 << 0;
     public static final int INVISIBLE_BY_APP_TRANSITIONS = 1 << 1;
@@ -72,7 +77,8 @@
             flag = true,
             value = {INVISIBLE_BY_STATE_HANDLER, INVISIBLE_BY_APP_TRANSITIONS,
                     INVISIBLE_BY_PENDING_FLAGS, PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION})
-    public @interface InvisibilityFlags{}
+    public @interface InvisibilityFlags {
+    }
 
     private final ArrayList<OnDeviceProfileChangeListener> mDPChangeListeners = new ArrayList<>();
     private final ArrayList<MultiWindowModeChangedListener> mMultiWindowModeChangedListeners =
@@ -97,6 +103,7 @@
     /**
      * State flag indicating if the user is active or the activity when to background as a result
      * of user action.
+     *
      * @see #isUserActive()
      */
     public static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 4;
@@ -120,14 +127,28 @@
                     ACTIVITY_STATE_WINDOW_FOCUSED,
                     ACTIVITY_STATE_USER_ACTIVE,
                     ACTIVITY_STATE_TRANSITION_ACTIVE})
-    public @interface ActivityFlags{}
+    public @interface ActivityFlags {
+    }
+
+    /** Returns a human-readable string for the specified {@link ActivityFlags}. */
+    public static String getActivityStateString(@ActivityFlags int flags) {
+        StringJoiner result = new StringJoiner("|");
+        appendFlag(result, flags, ACTIVITY_STATE_STARTED, "state_started");
+        appendFlag(result, flags, ACTIVITY_STATE_RESUMED, "state_resumed");
+        appendFlag(result, flags, ACTIVITY_STATE_DEFERRED_RESUMED, "state_deferred_resumed");
+        appendFlag(result, flags, ACTIVITY_STATE_WINDOW_FOCUSED, "state_window_focused");
+        appendFlag(result, flags, ACTIVITY_STATE_USER_ACTIVE, "state_user_active");
+        appendFlag(result, flags, ACTIVITY_STATE_TRANSITION_ACTIVE, "state_transition_active");
+        return result.toString();
+    }
 
     @ActivityFlags
     private int mActivityFlags;
 
     // When the recents animation is running, the visibility of the Launcher is managed by the
     // animation
-    @InvisibilityFlags private int mForceInvisible;
+    @InvisibilityFlags
+    private int mForceInvisible;
 
     private final ViewCache mViewCache = new ViewCache();
 
@@ -284,17 +305,29 @@
         return mActivityFlags;
     }
 
-    protected void addActivityFlags(int flags) {
-        mActivityFlags |= flags;
-        onActivityFlagsChanged(flags);
+    protected void addActivityFlags(int toAdd) {
+        final int oldFlags = mActivityFlags;
+        mActivityFlags |= toAdd;
+        if (DEBUG) {
+            Log.d(TAG, "Launcher flags updated: " + formatFlagChange(mActivityFlags, oldFlags,
+                    BaseActivity::getActivityStateString));
+        }
+        onActivityFlagsChanged(toAdd);
     }
 
-    protected void removeActivityFlags(int flags) {
-        mActivityFlags &= ~flags;
-        onActivityFlagsChanged(flags);
+    protected void removeActivityFlags(int toRemove) {
+        final int oldFlags = mActivityFlags;
+        mActivityFlags &= ~toRemove;
+        if (DEBUG) {
+            Log.d(TAG, "Launcher flags updated: " + formatFlagChange(mActivityFlags, oldFlags,
+                    BaseActivity::getActivityStateString));
+        }
+
+        onActivityFlagsChanged(toRemove);
     }
 
-    protected void onActivityFlagsChanged(int changeBits) { }
+    protected void onActivityFlagsChanged(int changeBits) {
+    }
 
     public void addMultiWindowModeChangedListener(MultiWindowModeChangedListener listener) {
         mMultiWindowModeChangedListeners.add(listener);
@@ -307,6 +340,7 @@
     /**
      * Used to set the override visibility state, used only to handle the transition home with the
      * recents animation.
+     *
      * @see QuickstepTransitionManager#createWallpaperOpenRunner
      */
     public void addForceInvisibleFlag(@InvisibilityFlags int flag) {
@@ -337,7 +371,7 @@
                 + getDeviceProfile().isVerticalBarLayout());
         writer.println(prefix + "orientation=" + getResources().getConfiguration().orientation);
         writer.println(prefix + "mSystemUiController: " + mSystemUiController);
-        writer.println(prefix + "mActivityFlags: " + mActivityFlags);
+        writer.println(prefix + "mActivityFlags: " + getActivityStateString(mActivityFlags));
         writer.println(prefix + "mForceInvisible: " + mForceInvisible);
     }
 
diff --git a/src/com/android/launcher3/util/FlagDebugUtils.kt b/src/com/android/launcher3/util/FlagDebugUtils.kt
new file mode 100644
index 0000000..f281943
--- /dev/null
+++ b/src/com/android/launcher3/util/FlagDebugUtils.kt
@@ -0,0 +1,37 @@
+package com.android.launcher3.util
+
+import java.util.StringJoiner
+import java.util.function.IntFunction
+
+object FlagDebugUtils {
+
+    /** Appends the [flagName] to [str] when the [flag] is set in [flags]. */
+    @JvmStatic
+    fun appendFlag(str: StringJoiner, flags: Int, flag: Int, flagName: String) {
+        if (flags and flag != 0) {
+            str.add(flagName)
+        }
+    }
+
+    /**
+     * Produces a human-readable representation of the [current] flags, followed by a diff from from
+     * [previous].
+     *
+     * The resulting string is intented for logging and debugging.
+     */
+    @JvmStatic
+    fun formatFlagChange(current: Int, previous: Int, flagSerializer: IntFunction<String>): String {
+        val result = StringJoiner(" ")
+        result.add("[" + flagSerializer.apply(current) + "]")
+        val changed = current xor previous
+        val added = current and changed
+        if (added != 0) {
+            result.add("+[" + flagSerializer.apply(added) + "]")
+        }
+        val removed = previous and changed
+        if (removed != 0) {
+            result.add("-[" + flagSerializer.apply(removed) + "]")
+        }
+        return result.toString()
+    }
+}