Snap for 12465365 from 84f7220dd5 to 25Q1-release

Change-Id: I87f910069201ac439c1a80cceffe5c9d07bcfb87
This commit is contained in:
Android Build Coastguard Worker
2024-10-07 23:23:14 +00:00
23 changed files with 488 additions and 184 deletions
+7
View File
@@ -434,6 +434,13 @@ flag {
bug: "293182501"
}
flag {
name: "enable_recents_window_proto_log"
namespace: "launcher"
description: "Enables tracking recents window logs in ProtoLog"
bug: "292269949"
}
flag {
name: "coordinate_workspace_scale"
+5 -5
View File
@@ -19,7 +19,7 @@
xmlns:tools="http://schemas.android.com/tools">
<ImageView
android:id="@+id/bubble_flyout_avatar"
android:id="@+id/bubble_flyout_icon"
android:layout_width="50dp"
android:layout_height="36dp"
android:paddingEnd="@dimen/bubblebar_flyout_avatar_message_space"
@@ -30,14 +30,14 @@
tools:src="#ff0000"/>
<TextView
android:id="@+id/bubble_flyout_name"
android:id="@+id/bubble_flyout_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/bubble_flyout_avatar"
app:layout_constraintStart_toEndOf="@id/bubble_flyout_icon"
tools:text="Sender"/>
<TextView
@@ -47,8 +47,8 @@
android:fontFamily="@*android:string/config_bodyFontFamily"
android:maxLines="2"
android:ellipsize="end"
app:layout_constraintTop_toBottomOf="@id/bubble_flyout_name"
app:layout_constraintStart_toEndOf="@id/bubble_flyout_avatar"
app:layout_constraintTop_toBottomOf="@id/bubble_flyout_title"
app:layout_constraintStart_toEndOf="@id/bubble_flyout_icon"
tools:text="This is a message"/>
</merge>
@@ -44,23 +44,31 @@ import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.android.launcher3.R;
import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.RemoteActionShortcut;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.DaggerSingletonObject;
import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.ExecutorUtil;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.dagger.QuickstepBaseAppComponent;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
/**
* Data model for digital wellbeing status of apps.
*/
@LauncherAppSingleton
public final class WellbeingModel implements SafeCloseable {
private static final String TAG = "WellbeingModel";
private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000};
@@ -75,8 +83,8 @@ public final class WellbeingModel implements SafeCloseable {
private static final String EXTRA_PACKAGES = "packages";
private static final String EXTRA_SUCCESS = "success";
public static final MainThreadInitializedObject<WellbeingModel> INSTANCE =
new MainThreadInitializedObject<>(WellbeingModel::new);
public static final DaggerSingletonObject<WellbeingModel> INSTANCE =
new DaggerSingletonObject<>(QuickstepBaseAppComponent::getWellbeingModel);
private final Context mContext;
private final String mWellbeingProviderPkg;
@@ -93,7 +101,9 @@ public final class WellbeingModel implements SafeCloseable {
private boolean mIsInTest;
private WellbeingModel(final Context context) {
@Inject
WellbeingModel(@ApplicationContext final Context context,
DaggerSingletonTracker tracker) {
mContext = context;
mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg);
mWorkerHandler = new Handler(TextUtils.isEmpty(mWellbeingProviderPkg)
@@ -112,6 +122,7 @@ public final class WellbeingModel implements SafeCloseable {
}
};
mWorkerHandler.post(this::initializeInBackground);
ExecutorUtil.executeSyncOnMainOrFail(() -> tracker.addCloseable(this));
}
@WorkerThread
@@ -18,9 +18,4 @@ package com.android.launcher3.taskbar.bubbles.flyout
import android.graphics.drawable.Drawable
data class BubbleBarFlyoutMessage(
val senderAvatar: Drawable?,
val senderName: CharSequence,
val message: CharSequence,
val isGroupChat: Boolean,
)
data class BubbleBarFlyoutMessage(val icon: Drawable?, val title: String, val message: String)
@@ -44,11 +44,11 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly
const val MIN_EXPANSION_PROGRESS_FOR_CONTENT_ALPHA = 0.75f
}
private val sender: TextView by
lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_name) }
private val title: TextView by
lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_title) }
private val avatar: ImageView by
lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_avatar) }
private val icon: ImageView by
lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_icon) }
private val message: TextView by
lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_text) }
@@ -171,8 +171,8 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly
/** Sets the data for the flyout and starts playing the expand animation. */
fun showFromCollapsed(flyoutMessage: BubbleBarFlyoutMessage, expandAnimation: () -> Unit) {
avatar.alpha = 0f
sender.alpha = 0f
icon.alpha = 0f
title.alpha = 0f
message.alpha = 0f
setData(flyoutMessage)
val txToCollapsedPosition =
@@ -202,18 +202,18 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly
private fun setData(flyoutMessage: BubbleBarFlyoutMessage) {
// the avatar is only displayed in group chat messages
if (flyoutMessage.senderAvatar != null && flyoutMessage.isGroupChat) {
avatar.visibility = VISIBLE
avatar.setImageDrawable(flyoutMessage.senderAvatar)
if (flyoutMessage.icon != null) {
icon.visibility = VISIBLE
icon.setImageDrawable(flyoutMessage.icon)
} else {
avatar.visibility = GONE
icon.visibility = GONE
}
val minTextViewWidth: Int
val maxTextViewWidth: Int
if (avatar.visibility == VISIBLE) {
minTextViewWidth = minFlyoutWidth - avatar.width - flyoutPadding * 2
maxTextViewWidth = maxFlyoutWidth - avatar.width - flyoutPadding * 2
if (icon.visibility == VISIBLE) {
minTextViewWidth = minFlyoutWidth - icon.width - flyoutPadding * 2
maxTextViewWidth = maxFlyoutWidth - icon.width - flyoutPadding * 2
} else {
// when there's no avatar, the width of the text view is constant, so we're setting the
// min and max to the same value
@@ -221,13 +221,13 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly
maxTextViewWidth = minTextViewWidth
}
if (flyoutMessage.senderName.isEmpty()) {
sender.visibility = GONE
if (flyoutMessage.title.isEmpty()) {
title.visibility = GONE
} else {
sender.minWidth = minTextViewWidth
sender.maxWidth = maxTextViewWidth
sender.text = flyoutMessage.senderName
sender.visibility = VISIBLE
title.minWidth = minTextViewWidth
title.maxWidth = maxTextViewWidth
title.text = flyoutMessage.title
title.visibility = VISIBLE
}
message.minWidth = minTextViewWidth
@@ -240,17 +240,17 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly
expansionProgress = fraction
updateTranslationForAnimation(message)
updateTranslationForAnimation(sender)
updateTranslationForAnimation(avatar)
updateTranslationForAnimation(title)
updateTranslationForAnimation(icon)
// start fading in the content only after we're past the threshold
val alpha =
((expansionProgress - MIN_EXPANSION_PROGRESS_FOR_CONTENT_ALPHA) /
(1f - MIN_EXPANSION_PROGRESS_FOR_CONTENT_ALPHA))
.coerceIn(0f, 1f)
sender.alpha = alpha
title.alpha = alpha
message.alpha = alpha
avatar.alpha = alpha
icon.alpha = alpha
translationZ =
collapsedElevation + (flyoutElevation - collapsedElevation) * expansionProgress
@@ -368,7 +368,7 @@ class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFly
)
)
backgroundColor = ta.getColor(0, defaultBackgroundColor)
sender.setTextColor(ta.getColor(1, defaultTextColor))
title.setTextColor(ta.getColor(1, defaultTextColor))
message.setTextColor(ta.getColor(2, defaultTextColor))
ta.recycle()
backgroundPaint.color = backgroundColor
@@ -237,6 +237,8 @@ public abstract class AbsSwipeUpHandler<
getNextStateFlag("STATE_SCALED_CONTROLLER_HOME");
private static final int STATE_SCALED_CONTROLLER_RECENTS =
getNextStateFlag("STATE_SCALED_CONTROLLER_RECENTS");
private static final int STATE_PARALLEL_ANIM_FINISHED =
getNextStateFlag("STATE_PARALLEL_ANIM_FINISHED");
protected static final int STATE_HANDLER_INVALIDATED =
getNextStateFlag("STATE_HANDLER_INVALIDATED");
@@ -453,7 +455,8 @@ public abstract class AbsSwipeUpHandler<
mStateCallback.runOnceAtState(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
| STATE_SCALED_CONTROLLER_HOME,
this::finishCurrentTransitionToHome);
mStateCallback.runOnceAtState(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED,
mStateCallback.runOnceAtState(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED
| STATE_PARALLEL_ANIM_FINISHED,
this::reset);
mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
@@ -1544,9 +1547,12 @@ public abstract class AbsSwipeUpHandler<
@Override
public void onAnimationEnd(Animator animation) {
mParallelRunningAnim = null;
mStateCallback.setStateOnUiThread(STATE_PARALLEL_ANIM_FINISHED);
}
});
mParallelRunningAnim.start();
} else {
mStateCallback.setStateOnUiThread(STATE_PARALLEL_ANIM_FINISHED);
}
}
@@ -18,6 +18,7 @@ package com.android.quickstep.dagger;
import com.android.launcher3.dagger.LauncherAppComponent;
import com.android.launcher3.dagger.LauncherBaseAppComponent;
import com.android.launcher3.model.WellbeingModel;
import com.android.quickstep.logging.SettingsChangeLogger;
/**
@@ -30,4 +31,6 @@ import com.android.quickstep.logging.SettingsChangeLogger;
*/
public interface QuickstepBaseAppComponent extends LauncherBaseAppComponent {
SettingsChangeLogger getSettingsChangeLogger();
WellbeingModel getWellbeingModel();
}
@@ -18,11 +18,9 @@ package com.android.quickstep.fallback.window
import android.animation.AnimatorSet
import android.app.ActivityOptions
import android.content.ComponentName
import android.content.Context
import android.content.LocusId
import android.os.Bundle
import android.util.Log
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.MotionEvent
@@ -30,7 +28,6 @@ import android.view.RemoteAnimationAdapter
import android.view.RemoteAnimationTarget
import android.view.SurfaceControl
import android.view.View
import android.view.Window
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
import android.window.RemoteTransition
@@ -63,6 +60,7 @@ import com.android.quickstep.fallback.RecentsState.HOME
import com.android.quickstep.fallback.RecentsState.MODAL_TASK
import com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT
import com.android.quickstep.util.RecentsAtomicAnimationFactory
import com.android.quickstep.util.RecentsWindowProtoLogProxy
import com.android.quickstep.util.SplitSelectStateController
import com.android.quickstep.util.TISBindHelper
import com.android.quickstep.views.OverviewActionsView
@@ -71,9 +69,12 @@ import com.android.quickstep.views.RecentsViewContainer
import java.util.function.Predicate
/**
* Class that will manage RecentsView lifecycle within a window and interface correctly
* where needed. This allows us to run RecentsView in a window where needed.
* todo: b/365776320, b/365777482
* Class that will manage RecentsView lifecycle within a window and interface correctly where
* needed. This allows us to run RecentsView in a window where needed. todo: b/365776320,
* b/365777482
*
* To add new protologs, see [RecentsWindowProtoLogProxy]. To enable logging to logcat, see
* [QuickstepProtoLogGroup.Constants.DEBUG_RECENTS_WINDOW]
*/
class RecentsWindowManager(context: Context) :
RecentsWindowContext(context), RecentsViewContainer, StatefulContainer<RecentsState> {
@@ -81,12 +82,12 @@ class RecentsWindowManager(context: Context) :
companion object {
private const val HOME_APPEAR_DURATION: Long = 250
private const val TAG = "RecentsWindowManager"
private const val DEBUG = false
}
protected var recentsView: FallbackRecentsView<RecentsWindowManager>? = null
private val windowContext: Context = createWindowContext(TYPE_APPLICATION_OVERLAY, null)
private val windowManager: WindowManager = windowContext.getSystemService(WindowManager::class.java)!!
private val windowManager: WindowManager =
windowContext.getSystemService(WindowManager::class.java)!!
private var layoutInflater: LayoutInflater = LayoutInflater.from(this).cloneInContext(this)
private var stateManager: StateManager<RecentsState, RecentsWindowManager> =
StateManager<RecentsState, RecentsWindowManager>(this, RecentsState.BG_LAUNCHER)
@@ -138,11 +139,11 @@ class RecentsWindowManager(context: Context) :
private val mAnimationToHomeFactory =
RemoteAnimationFactory {
_: Int,
appTargets: Array<RemoteAnimationTarget>?,
wallpaperTargets: Array<RemoteAnimationTarget>?,
nonAppTargets: Array<RemoteAnimationTarget>?,
result: LauncherAnimationRunner.AnimationResult? ->
_: Int,
appTargets: Array<RemoteAnimationTarget>?,
wallpaperTargets: Array<RemoteAnimationTarget>?,
nonAppTargets: Array<RemoteAnimationTarget>?,
result: LauncherAnimationRunner.AnimationResult? ->
val controller =
getStateManager().createAnimationToNewWorkspace(BG_LAUNCHER, HOME_APPEAR_DURATION)
controller.dispatchOnStart()
@@ -171,6 +172,7 @@ class RecentsWindowManager(context: Context) :
}
fun cleanup() {
RecentsWindowProtoLogProxy.logCleanup(isShown)
if (isShown) {
windowManager.removeViewImmediate(windowView)
isShown = false
@@ -178,6 +180,7 @@ class RecentsWindowManager(context: Context) :
}
fun startRecentsWindow() {
RecentsWindowProtoLogProxy.logStartRecentsWindow(isShown, windowView == null)
if (isShown) return
if (windowView == null) {
windowView = layoutInflater.inflate(R.layout.fallback_recents_activity, null)
@@ -245,37 +248,24 @@ class RecentsWindowManager(context: Context) :
override fun onStateSetStart(state: RecentsState?) {
super.onStateSetStart(state)
logState(state, "state started:")
RecentsWindowProtoLogProxy.logOnStateSetStart(getStateName(state))
}
override fun onStateSetEnd(state: RecentsState?) {
super.onStateSetEnd(state)
logState(state, "state ended:")
RecentsWindowProtoLogProxy.logOnStateSetEnd(getStateName(state))
}
private fun logState(state: RecentsState?, prefix: String) {
if (!DEBUG) {
return
}
if (state != null) {
when (state) {
DEFAULT -> Log.d(TAG, prefix + "default")
MODAL_TASK -> {
Log.d(TAG, prefix + "MODAL_TASK")
}
BACKGROUND_APP -> {
Log.d(TAG, prefix + "BACKGROUND_APP")
}
HOME -> {
Log.d(TAG, prefix + "HOME")
}
BG_LAUNCHER -> {
Log.d(TAG, prefix + "BG_LAUNCHER")
}
OVERVIEW_SPLIT_SELECT -> {
Log.d(TAG, prefix + "OVERVIEW_SPLIT_SELECT")
}
}
private fun getStateName(state: RecentsState?): String {
return when (state) {
null -> "NULL"
DEFAULT -> "default"
MODAL_TASK -> "MODAL_TASK"
BACKGROUND_APP -> "BACKGROUND_APP"
HOME -> "HOME"
BG_LAUNCHER -> "BG_LAUNCHER"
OVERVIEW_SPLIT_SELECT -> "OVERVIEW_SPLIT_SELECT"
else -> "ordinal=" + state.ordinal
}
}
@@ -26,7 +26,8 @@ import java.util.UUID;
/** Enums used to interface with the ProtoLog API. */
public enum QuickstepProtoLogGroup implements IProtoLogGroup {
ACTIVE_GESTURE_LOG(true, true, false, "ActiveGestureLog");
ACTIVE_GESTURE_LOG(true, true, false, "ActiveGestureLog"),
RECENTS_WINDOW(true, true, Constants.DEBUG_RECENTS_WINDOW, "RecentsWindow");
private final boolean mEnabled;
private volatile boolean mLogToProto;
@@ -95,6 +96,8 @@ public enum QuickstepProtoLogGroup implements IProtoLogGroup {
private static final class Constants {
private static final boolean DEBUG_RECENTS_WINDOW = false;
private static final int LOG_START_ID =
(int) (UUID.nameUUIDFromBytes(QuickstepProtoLogGroup.class.getName().getBytes())
.getMostSignificantBits() % Integer.MAX_VALUE);
@@ -0,0 +1,60 @@
/*
* Copyright (C) 2024 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 static com.android.launcher3.Flags.enableRecentsWindowProtoLog;
import static com.android.quickstep.util.QuickstepProtoLogGroup.RECENTS_WINDOW;
import androidx.annotation.NonNull;
import com.android.internal.protolog.ProtoLog;
import com.android.internal.protolog.common.IProtoLogGroup;
/**
* Proxy class used for Recents Window ProtoLog support.
* <p>
* This file will have all of its static strings in the
* {@link ProtoLog#d(IProtoLogGroup, String, Object...)} calls replaced by dynamic code/strings.
* <p>
* When a new Recents Window log needs to be added to the codebase, add it here under a new unique
* method. Or, if an existing entry needs to be modified, simply update it here.
*/
public class RecentsWindowProtoLogProxy {
public static void logOnStateSetStart(@NonNull String stateName) {
if (!enableRecentsWindowProtoLog()) return;
ProtoLog.d(RECENTS_WINDOW, "onStateSetStart: %s", stateName);
}
public static void logOnStateSetEnd(@NonNull String stateName) {
if (!enableRecentsWindowProtoLog()) return;
ProtoLog.d(RECENTS_WINDOW, "onStateSetEnd: %s", stateName);
}
public static void logStartRecentsWindow(boolean isShown, boolean windowViewIsNull) {
if (!enableRecentsWindowProtoLog()) return;
ProtoLog.d(RECENTS_WINDOW,
"Starting recents window: isShow= %b, windowViewIsNull=%b",
isShown,
windowViewIsNull);
}
public static void logCleanup(boolean isShown) {
if (!enableRecentsWindowProtoLog()) return;
ProtoLog.d(RECENTS_WINDOW, "Cleaning up recents window: isShow= %b", isShown);
}
}
@@ -54,7 +54,19 @@ object FakeBubbleViewFactory {
val flags =
if (suppressNotification) Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION else 0
val bubbleInfo =
BubbleInfo(key, flags, null, null, 0, context.packageName, null, null, false, true)
BubbleInfo(
key,
flags,
null,
null,
0,
context.packageName,
null,
null,
false,
true,
null,
)
val bubbleView = inflater.inflate(R.layout.bubblebar_item_view, parent, false) as BubbleView
val dotPath =
PathParser.createPathFromPathData(
@@ -63,12 +63,7 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
val flyout =
BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = false))
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
senderAvatar = null,
senderName = "sender",
message = "message",
isGroupChat = false,
)
BubbleBarFlyoutMessage(icon = null, title = "sender", message = "message")
) {}
flyout.updateExpansionProgress(1f)
flyout
@@ -82,12 +77,7 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
val flyout =
BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true))
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
senderAvatar = null,
senderName = "sender",
message = "message",
isGroupChat = false,
)
BubbleBarFlyoutMessage(icon = null, title = "sender", message = "message")
) {}
flyout.updateExpansionProgress(1f)
flyout
@@ -102,10 +92,9 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true))
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
senderAvatar = null,
senderName = "sender",
icon = null,
title = "sender",
message = "really, really, really, really, really long message. like really.",
isGroupChat = false,
)
) {}
flyout.updateExpansionProgress(1f)
@@ -121,10 +110,9 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = false))
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
senderAvatar = ColorDrawable(Color.RED),
senderName = "sender",
icon = ColorDrawable(Color.RED),
title = "sender",
message = "message",
isGroupChat = true,
)
) {}
flyout.updateExpansionProgress(1f)
@@ -140,10 +128,9 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true))
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
senderAvatar = ColorDrawable(Color.RED),
senderName = "sender",
icon = ColorDrawable(Color.RED),
title = "sender",
message = "message",
isGroupChat = true,
)
) {}
flyout.updateExpansionProgress(1f)
@@ -159,10 +146,9 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true))
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
senderAvatar = ColorDrawable(Color.RED),
senderName = "sender",
icon = ColorDrawable(Color.RED),
title = "sender",
message = "really, really, really, really, really long message. like really.",
isGroupChat = true,
)
) {}
flyout.updateExpansionProgress(1f)
@@ -178,10 +164,9 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true))
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
senderAvatar = ColorDrawable(Color.RED),
senderName = "sender",
icon = ColorDrawable(Color.RED),
title = "sender",
message = "collapsed on left",
isGroupChat = true,
)
) {}
flyout.updateExpansionProgress(0f)
@@ -197,10 +182,9 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = false))
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
senderAvatar = ColorDrawable(Color.RED),
senderName = "sender",
icon = ColorDrawable(Color.RED),
title = "sender",
message = "collapsed on right",
isGroupChat = true,
)
) {}
flyout.updateExpansionProgress(0f)
@@ -222,10 +206,9 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
)
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
senderAvatar = ColorDrawable(Color.RED),
senderName = "sender",
icon = ColorDrawable(Color.RED),
title = "sender",
message = "expanded 90% on left",
isGroupChat = true,
)
) {}
flyout.updateExpansionProgress(0.9f)
@@ -247,10 +230,9 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
)
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
senderAvatar = ColorDrawable(Color.RED),
senderName = "sender",
icon = ColorDrawable(Color.RED),
title = "sender",
message = "expanded 80% on right",
isGroupChat = true,
)
) {}
flyout.updateExpansionProgress(0.8f)
@@ -35,7 +35,8 @@ import com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATI
import com.android.launcher3.taskbar.TaskbarStashController.TRANSIENT_TASKBAR_STASH_ALPHA_DURATION
import com.android.launcher3.taskbar.TaskbarStashController.TRANSIENT_TASKBAR_STASH_DURATION
import com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_STASH
import com.android.launcher3.taskbar.bubbles.BubbleControllers
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
import com.android.launcher3.taskbar.rules.TaskbarModeRule
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.PINNED
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.THREE_BUTTONS
@@ -52,7 +53,6 @@ import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING
import com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_BAR
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import org.junit.After
import org.junit.Before
import org.junit.Rule
@@ -60,6 +60,7 @@ import org.junit.Test
import org.junit.runner.RunWith
@RunWith(LauncherMultivalentJUnit::class)
@EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
@EmulatedDevices(["pixelTablet2023"])
class TaskbarStashControllerTest {
private val context = TaskbarWindowSandboxContext.create(getInstrumentation().targetContext)
@@ -74,7 +75,8 @@ class TaskbarStashControllerTest {
@InjectController lateinit var stashedHandleViewController: StashedHandleViewController
@InjectController lateinit var dragLayerController: TaskbarDragLayerController
@InjectController lateinit var autohideSuspendController: TaskbarAutohideSuspendController
@InjectController lateinit var bubbleControllers: Optional<BubbleControllers>
@InjectController lateinit var bubbleBarViewController: BubbleBarViewController
@InjectController lateinit var bubbleStashController: BubbleStashController
private val activityContext by taskbarUnitTestRule::activityContext
@@ -420,60 +422,55 @@ class TaskbarStashControllerTest {
}
@Test
@EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
@TaskbarMode(TRANSIENT)
fun testUpdateAndAnimateTransientTaskbar_unstashTaskbarWithBubbles_bubbleBarUnstashes() {
getInstrumentation().runOnMainSync {
bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false)
bubbleControllers.get().bubbleStashController.stashBubbleBarImmediate()
bubbleBarViewController.setHiddenForBubbles(false)
bubbleStashController.stashBubbleBarImmediate()
stashController.updateAndAnimateTransientTaskbar(false, true)
}
assertThat(bubbleControllers.get().bubbleStashController.isStashed).isFalse()
assertThat(bubbleStashController.isStashed).isFalse()
}
@Test
@EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
@TaskbarMode(TRANSIENT)
fun testUpdateAndAnimateTransientTaskbar_unstashTaskbarWithoutBubbles_bubbleBarStashed() {
getInstrumentation().runOnMainSync {
bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false)
bubbleControllers.get().bubbleStashController.stashBubbleBarImmediate()
bubbleBarViewController.setHiddenForBubbles(false)
bubbleStashController.stashBubbleBarImmediate()
stashController.updateAndAnimateTransientTaskbar(false, false)
}
assertThat(bubbleControllers.get().bubbleStashController.isStashed).isTrue()
assertThat(bubbleStashController.isStashed).isTrue()
}
@Test
@EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
@TaskbarMode(TRANSIENT)
fun testUpdateAndAnimateTransientTaskbar_stashTaskbarWithBubbles_bubbleBarStashes() {
getInstrumentation().runOnMainSync {
bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false)
bubbleControllers.get().bubbleStashController.showBubbleBarImmediate()
bubbleBarViewController.setHiddenForBubbles(false)
bubbleStashController.showBubbleBarImmediate()
stashController.updateAndAnimateTransientTaskbar(true, true)
}
assertThat(bubbleControllers.get().bubbleStashController.isStashed).isTrue()
assertThat(bubbleStashController.isStashed).isTrue()
}
@Test
@EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
@TaskbarMode(TRANSIENT)
fun testUpdateAndAnimateTransientTaskbar_stashTaskbarWithoutBubbles_bubbleBarUnstashed() {
getInstrumentation().runOnMainSync {
bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false)
bubbleControllers.get().bubbleStashController.showBubbleBarImmediate()
bubbleBarViewController.setHiddenForBubbles(false)
bubbleStashController.showBubbleBarImmediate()
stashController.updateAndAnimateTransientTaskbar(true, false)
}
assertThat(bubbleControllers.get().bubbleStashController.isStashed).isFalse()
assertThat(bubbleStashController.isStashed).isFalse()
}
@Test
@EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
@TaskbarMode(TRANSIENT)
fun testUpdateAndAnimateTransientTaskbar_bubbleBarExpandedBeforeTimeout_expandedAfterwards() {
getInstrumentation().runOnMainSync {
bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false)
bubbleControllers.get().bubbleBarViewController.isExpanded = true
bubbleBarViewController.setHiddenForBubbles(false)
bubbleBarViewController.isExpanded = true
stashController.updateAndAnimateTransientTaskbar(false)
animatorTestRule.advanceTimeBy(stashController.stashDuration)
}
@@ -483,7 +480,7 @@ class TaskbarStashControllerTest {
stashController.timeoutAlarm.finishAlarm()
animatorTestRule.advanceTimeBy(stashController.stashDuration)
}
assertThat(bubbleControllers.get().bubbleBarViewController.isExpanded).isTrue()
assertThat(bubbleBarViewController.isExpanded).isTrue()
}
@Test
@@ -65,7 +65,19 @@ class BubbleViewTest {
overflowView.setOverflow(BubbleBarOverflow(overflowView), bitmap)
val bubbleInfo =
BubbleInfo("key", 0, null, null, 0, context.packageName, null, null, false, true)
BubbleInfo(
"key",
0,
null,
null,
0,
context.packageName,
null,
null,
false,
true,
null,
)
bubbleView = inflater.inflate(R.layout.bubblebar_item_view, null, false) as BubbleView
bubble =
BubbleBarBubble(bubbleInfo, bubbleView, bitmap, bitmap, Color.WHITE, Path(), "")
@@ -86,7 +86,7 @@ class BubbleBarViewAnimatorTest {
bubbleBarView,
bubbleStashController,
onExpandedNoOp,
animatorScheduler
animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -135,7 +135,7 @@ class BubbleBarViewAnimatorTest {
bubbleBarView,
bubbleStashController,
onExpandedNoOp,
animatorScheduler
animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -183,7 +183,7 @@ class BubbleBarViewAnimatorTest {
bubbleBarView,
bubbleStashController,
onExpandedNoOp,
animatorScheduler
animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -228,7 +228,7 @@ class BubbleBarViewAnimatorTest {
bubbleBarView,
bubbleStashController,
onExpandedNoOp,
animatorScheduler
animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -274,7 +274,7 @@ class BubbleBarViewAnimatorTest {
bubbleBarView,
bubbleStashController,
onExpandedNoOp,
animatorScheduler
animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -311,7 +311,7 @@ class BubbleBarViewAnimatorTest {
bubbleBarView,
bubbleStashController,
onExpanded,
animatorScheduler
animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -355,7 +355,7 @@ class BubbleBarViewAnimatorTest {
bubbleBarView,
bubbleStashController,
onExpanded,
animatorScheduler
animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -405,7 +405,7 @@ class BubbleBarViewAnimatorTest {
bubbleBarView,
bubbleStashController,
onExpanded,
animatorScheduler
animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -454,7 +454,7 @@ class BubbleBarViewAnimatorTest {
bubbleBarView,
bubbleStashController,
onExpandedNoOp,
animatorScheduler
animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -504,7 +504,7 @@ class BubbleBarViewAnimatorTest {
bubbleBarView,
bubbleStashController,
onExpanded,
animatorScheduler
animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -538,7 +538,7 @@ class BubbleBarViewAnimatorTest {
bubbleBarView,
bubbleStashController,
onExpandedNoOp,
animatorScheduler
animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -577,7 +577,7 @@ class BubbleBarViewAnimatorTest {
bubbleBarView,
bubbleStashController,
onExpanded,
animatorScheduler
animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -625,7 +625,7 @@ class BubbleBarViewAnimatorTest {
bubbleBarView,
bubbleStashController,
onExpanded,
animatorScheduler
animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -666,7 +666,7 @@ class BubbleBarViewAnimatorTest {
bubbleBarView,
bubbleStashController,
onExpandedNoOp,
animatorScheduler
animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -713,7 +713,7 @@ class BubbleBarViewAnimatorTest {
bubbleBarView,
bubbleStashController,
onExpanded,
animatorScheduler
animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -760,7 +760,7 @@ class BubbleBarViewAnimatorTest {
bubbleBarView,
bubbleStashController,
onExpanded,
animatorScheduler
animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -818,7 +818,7 @@ class BubbleBarViewAnimatorTest {
bubbleBarView,
bubbleStashController,
onExpanded,
animatorScheduler
animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -870,7 +870,19 @@ class BubbleBarViewAnimatorTest {
bubbleBarView.addView(overflowView)
val bubbleInfo =
BubbleInfo("key", 0, null, null, 0, context.packageName, null, null, false, true)
BubbleInfo(
"key",
0,
null,
null,
0,
context.packageName,
null,
null,
false,
true,
null,
)
bubbleView =
inflater.inflate(R.layout.bubblebar_item_view, bubbleBarView, false) as BubbleView
bubble =
@@ -39,8 +39,7 @@ class BubbleBarFlyoutControllerTest {
private lateinit var flyoutController: BubbleBarFlyoutController
private lateinit var flyoutContainer: FrameLayout
private val context = ApplicationProvider.getApplicationContext<Context>()
private val flyoutMessage =
BubbleBarFlyoutMessage(senderAvatar = null, "sender name", "message", isGroupChat = false)
private val flyoutMessage = BubbleBarFlyoutMessage(icon = null, "sender name", "message")
private var onLeft = true
@Before
@@ -87,7 +86,7 @@ class BubbleBarFlyoutControllerTest {
flyoutController.setUpFlyout(flyoutMessage)
assertThat(flyoutContainer.childCount).isEqualTo(1)
val flyout = flyoutContainer.getChildAt(0)
val sender = flyout.findViewById<TextView>(R.id.bubble_flyout_name)
val sender = flyout.findViewById<TextView>(R.id.bubble_flyout_title)
assertThat(sender.text).isEqualTo("sender name")
val message = flyout.findViewById<TextView>(R.id.bubble_flyout_text)
assertThat(message.text).isEqualTo("message")
@@ -28,9 +28,11 @@ import androidx.test.rule.ServiceTestRule
import com.android.launcher3.LauncherAppState
import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.TaskbarControllers
import com.android.launcher3.taskbar.TaskbarManager
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks
import com.android.launcher3.taskbar.TaskbarViewController
import com.android.launcher3.taskbar.bubbles.BubbleControllers
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
import com.android.launcher3.util.LauncherMultivalentJUnit.Companion.isRunningInRobolectric
@@ -38,6 +40,9 @@ import com.android.launcher3.util.TestUtil
import com.android.quickstep.AllAppsActionManager
import com.android.quickstep.TouchInteractionService
import com.android.quickstep.TouchInteractionService.TISBinder
import java.lang.reflect.Field
import java.lang.reflect.ParameterizedType
import java.util.Optional
import org.junit.Assume.assumeTrue
import org.junit.rules.RuleChain
import org.junit.rules.TestRule
@@ -180,19 +185,38 @@ class TaskbarUnitTestRule(
fun recreateTaskbar() = instrumentation.runOnMainSync { taskbarManager.recreateTaskbar() }
private fun injectControllers() {
val controllers = activityContext.controllers
val controllerFieldsByType = controllers.javaClass.fields.associateBy { it.type }
val bubbleControllerTypes =
BubbleControllers::class.java.fields.map { f ->
if (f.type == Optional::class.java) {
(f.genericType as ParameterizedType).actualTypeArguments[0] as Class<*>
} else {
f.type
}
}
testInstance.javaClass.fields
.filter { it.isAnnotationPresent(InjectController::class.java) }
.forEach {
it.set(
testInstance,
controllerFieldsByType[it.type]?.get(controllers)
?: throw NoSuchElementException("Failed to find controller for ${it.type}"),
)
val controllers: Any =
if (it.type in bubbleControllerTypes) {
activityContext.controllers.bubbleControllers.orElseThrow {
NoSuchElementException("Bubble controllers are not initialized")
}
} else {
activityContext.controllers
}
injectController(it, testInstance, controllers)
}
}
private fun injectController(field: Field, testInstance: Any, controllers: Any) {
val controllerFieldsByType = controllers.javaClass.fields.associateBy { it.type }
field.set(
testInstance,
controllerFieldsByType[field.type]?.get(controllers)
?: throw NoSuchElementException("Failed to find controller for ${field.type}"),
)
}
/**
* Annotates test controller fields to inject the corresponding controllers from the current
* [TaskbarControllers] instance.
@@ -16,18 +16,24 @@
package com.android.launcher3.taskbar.rules
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.TaskbarKeyguardController
import com.android.launcher3.taskbar.TaskbarManager
import com.android.launcher3.taskbar.TaskbarStashController
import com.android.launcher3.taskbar.bubbles.BubbleBarController
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.NavBarKidsMode
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.UserSetupMode
import com.android.launcher3.util.LauncherMultivalentJUnit
import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
import com.android.wm.shell.Flags
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertThrows
import org.junit.Rule
import org.junit.Test
import org.junit.runner.Description
import org.junit.runner.RunWith
@@ -39,6 +45,8 @@ class TaskbarUnitTestRuleTest {
private val context = TaskbarWindowSandboxContext.create(getInstrumentation().targetContext)
@get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
@Test
fun testSetup_taskbarInitialized() {
onSetup { assertThat(activityContext).isInstanceOf(TaskbarActivityContext::class.java) }
@@ -127,6 +135,44 @@ class TaskbarUnitTestRuleTest {
}
}
@EnableFlags(Flags.FLAG_ENABLE_BUBBLE_BAR)
@Test
fun testInjectBubbleController_bubbleFlagOn_isInjected() {
val testClass =
object {
@InjectController lateinit var controller: BubbleBarController
val isInjected: Boolean
get() = ::controller.isInitialized
}
TaskbarUnitTestRule(testClass, context).apply(EMPTY_STATEMENT, DESCRIPTION).evaluate()
onSetup(TaskbarUnitTestRule(testClass, context)) {
assertThat(testClass.isInjected).isTrue()
}
}
@DisableFlags(Flags.FLAG_ENABLE_BUBBLE_BAR)
@Test
fun testInjectBubbleController_bubbleFlagOff_exceptionThrown() {
val testClass =
object {
@InjectController lateinit var controller: BubbleBarController
}
// We cannot use #assertThrows because we also catch an assumption violated exception when
// running #evaluate on devices that do not support Taskbar.
val result =
try {
TaskbarUnitTestRule(testClass, context)
.apply(EMPTY_STATEMENT, DESCRIPTION)
.evaluate()
} catch (e: NoSuchElementException) {
e
}
assertThat(result).isInstanceOf(NoSuchElementException::class.java)
}
@Test
fun testUserSetupMode_default_isComplete() {
onSetup { assertThat(activityContext.isUserSetupComplete).isTrue() }
@@ -16,14 +16,22 @@
package com.android.quickstep;
import static com.android.quickstep.AbsSwipeUpHandler.STATE_HANDLER_INVALIDATED;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
@@ -55,6 +63,7 @@ import com.android.systemui.shared.system.InputConsumerController;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -209,7 +218,7 @@ public abstract class AbsSwipeUpHandlerTestCase<
runOnMainSync(() -> {
absSwipeUpHandler.startNewTask(unused -> {});
verify(mRecentsAnimationController).finish(anyBoolean(), any());
verifyRecentsAnimationFinishedAndCallCallback();
});
}
@@ -219,10 +228,57 @@ public abstract class AbsSwipeUpHandlerTestCase<
runOnMainSync(() -> {
verify(mRecentsAnimationController).detachNavigationBarFromApp(true);
verify(mRecentsAnimationController).finish(anyBoolean(), any(), anyBoolean());
verifyRecentsAnimationFinishedAndCallCallback();
});
}
@Test
public void testHomeGesture_invalidatesHandlerAfterParallelAnim() {
ValueAnimator parallelAnim = new ValueAnimator();
parallelAnim.setRepeatCount(ValueAnimator.INFINITE);
when(mActivityInterface.getParallelAnimationToLauncher(any(), anyLong(), any()))
.thenReturn(parallelAnim);
SWIPE_HANDLER handler = createSwipeUpHandlerForGesture(GestureState.GestureEndTarget.HOME);
runOnMainSync(() -> {
parallelAnim.start();
verifyRecentsAnimationFinishedAndCallCallback();
assertFalse(handler.mStateCallback.hasStates(STATE_HANDLER_INVALIDATED));
parallelAnim.end();
assertTrue(handler.mStateCallback.hasStates(STATE_HANDLER_INVALIDATED));
});
}
@Test
public void testHomeGesture_invalidatesHandlerIfNoParallelAnim() {
when(mActivityInterface.getParallelAnimationToLauncher(any(), anyLong(), any()))
.thenReturn(null);
SWIPE_HANDLER handler = createSwipeUpHandlerForGesture(GestureState.GestureEndTarget.HOME);
runOnMainSync(() -> {
verifyRecentsAnimationFinishedAndCallCallback();
assertTrue(handler.mStateCallback.hasStates(STATE_HANDLER_INVALIDATED));
});
}
/**
* Verifies that RecentsAnimationController#finish() is called, and captures and runs any
* callback that was passed to it. This ensures that STATE_CURRENT_TASK_FINISHED is correctly
* set for example.
*/
private void verifyRecentsAnimationFinishedAndCallCallback() {
ArgumentCaptor<Runnable> finishCallback = ArgumentCaptor.forClass(Runnable.class);
// Check if the 2 parameter method is called.
verify(mRecentsAnimationController, atLeast(0)).finish(
anyBoolean(), finishCallback.capture());
if (finishCallback.getAllValues().isEmpty()) {
// Check if the 3 parameter method is called.
verify(mRecentsAnimationController).finish(
anyBoolean(), finishCallback.capture(), anyBoolean());
}
if (finishCallback.getValue() != null) {
finishCallback.getValue().run();
}
}
private SWIPE_HANDLER createSwipeUpHandlerForGesture(GestureState.GestureEndTarget endTarget) {
boolean isQuickSwitch = endTarget == GestureState.GestureEndTarget.NEW_TASK;
@@ -95,6 +95,7 @@ public class GridCustomizationsProvider extends ContentProvider {
private static final int MESSAGE_ID_UPDATE_PREVIEW = 1337;
private static final int MESSAGE_ID_UPDATE_GRID = 7414;
private static final int MESSAGE_ID_UPDATE_COLOR = 856;
// Set of all active previews used to track duplicate memory allocations
private final Set<PreviewLifecycleObserver> mActivePreviews =
@@ -289,6 +290,11 @@ public class GridCustomizationsProvider extends ContentProvider {
renderer.updateGrid(gridName);
}
break;
case MESSAGE_ID_UPDATE_COLOR:
if (Flags.newCustomizationPickerUi()) {
renderer.previewColor(message.getData());
}
break;
default:
// Unknown command, destroy lifecycle
Log.d(TAG, "Unknown preview command: " + message.what + ", destroying preview");
@@ -98,6 +98,7 @@ import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.LauncherWidgetHolder;
import com.android.launcher3.widget.LocalColorExtractor;
import com.android.launcher3.widget.util.WidgetSizes;
import com.android.systemui.shared.Flags;
import java.util.ArrayList;
import java.util.Collections;
@@ -150,6 +151,14 @@ public class LauncherPreviewRenderer extends ContextWrapper
InvariantDeviceProfile idp,
WallpaperColors wallpaperColorsOverride,
@Nullable final SparseArray<Size> launcherWidgetSpanInfo) {
this(context, idp, null, wallpaperColorsOverride, launcherWidgetSpanInfo);
}
public LauncherPreviewRenderer(Context context,
InvariantDeviceProfile idp,
SparseIntArray previewColorOverride,
WallpaperColors wallpaperColorsOverride,
@Nullable final SparseArray<Size> launcherWidgetSpanInfo) {
super(context);
mUiHandler = new Handler(Looper.getMainLooper());
@@ -206,12 +215,29 @@ public class LauncherPreviewRenderer extends ContextWrapper
mWorkspaceScreens.put(Workspace.SECOND_SCREEN_ID, rightPanel);
}
WallpaperColors wallpaperColors = wallpaperColorsOverride != null
? wallpaperColorsOverride
: WallpaperManager.getInstance(context).getWallpaperColors(FLAG_SYSTEM);
mWallpaperColorResources = wallpaperColors != null
? LocalColorExtractor.newInstance(context).generateColorsOverride(wallpaperColors)
: null;
if (Flags.newCustomizationPickerUi()) {
if (previewColorOverride != null) {
mWallpaperColorResources = previewColorOverride;
} else if (wallpaperColorsOverride != null) {
mWallpaperColorResources = LocalColorExtractor.newInstance(
context).generateColorsOverride(wallpaperColorsOverride);
} else {
WallpaperColors wallpaperColors = WallpaperManager.getInstance(
context).getWallpaperColors(FLAG_SYSTEM);
mWallpaperColorResources = wallpaperColors != null
? LocalColorExtractor.newInstance(context).generateColorsOverride(
wallpaperColors)
: null;
}
} else {
WallpaperColors wallpaperColors = wallpaperColorsOverride != null
? wallpaperColorsOverride
: WallpaperManager.getInstance(context).getWallpaperColors(FLAG_SYSTEM);
mWallpaperColorResources = wallpaperColors != null
? LocalColorExtractor.newInstance(context).generateColorsOverride(
wallpaperColors)
: null;
}
mAppWidgetHost = new LauncherPreviewAppWidgetHost(context);
}
@@ -32,6 +32,7 @@ import android.os.IBinder;
import android.util.Log;
import android.util.Size;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.SurfaceControlViewHost;
@@ -81,6 +82,8 @@ public class PreviewSurfaceRenderer {
private static final String KEY_VIEW_HEIGHT = "height";
private static final String KEY_DISPLAY_ID = "display_id";
private static final String KEY_COLORS = "wallpaper_colors";
private static final String KEY_COLOR_RESOURCE_IDS = "color_resource_ids";
private static final String KEY_COLOR_VALUES = "color_values";
private Context mContext;
private final IBinder mHostToken;
@@ -91,6 +94,7 @@ public class PreviewSurfaceRenderer {
private final int mDisplayId;
private final Display mDisplay;
private final WallpaperColors mWallpaperColors;
private SparseIntArray mPreviewColorOverride;
private final RunnableList mLifeCycleTracker;
private final SurfaceControlViewHost mSurfaceControlViewHost;
@@ -110,6 +114,9 @@ public class PreviewSurfaceRenderer {
mGridName = InvariantDeviceProfile.getCurrentGridName(context);
}
mWallpaperColors = bundle.getParcelable(KEY_COLORS);
if (Flags.newCustomizationPickerUi()) {
updateColorOverrides(bundle);
}
mHideQsb = bundle.getBoolean(GridCustomizationsProvider.KEY_HIDE_BOTTOM_ROW);
mHostToken = bundle.getBinder(KEY_HOST_TOKEN);
@@ -217,20 +224,60 @@ public class PreviewSurfaceRenderer {
}
}
/**
* Updates the colors of the preview.
*
* @param bundle Bundle with an int array of color ids and an int array of overriding colors.
*/
public void previewColor(Bundle bundle) {
updateColorOverrides(bundle);
loadAsync();
}
private void updateColorOverrides(Bundle bundle) {
int[] ids = bundle.getIntArray(KEY_COLOR_RESOURCE_IDS);
int[] colors = bundle.getIntArray(KEY_COLOR_VALUES);
if (ids != null && colors != null) {
mPreviewColorOverride = new SparseIntArray();
for (int i = 0; i < ids.length; i++) {
mPreviewColorOverride.put(ids[i], colors[i]);
}
} else {
mPreviewColorOverride = null;
}
}
/***
* Generates a new context overriding the theme color and the display size without affecting the
* main application context
*/
private Context getPreviewContext() {
Context context = mContext.createDisplayContext(mDisplay);
if (mWallpaperColors == null) {
if (Flags.newCustomizationPickerUi()) {
if (mPreviewColorOverride != null) {
LocalColorExtractor.newInstance(context)
.applyColorsOverride(context, mPreviewColorOverride);
} else if (mWallpaperColors != null) {
LocalColorExtractor.newInstance(context)
.applyColorsOverride(context, mWallpaperColors);
}
if (mWallpaperColors != null) {
return new ContextThemeWrapper(context,
Themes.getActivityThemeRes(context, mWallpaperColors.getColorHints()));
} else {
return new ContextThemeWrapper(context,
Themes.getActivityThemeRes(context));
}
} else {
if (mWallpaperColors == null) {
return new ContextThemeWrapper(context,
Themes.getActivityThemeRes(context));
}
LocalColorExtractor.newInstance(context)
.applyColorsOverride(context, mWallpaperColors);
return new ContextThemeWrapper(context,
Themes.getActivityThemeRes(context));
Themes.getActivityThemeRes(context, mWallpaperColors.getColorHints()));
}
LocalColorExtractor.newInstance(context)
.applyColorsOverride(context, mWallpaperColors);
return new ContextThemeWrapper(context,
Themes.getActivityThemeRes(context, mWallpaperColors.getColorHints()));
}
@WorkerThread
@@ -300,8 +347,13 @@ public class PreviewSurfaceRenderer {
if (mDestroyed) {
return;
}
mRenderer = new LauncherPreviewRenderer(inflationContext, idp,
mWallpaperColors, launcherWidgetSpanInfo);
if (Flags.newCustomizationPickerUi()) {
mRenderer = new LauncherPreviewRenderer(inflationContext, idp, mPreviewColorOverride,
mWallpaperColors, launcherWidgetSpanInfo);
} else {
mRenderer = new LauncherPreviewRenderer(inflationContext, idp,
mWallpaperColors, launcherWidgetSpanInfo);
}
mRenderer.hideBottomRow(mHideQsb);
View view = mRenderer.getRenderedView(dataModel, widgetProviderInfoMap);
// This aspect scales the view to fit in the surface and centers it
@@ -48,4 +48,9 @@ public class LocalColorExtractor implements ResourceBasedOverride {
public SparseIntArray generateColorsOverride(WallpaperColors colors) {
return null;
}
/**
* Updates the base context to contain the colors override
*/
public void applyColorsOverride(Context base, SparseIntArray override) { }
}