Merge "Fix media output component appearance" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 7dc15fb..9ae5afb 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7239,8 +7239,8 @@
public final class PictureInPictureUiState implements android.os.Parcelable {
method public int describeContents();
- method @FlaggedApi("android.app.enable_pip_ui_state_callback_on_entering") public boolean isEnteringPip();
method public boolean isStashed();
+ method @FlaggedApi("android.app.enable_pip_ui_state_callback_on_entering") public boolean isTransitioningToPip();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.PictureInPictureUiState> CREATOR;
}
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 7c4df28..9c1a8e8 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -135,7 +135,7 @@
@FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public class SignedPackage {
method @NonNull public byte[] getCertificateDigest();
- method @NonNull public String getPkgName();
+ method @NonNull public String getPackageName();
}
}
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 4cad585..c58624e 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -26,6 +26,7 @@
import android.util.LongArray;
import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicReference;
/**
* This is the superclass for classes which provide basic support for animations which can be
@@ -76,7 +77,7 @@
* of it in case the list is modified while iterating. The array can be reused to avoid
* allocation on every notification.
*/
- private Object[] mCachedList;
+ private AtomicReference<Object[]> mCachedList = new AtomicReference<>();
/**
* Tracks whether we've notified listeners of the onAnimationStart() event. This can be
@@ -452,7 +453,7 @@
if (mPauseListeners != null) {
anim.mPauseListeners = new ArrayList<AnimatorPauseListener>(mPauseListeners);
}
- anim.mCachedList = null;
+ anim.mCachedList.set(null);
anim.mStartListenersCalled = false;
return anim;
} catch (CloneNotSupportedException e) {
@@ -654,13 +655,9 @@
int size = list == null ? 0 : list.size();
if (size > 0) {
// Try to reuse mCacheList to store the items of list.
- Object[] array;
- if (mCachedList == null || mCachedList.length < size) {
+ Object[] array = mCachedList.getAndSet(null);
+ if (array == null || array.length < size) {
array = new Object[size];
- } else {
- array = mCachedList;
- // Clear it in case there is some reentrancy
- mCachedList = null;
}
list.toArray(array);
for (int i = 0; i < size; i++) {
@@ -670,7 +667,7 @@
array[i] = null;
}
// Store it for the next call so we can reuse this array, if needed.
- mCachedList = array;
+ mCachedList.compareAndSet(null, array);
}
}
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index da0cc01..41b97d0 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -118,6 +118,8 @@
# Multitasking
per-file multitasking.aconfig = file:/services/core/java/com/android/server/wm/OWNERS
per-file multitasking.aconfig = file:/libs/WindowManager/Shell/OWNERS
+per-file PictureInPicture* = file:/services/core/java/com/android/server/wm/OWNERS
+per-file PictureInPicture* = file:/libs/WindowManager/Shell/OWNERS
# Zygote
per-file *Zygote* = file:/ZYGOTE_OWNERS
diff --git a/core/java/android/app/PictureInPictureUiState.java b/core/java/android/app/PictureInPictureUiState.java
index 39ba54c..1629536 100644
--- a/core/java/android/app/PictureInPictureUiState.java
+++ b/core/java/android/app/PictureInPictureUiState.java
@@ -31,12 +31,12 @@
public final class PictureInPictureUiState implements Parcelable {
private final boolean mIsStashed;
- private final boolean mIsEnteringPip;
+ private final boolean mIsTransitioningToPip;
/** {@hide} */
PictureInPictureUiState(Parcel in) {
mIsStashed = in.readBoolean();
- mIsEnteringPip = in.readBoolean();
+ mIsTransitioningToPip = in.readBoolean();
}
/** {@hide} */
@@ -45,9 +45,9 @@
this(isStashed, false /* isEnteringPip */);
}
- private PictureInPictureUiState(boolean isStashed, boolean isEnteringPip) {
+ private PictureInPictureUiState(boolean isStashed, boolean isTransitioningToPip) {
mIsStashed = isStashed;
- mIsEnteringPip = isEnteringPip;
+ mIsTransitioningToPip = isTransitioningToPip;
}
/**
@@ -77,14 +77,14 @@
* whether via auto enter PiP or calling
* {@link Activity#enterPictureInPictureMode(PictureInPictureParams)} explicitly, app can expect
* {@link Activity#onPictureInPictureUiStateChanged(PictureInPictureUiState)} callback with
- * {@link #isEnteringPip()} to be {@code true} first,
+ * {@link #isTransitioningToPip()} to be {@code true} first,
* followed by {@link Activity#onPictureInPictureModeChanged(boolean, Configuration)} when it
* fully settles in PiP mode.
*
* When app receives the
* {@link Activity#onPictureInPictureUiStateChanged(PictureInPictureUiState)} callback with
- * {@link #isEnteringPip()} being {@code true}, it's recommended to hide certain UI elements,
- * such as video controls, to archive a clean entering PiP animation.
+ * {@link #isTransitioningToPip()} being {@code true}, it's recommended to hide certain UI
+ * elements, such as video controls, to archive a clean entering PiP animation.
*
* In case an application wants to restore the previously hidden UI elements when exiting
* PiP, it is recommended to do that in
@@ -92,8 +92,8 @@
* than the beginning of exit PiP animation.
*/
@FlaggedApi(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
- public boolean isEnteringPip() {
- return mIsEnteringPip;
+ public boolean isTransitioningToPip() {
+ return mIsTransitioningToPip;
}
@Override
@@ -102,12 +102,12 @@
if (!(o instanceof PictureInPictureUiState)) return false;
PictureInPictureUiState that = (PictureInPictureUiState) o;
return mIsStashed == that.mIsStashed
- && mIsEnteringPip == that.mIsEnteringPip;
+ && mIsTransitioningToPip == that.mIsTransitioningToPip;
}
@Override
public int hashCode() {
- return Objects.hash(mIsStashed, mIsEnteringPip);
+ return Objects.hash(mIsStashed, mIsTransitioningToPip);
}
@Override
@@ -118,7 +118,7 @@
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeBoolean(mIsStashed);
- out.writeBoolean(mIsEnteringPip);
+ out.writeBoolean(mIsTransitioningToPip);
}
public static final @android.annotation.NonNull Creator<PictureInPictureUiState> CREATOR =
@@ -138,7 +138,7 @@
@FlaggedApi(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
public static final class Builder {
private boolean mIsStashed;
- private boolean mIsEnteringPip;
+ private boolean mIsTransitioningToPip;
/** Empty constructor. */
public Builder() {
@@ -154,11 +154,11 @@
}
/**
- * Sets the {@link #mIsEnteringPip} state.
+ * Sets the {@link #mIsTransitioningToPip} state.
* @return The same {@link Builder} instance.
*/
- public Builder setEnteringPip(boolean isEnteringPip) {
- mIsEnteringPip = isEnteringPip;
+ public Builder setTransitioningToPip(boolean isEnteringPip) {
+ mIsTransitioningToPip = isEnteringPip;
return this;
}
@@ -166,7 +166,7 @@
* @return The constructed {@link PictureInPictureUiState} instance.
*/
public PictureInPictureUiState build() {
- return new PictureInPictureUiState(mIsStashed, mIsEnteringPip);
+ return new PictureInPictureUiState(mIsStashed, mIsTransitioningToPip);
}
}
}
diff --git a/core/java/android/content/pm/SignedPackage.java b/core/java/android/content/pm/SignedPackage.java
index 4d1b136..7bffa77 100644
--- a/core/java/android/content/pm/SignedPackage.java
+++ b/core/java/android/content/pm/SignedPackage.java
@@ -35,9 +35,9 @@
private final SignedPackageParcel mData;
/** @hide */
- public SignedPackage(@NonNull String pkgName, @NonNull byte[] certificateDigest) {
+ public SignedPackage(@NonNull String packageName, @NonNull byte[] certificateDigest) {
SignedPackageParcel data = new SignedPackageParcel();
- data.pkgName = pkgName;
+ data.packageName = packageName;
data.certificateDigest = certificateDigest;
mData = data;
}
@@ -52,8 +52,8 @@
return mData;
}
- public @NonNull String getPkgName() {
- return mData.pkgName;
+ public @NonNull String getPackageName() {
+ return mData.packageName;
}
public @NonNull byte[] getCertificateDigest() {
@@ -64,12 +64,12 @@
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SignedPackage that)) return false;
- return mData.pkgName.equals(that.mData.pkgName) && Arrays.equals(mData.certificateDigest,
- that.mData.certificateDigest);
+ return mData.packageName.equals(that.mData.packageName) && Arrays.equals(
+ mData.certificateDigest, that.mData.certificateDigest);
}
@Override
public int hashCode() {
- return Objects.hash(mData.pkgName, Arrays.hashCode(mData.certificateDigest));
+ return Objects.hash(mData.packageName, Arrays.hashCode(mData.certificateDigest));
}
}
diff --git a/core/java/android/content/pm/SignedPackageParcel.aidl b/core/java/android/content/pm/SignedPackageParcel.aidl
index 7957f7f..bb4dc86 100644
--- a/core/java/android/content/pm/SignedPackageParcel.aidl
+++ b/core/java/android/content/pm/SignedPackageParcel.aidl
@@ -20,6 +20,6 @@
/** @hide */
parcelable SignedPackageParcel {
- String pkgName;
+ String packageName;
byte[] certificateDigest;
}
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 76c20ce..749f218 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -874,8 +874,11 @@
Class<CameraCharacteristics.Key<?>> keyTyped =
(Class<CameraCharacteristics.Key<?>>) key;
+ // Do not include synthetic keys. Including synthetic keys leads to undefined
+ // behavior. This causes inclusion of capabilities that may not be supported in
+ // camera extensions.
ret.addAll(chars.getAvailableKeyList(CameraCharacteristics.class, keyTyped, keys,
- /*includeSynthetic*/ true));
+ /*includeSynthetic*/ false));
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension for all available keys! Extension "
diff --git a/core/java/android/net/flags.aconfig b/core/java/android/net/flags.aconfig
index 311dc09..6efb872 100644
--- a/core/java/android/net/flags.aconfig
+++ b/core/java/android/net/flags.aconfig
@@ -9,3 +9,11 @@
description: "The flag controls the access for getIpSecTransformState and IpSecTransformState"
bug: "308011229"
}
+
+flag {
+ name: "powered_off_finding_platform"
+ namespace: "nearby"
+ description: "Controls whether the Powered Off Finding feature is enabled"
+ bug: "307898240"
+}
+
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index c9c91fc..efbd96b 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -591,9 +591,14 @@
/**
* Scale given vibration intensity by the given factor.
*
+ * <p> This scale is not necessarily linear and may apply a gamma correction to the scale
+ * factor before using it.
+ *
* @param intensity relative intensity of the effect, must be between 0 and 1
* @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
* scale down the intensity, values larger than 1 will scale up
+ * @return the scaled intensity which will be values within [0, 1].
+ *
* @hide
*/
public static float scale(float intensity, float scaleFactor) {
@@ -624,6 +629,20 @@
}
/**
+ * Performs a linear scaling on the given vibration intensity by the given factor.
+ *
+ * @param intensity relative intensity of the effect, must be between 0 and 1.
+ * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
+ * scale down the intensity, values larger than 1 will scale up.
+ * @return the scaled intensity which will be values within [0, 1].
+ *
+ * @hide
+ */
+ public static float scaleLinearly(float intensity, float scaleFactor) {
+ return MathUtils.constrain(intensity * scaleFactor, 0f, 1f);
+ }
+
+ /**
* Returns a compact version of the {@link #toString()} result for debugging purposes.
*
* @hide
diff --git a/core/java/android/os/vibrator/PrebakedSegment.java b/core/java/android/os/vibrator/PrebakedSegment.java
index a035092..39f8412 100644
--- a/core/java/android/os/vibrator/PrebakedSegment.java
+++ b/core/java/android/os/vibrator/PrebakedSegment.java
@@ -137,6 +137,14 @@
/** @hide */
@NonNull
@Override
+ public PrebakedSegment scaleLinearly(float scaleFactor) {
+ // Prebaked effect strength cannot be scaled with this method.
+ return this;
+ }
+
+ /** @hide */
+ @NonNull
+ @Override
public PrebakedSegment applyEffectStrength(int effectStrength) {
if (effectStrength != mEffectStrength && isValidEffectStrength(effectStrength)) {
return new PrebakedSegment(mEffectId, mFallback, effectStrength);
diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java
index 95d97bf..3c84bcd 100644
--- a/core/java/android/os/vibrator/PrimitiveSegment.java
+++ b/core/java/android/os/vibrator/PrimitiveSegment.java
@@ -98,8 +98,24 @@
@NonNull
@Override
public PrimitiveSegment scale(float scaleFactor) {
- return new PrimitiveSegment(mPrimitiveId, VibrationEffect.scale(mScale, scaleFactor),
- mDelay);
+ float newScale = VibrationEffect.scale(mScale, scaleFactor);
+ if (Float.compare(mScale, newScale) == 0) {
+ return this;
+ }
+
+ return new PrimitiveSegment(mPrimitiveId, newScale, mDelay);
+ }
+
+ /** @hide */
+ @NonNull
+ @Override
+ public PrimitiveSegment scaleLinearly(float scaleFactor) {
+ float newScale = VibrationEffect.scaleLinearly(mScale, scaleFactor);
+ if (Float.compare(mScale, newScale) == 0) {
+ return this;
+ }
+
+ return new PrimitiveSegment(mPrimitiveId, newScale, mDelay);
}
/** @hide */
diff --git a/core/java/android/os/vibrator/RampSegment.java b/core/java/android/os/vibrator/RampSegment.java
index 5f9d102..09d2e26 100644
--- a/core/java/android/os/vibrator/RampSegment.java
+++ b/core/java/android/os/vibrator/RampSegment.java
@@ -159,6 +159,21 @@
/** @hide */
@NonNull
@Override
+ public RampSegment scaleLinearly(float scaleFactor) {
+ float newStartAmplitude = VibrationEffect.scaleLinearly(mStartAmplitude, scaleFactor);
+ float newEndAmplitude = VibrationEffect.scaleLinearly(mEndAmplitude, scaleFactor);
+ if (Float.compare(mStartAmplitude, newStartAmplitude) == 0
+ && Float.compare(mEndAmplitude, newEndAmplitude) == 0) {
+ return this;
+ }
+ return new RampSegment(newStartAmplitude, newEndAmplitude, mStartFrequencyHz,
+ mEndFrequencyHz,
+ mDuration);
+ }
+
+ /** @hide */
+ @NonNull
+ @Override
public RampSegment applyEffectStrength(int effectStrength) {
return this;
}
diff --git a/core/java/android/os/vibrator/StepSegment.java b/core/java/android/os/vibrator/StepSegment.java
index 9576a5b..fa083c1 100644
--- a/core/java/android/os/vibrator/StepSegment.java
+++ b/core/java/android/os/vibrator/StepSegment.java
@@ -137,8 +137,25 @@
if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) == 0) {
return this;
}
- return new StepSegment(VibrationEffect.scale(mAmplitude, scaleFactor), mFrequencyHz,
- mDuration);
+ float newAmplitude = VibrationEffect.scale(mAmplitude, scaleFactor);
+ if (Float.compare(newAmplitude, mAmplitude) == 0) {
+ return this;
+ }
+ return new StepSegment(newAmplitude, mFrequencyHz, mDuration);
+ }
+
+ /** @hide */
+ @NonNull
+ @Override
+ public StepSegment scaleLinearly(float scaleFactor) {
+ if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) == 0) {
+ return this;
+ }
+ float newAmplitude = VibrationEffect.scaleLinearly(mAmplitude, scaleFactor);
+ if (Float.compare(newAmplitude, mAmplitude) == 0) {
+ return this;
+ }
+ return new StepSegment(newAmplitude, mFrequencyHz, mDuration);
}
/** @hide */
diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java
index 17ac36f..e1fb4e3 100644
--- a/core/java/android/os/vibrator/VibrationEffectSegment.java
+++ b/core/java/android/os/vibrator/VibrationEffectSegment.java
@@ -96,6 +96,9 @@
/**
* Scale the segment intensity with the given factor.
*
+ * <p> This scale is not necessarily linear and may apply a gamma correction to the scale
+ * factor before using it.
+ *
* @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
* scale down the intensity, values larger than 1 will scale up
*
@@ -105,6 +108,17 @@
public abstract <T extends VibrationEffectSegment> T scale(float scaleFactor);
/**
+ * Performs a linear scaling on the segment intensity with the given factor.
+ *
+ * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
+ * scale down the intensity, values larger than 1 will scale up
+ *
+ * @hide
+ */
+ @NonNull
+ public abstract <T extends VibrationEffectSegment> T scaleLinearly(float scaleFactor);
+
+ /**
* Applies given effect strength to prebaked effects.
*
* @param effectStrength new effect strength to be applied, one of
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c475f6b..51d7caa 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -69,11 +69,11 @@
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
import android.window.AddToSurfaceSyncGroupResult;
+import android.window.IGlobalDragListener;
import android.window.IScreenRecordingCallback;
import android.window.ISurfaceSyncGroupCompletedListener;
import android.window.ITaskFpsCallback;
import android.window.ITrustedPresentationListener;
-import android.window.IUnhandledDragListener;
import android.window.InputTransferToken;
import android.window.ScreenCapture;
import android.window.TrustedPresentationThresholds;
@@ -1094,10 +1094,9 @@
void unregisterScreenRecordingCallback(IScreenRecordingCallback callback);
/**
- * Sets the listener to be called back when a cross-window drag and drop operation is unhandled
- * (ie. not handled by any window which can handle the drag).
+ * Sets the listener to be called back when a cross-window drag and drop operation happens.
*/
- void setUnhandledDragListener(IUnhandledDragListener listener);
+ void setGlobalDragListener(IGlobalDragListener listener);
boolean transferTouchGesture(in InputTransferToken transferFromToken,
in InputTransferToken transferToToken);
diff --git a/core/java/android/window/IUnhandledDragListener.aidl b/core/java/android/window/IGlobalDragListener.aidl
similarity index 78%
rename from core/java/android/window/IUnhandledDragListener.aidl
rename to core/java/android/window/IGlobalDragListener.aidl
index 52e9895..8f2ca02 100644
--- a/core/java/android/window/IUnhandledDragListener.aidl
+++ b/core/java/android/window/IGlobalDragListener.aidl
@@ -16,14 +16,21 @@
package android.window;
+import android.app.ActivityManager;
import android.view.DragEvent;
import android.window.IUnhandledDragCallback;
/**
- * An interface to a handler for global drags that are not consumed (ie. not handled by any window).
+ * An interface to a handler for global drags.
* {@hide}
*/
-oneway interface IUnhandledDragListener {
+oneway interface IGlobalDragListener {
+ /**
+ * Called when a cross-window drag is handled by another window.
+ * @param taskInfo the task containing the window that consumed the drop
+ */
+ void onCrossWindowDrop(in ActivityManager.RunningTaskInfo taskInfo);
+
/**
* Called when the user finishes the drag gesture but no windows have reported handling the
* drop. The DragEvent is populated with the drag surface for the listener to animate. The
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index f93b306..a883144 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -378,8 +378,8 @@
ALOGD("channel '%s' ~ Received key event.", getInputChannelName().c_str());
}
inputEventObj =
- android_view_KeyEvent_fromNative(env,
- static_cast<KeyEvent&>(*inputEvent));
+ android_view_KeyEvent_obtainAsCopy(env,
+ static_cast<KeyEvent&>(*inputEvent));
break;
case InputEventType::MOTION: {
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index b5fbb22..88b02ba 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -359,7 +359,7 @@
jint seq, jobject eventObj) {
sp<NativeInputEventSender> sender =
reinterpret_cast<NativeInputEventSender*>(senderPtr);
- const KeyEvent event = android_view_KeyEvent_toNative(env, eventObj);
+ const KeyEvent event = android_view_KeyEvent_obtainAsCopy(env, eventObj);
status_t status = sender->sendKeyEvent(seq, &event);
return !status;
}
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index a0d081d..50d2cbe 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -221,7 +221,7 @@
jboolean predispatch) {
InputQueue* queue = reinterpret_cast<InputQueue*>(ptr);
KeyEvent* event = queue->createKeyEvent();
- *event = android_view_KeyEvent_toNative(env, eventObj);
+ *event = android_view_KeyEvent_obtainAsCopy(env, eventObj);
if (predispatch) {
event->setFlags(event->getFlags() | AKEY_EVENT_FLAG_PREDISPATCH);
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index a79e37a..3325e89 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -219,7 +219,7 @@
result = env->NewObjectArray(jsize(events.size()), gKeyEventClassInfo.clazz, NULL);
if (result) {
for (size_t i = 0; i < events.size(); i++) {
- jobject keyEventObj = android_view_KeyEvent_fromNative(env, events.itemAt(i));
+ jobject keyEventObj = android_view_KeyEvent_obtainAsCopy(env, events.itemAt(i));
if (!keyEventObj) break; // threw OOM exception
env->SetObjectArrayElement(result, jsize(i), keyEventObj);
env->DeleteLocalRef(keyEventObj);
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index a9c9919..908ab5e 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -94,7 +94,7 @@
// ----------------------------------------------------------------------------
-jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent& event) {
+jobject android_view_KeyEvent_obtainAsCopy(JNIEnv* env, const KeyEvent& event) {
ScopedLocalRef<jbyteArray> hmac = toJbyteArray(env, event.getHmac());
jobject eventObj =
env->CallStaticObjectMethod(gKeyEventClassInfo.clazz, gKeyEventClassInfo.obtain,
@@ -113,7 +113,7 @@
return eventObj;
}
-KeyEvent android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj) {
+KeyEvent android_view_KeyEvent_obtainAsCopy(JNIEnv* env, jobject eventObj) {
jint id = env->GetIntField(eventObj, gKeyEventClassInfo.mId);
jint deviceId = env->GetIntField(eventObj, gKeyEventClassInfo.mDeviceId);
jint source = env->GetIntField(eventObj, gKeyEventClassInfo.mSource);
diff --git a/core/jni/android_view_KeyEvent.h b/core/jni/android_view_KeyEvent.h
index bc4876a..eab7c97 100644
--- a/core/jni/android_view_KeyEvent.h
+++ b/core/jni/android_view_KeyEvent.h
@@ -27,11 +27,11 @@
/* Obtains an instance of a DVM KeyEvent object as a copy of a native KeyEvent instance.
* Returns NULL on error. */
-extern jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent& event);
+extern jobject android_view_KeyEvent_obtainAsCopy(JNIEnv* env, const KeyEvent& event);
/* Copies the contents of a DVM KeyEvent object to a native KeyEvent instance.
* Returns non-zero on error. */
-extern KeyEvent android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj);
+extern KeyEvent android_view_KeyEvent_obtainAsCopy(JNIEnv* env, jobject eventObj);
/* Recycles a DVM KeyEvent object.
* Key events should only be recycled if they are owned by the system since user
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index ebf4cca..a7d083c 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -988,7 +988,7 @@
@Override
public void onPictureInPictureUiStateChanged(PictureInPictureUiState pipState) {
- if (mPipUiStateLatch != null && pipState.isEnteringPip()) {
+ if (mPipUiStateLatch != null && pipState.isTransitioningToPip()) {
mPipUiStateLatch.countDown();
}
}
diff --git a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
index 4f5f3c0..7dd9e55 100644
--- a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
@@ -106,6 +106,13 @@
}
@Test
+ public void testScaleLinearly_ignoresAndReturnsSameEffect() {
+ PrebakedSegment prebaked = new PrebakedSegment(
+ VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
+ assertSame(prebaked, prebaked.scaleLinearly(0.5f));
+ }
+
+ @Test
public void testDuration() {
assertEquals(-1, new PrebakedSegment(
VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
diff --git a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
index ec5a084..e9a08ae 100644
--- a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
@@ -129,6 +129,27 @@
}
@Test
+ public void testScaleLinearly() {
+ PrimitiveSegment initial = new PrimitiveSegment(
+ VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0);
+
+ assertEquals(1f, initial.scaleLinearly(1).getScale(), TOLERANCE);
+ assertEquals(0.5f, initial.scaleLinearly(0.5f).getScale(), TOLERANCE);
+ assertEquals(1f, initial.scaleLinearly(1.5f).getScale(), TOLERANCE);
+ assertEquals(0.8f, initial.scaleLinearly(0.8f).getScale(), TOLERANCE);
+ // Restores back to the exact original value since this is a linear scaling.
+ assertEquals(1f, initial.scaleLinearly(0.8f).scaleLinearly(1.25f).getScale(), TOLERANCE);
+
+ initial = new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_CLICK, 0, 0);
+
+ assertEquals(0f, initial.scaleLinearly(1).getScale(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(0.5f).getScale(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(1.5f).getScale(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(1.5f).scaleLinearly(2 / 3f).getScale(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(0.8f).scaleLinearly(1.25f).getScale(), TOLERANCE);
+ }
+
+ @Test
public void testDuration() {
assertEquals(-1, new PrimitiveSegment(
VibrationEffect.Composition.PRIMITIVE_NOOP, 1, 10).getDuration());
diff --git a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
index 5caa86b..01013ab 100644
--- a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
@@ -131,6 +131,37 @@
}
@Test
+ public void testScaleLinearly() {
+ RampSegment initial = new RampSegment(0, 1, 0, 0, 0);
+
+ assertEquals(0f, initial.scaleLinearly(1f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(0.5f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(1.5f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(1.5f).scaleLinearly(2 / 3f).getStartAmplitude(),
+ TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(0.8f).scaleLinearly(1.25f).getStartAmplitude(),
+ TOLERANCE);
+
+ assertEquals(1f, initial.scaleLinearly(1f).getEndAmplitude(), TOLERANCE);
+ assertEquals(0.5f, initial.scaleLinearly(0.5f).getEndAmplitude(), TOLERANCE);
+ assertEquals(1f, initial.scaleLinearly(1.5f).getEndAmplitude(), TOLERANCE);
+ assertEquals(0.8f, initial.scaleLinearly(0.8f).getEndAmplitude(), TOLERANCE);
+ // Restores back to the exact original value since this is a linear scaling.
+ assertEquals(0.8f, initial.scaleLinearly(1.5f).scaleLinearly(0.8f).getEndAmplitude(),
+ TOLERANCE);
+
+ initial = new RampSegment(0.5f, 1, 0, 0, 0);
+
+ assertEquals(0.5f, initial.scaleLinearly(1).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.25f, initial.scaleLinearly(0.5f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.75f, initial.scaleLinearly(1.5f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.4f, initial.scaleLinearly(0.8f).getStartAmplitude(), TOLERANCE);
+ // Restores back to the exact original value since this is a linear scaling.
+ assertEquals(0.5f, initial.scaleLinearly(0.8f).scaleLinearly(1.25f).getStartAmplitude(),
+ TOLERANCE);
+ }
+
+ @Test
public void testDuration() {
assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration());
}
diff --git a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
index 44db306..40776ab 100644
--- a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
@@ -141,6 +141,37 @@
}
@Test
+ public void testScaleLinearly_fullAmplitude() {
+ StepSegment initial = new StepSegment(1f, 0, 0);
+
+ assertEquals(1f, initial.scaleLinearly(1).getAmplitude(), TOLERANCE);
+ assertEquals(0.5f, initial.scaleLinearly(0.5f).getAmplitude(), TOLERANCE);
+ assertEquals(1f, initial.scaleLinearly(1.5f).getAmplitude(), TOLERANCE);
+ assertEquals(0.8f, initial.scaleLinearly(0.8f).getAmplitude(), TOLERANCE);
+ // Restores back to the exact original value since this is a linear scaling.
+ assertEquals(1f, initial.scaleLinearly(0.8f).scaleLinearly(1.25f).getAmplitude(),
+ TOLERANCE);
+
+ initial = new StepSegment(0, 0, 0);
+
+ assertEquals(0f, initial.scaleLinearly(1).getAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(0.5f).getAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(1.5f).getAmplitude(), TOLERANCE);
+ }
+
+ @Test
+ public void testScaleLinearly_defaultAmplitude() {
+ StepSegment initial = new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0, 0);
+
+ assertEquals(VibrationEffect.DEFAULT_AMPLITUDE, initial.scaleLinearly(1).getAmplitude(),
+ TOLERANCE);
+ assertEquals(VibrationEffect.DEFAULT_AMPLITUDE, initial.scaleLinearly(0.5f).getAmplitude(),
+ TOLERANCE);
+ assertEquals(VibrationEffect.DEFAULT_AMPLITUDE, initial.scaleLinearly(1.5f).getAmplitude(),
+ TOLERANCE);
+ }
+
+ @Test
public void testDuration() {
assertEquals(5, new StepSegment(0, 0, 5).getDuration());
}
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
index e04ab81..34f03c2 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
@@ -23,7 +23,8 @@
<com.android.wm.shell.bubbles.bar.BubbleBarHandleView
android:id="@+id/bubble_bar_handle_view"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content" />
+ android:layout_height="@dimen/bubble_bar_expanded_view_caption_height"
+ android:layout_width="@dimen/bubble_bar_expanded_view_caption_width"
+ android:layout_gravity="top|center_horizontal" />
</com.android.wm.shell.bubbles.bar.BubbleBarExpandedView>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index f73775b..3b0eb49 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -244,6 +244,8 @@
<dimen name="bubble_popup_padding">24dp</dimen>
<!-- The size of the caption bar inset at the top of bubble bar expanded view. -->
<dimen name="bubble_bar_expanded_view_caption_height">32dp</dimen>
+ <!-- The width of the caption bar at the top of bubble bar expanded view. -->
+ <dimen name="bubble_bar_expanded_view_caption_width">128dp</dimen>
<!-- The height of the dots shown for the caption menu in the bubble bar expanded view.. -->
<dimen name="bubble_bar_expanded_view_caption_dot_size">4dp</dimen>
<!-- The spacing between the dots for the caption menu in the bubble bar expanded view.. -->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index fe65fdd..d8d0d87 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -643,19 +643,6 @@
}
}
- /** Helper to set int metadata on the Surface corresponding to the task id. */
- public void setSurfaceMetadata(int taskId, int key, int value) {
- synchronized (mLock) {
- final TaskAppearedInfo info = mTasks.get(taskId);
- if (info == null || info.getLeash() == null) {
- return;
- }
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.setMetadata(info.getLeash(), key, value);
- t.apply();
- }
- }
-
private boolean updateTaskListenerIfNeeded(RunningTaskInfo taskInfo, SurfaceControl leash,
TaskListener oldListener, TaskListener newListener) {
if (oldListener == newListener) return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 5c6f73f..b123c28c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1838,7 +1838,7 @@
+ " expanded=%b selectionChanged=%b selected=%s"
+ " suppressed=%s unsupressed=%s shouldShowEducation=%b",
update.addedBubble != null ? update.addedBubble.getKey() : "null",
- update.removedBubbles.isEmpty(),
+ !update.removedBubbles.isEmpty(),
update.updatedBubble != null ? update.updatedBubble.getKey() : "null",
update.orderChanged, update.expandedChanged, update.expanded,
update.selectionChanged,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index eddd43f..271fb9a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -143,6 +143,8 @@
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCurrentCornerRadius);
}
});
+ // Set a touch sink to ensure that clicks on the caption area do not propagate to the parent
+ setOnTouchListener((v, event) -> true);
}
@Override
@@ -245,12 +247,8 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int height = MeasureSpec.getSize(heightMeasureSpec);
- int menuViewHeight = Math.min(mCaptionHeight, height);
- measureChild(mHandleView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(menuViewHeight,
- MeasureSpec.getMode(heightMeasureSpec)));
-
if (mTaskView != null) {
+ int height = MeasureSpec.getSize(heightMeasureSpec);
measureChild(mTaskView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(height,
MeasureSpec.getMode(heightMeasureSpec)));
}
@@ -259,14 +257,11 @@
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
- final int captionBottom = t + mCaptionHeight;
if (mTaskView != null) {
mTaskView.layout(l, t, r,
t + mTaskView.getMeasuredHeight());
mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0));
}
- // Handle draws on top of task view in the caption area.
- mHandleView.layout(l, t, r, captionBottom);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 62f2726..78a41f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -231,6 +231,7 @@
// Touch delegate for the menu
BubbleBarHandleView view = mExpandedView.getHandleView();
view.getBoundsOnScreen(mHandleTouchBounds);
+ // Move top value up to ensure touch target is large enough
mHandleTouchBounds.top -= mPositioner.getBubblePaddingTop();
mHandleTouchDelegate = new TouchDelegate(mHandleTouchBounds,
mExpandedView.getHandleView());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
index a9f687f..6ffeb97 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
@@ -125,11 +125,15 @@
public Rect getEntryDestinationBoundsIgnoringKeepClearAreas() {
final PipBoundsState.PipReentryState reentryState = mPipBoundsState.getReentryState();
- final Rect destinationBounds = reentryState != null
- ? getDefaultBounds(reentryState.getSnapFraction(), reentryState.getSize())
- : getDefaultBounds();
+ final Rect destinationBounds = getDefaultBounds();
+ if (reentryState != null) {
+ final Size scaledBounds = new Size(
+ Math.round(mPipBoundsState.getMaxSize().x * reentryState.getBoundsScale()),
+ Math.round(mPipBoundsState.getMaxSize().y * reentryState.getBoundsScale()));
+ destinationBounds.set(getDefaultBounds(reentryState.getSnapFraction(), scaledBounds));
+ }
- final boolean useCurrentSize = reentryState != null && reentryState.getSize() != null;
+ final boolean useCurrentSize = reentryState != null;
Rect aspectRatioBounds = transformBoundsToAspectRatioIfValid(destinationBounds,
mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
useCurrentSize);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
index df589df..b57e2d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
@@ -298,8 +298,8 @@
}
/** Save the reentry state to restore to when re-entering PIP mode. */
- public void saveReentryState(Size size, float fraction) {
- mPipReentryState = new PipReentryState(size, fraction);
+ public void saveReentryState(float fraction) {
+ mPipReentryState = new PipReentryState(mBoundsScale, fraction);
}
/** Returns the saved reentry state. */
@@ -601,17 +601,16 @@
public static final class PipReentryState {
private static final String TAG = PipReentryState.class.getSimpleName();
- private final @Nullable Size mSize;
private final float mSnapFraction;
+ private final float mBoundsScale;
- PipReentryState(@Nullable Size size, float snapFraction) {
- mSize = size;
+ PipReentryState(float boundsScale, float snapFraction) {
+ mBoundsScale = boundsScale;
mSnapFraction = snapFraction;
}
- @Nullable
- public Size getSize() {
- return mSize;
+ public float getBoundsScale() {
+ return mBoundsScale;
}
public float getSnapFraction() {
@@ -621,7 +620,7 @@
void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
- pw.println(innerPrefix + "mSize=" + mSize);
+ pw.println(innerPrefix + "mBoundsScale=" + mBoundsScale);
pw.println(innerPrefix + "mSnapFraction=" + mSnapFraction);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 4fe79c1..aded115 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -65,7 +65,7 @@
import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler;
import com.android.wm.shell.draganddrop.DragAndDropController;
-import com.android.wm.shell.draganddrop.UnhandledDragController;
+import com.android.wm.shell.draganddrop.GlobalDragListener;
import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.freeform.FreeformTaskTransitionHandler;
@@ -562,10 +562,10 @@
@WMSingleton
@Provides
- static UnhandledDragController provideUnhandledDragController(
+ static GlobalDragListener provideGlobalDragListener(
IWindowManager wmService,
@ShellMainThread ShellExecutor mainExecutor) {
- return new UnhandledDragController(wmService, mainExecutor);
+ return new GlobalDragListener(wmService, mainExecutor);
}
@WMSingleton
@@ -577,9 +577,12 @@
DisplayController displayController,
UiEventLogger uiEventLogger,
IconProvider iconProvider,
+ GlobalDragListener globalDragListener,
+ Transitions transitions,
@ShellMainThread ShellExecutor mainExecutor) {
- return new DragAndDropController(context, shellInit, shellController,
- shellCommandHandler, displayController, uiEventLogger, iconProvider, mainExecutor);
+ return new DragAndDropController(context, shellInit, shellController, shellCommandHandler,
+ displayController, uiEventLogger, iconProvider, globalDragListener, transitions,
+ mainExecutor);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 6de5d74..645ff06 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -255,7 +255,10 @@
2 -> {
// Split-screen case where there are two focused tasks, then we find the child
// task to move to desktop.
- val splitFocusedTask = findChildFocusedTask(allFocusedTasks)
+ val splitFocusedTask =
+ if (allFocusedTasks[0].taskId == allFocusedTasks[1].parentTaskId)
+ allFocusedTasks[1]
+ else allFocusedTasks[0]
moveToDesktop(splitFocusedTask)
}
1 -> {
@@ -263,21 +266,17 @@
moveToDesktop(allFocusedTasks[0].taskId)
}
else -> {
- KtProtoLog.v(
+ KtProtoLog.w(
WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: Cannot enter desktop expected less " +
- "than 3 focused tasks but found " + allFocusedTasks.size
+ "DesktopTasksController: Cannot enter desktop, expected less " +
+ "than 3 focused tasks but found %d",
+ allFocusedTasks.size
)
}
}
}
}
- private fun findChildFocusedTask(allFocusedTasks: List<RunningTaskInfo>): RunningTaskInfo {
- if (allFocusedTasks[0].taskId == allFocusedTasks[1].parentTaskId) return allFocusedTasks[1]
- return allFocusedTasks[0]
- }
-
/** Move a task with given `taskId` to desktop */
fun moveToDesktop(
taskId: Int,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 269c369..0fea3e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -35,6 +35,7 @@
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DRAG_AND_DROP;
+import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.content.ClipDescription;
import android.content.ComponentCallbacks2;
@@ -51,6 +52,7 @@
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
+import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
@@ -71,6 +73,7 @@
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -79,6 +82,7 @@
* Handles the global drag and drop handling for the Shell.
*/
public class DragAndDropController implements RemoteCallable<DragAndDropController>,
+ GlobalDragListener.GlobalDragListenerCallback,
DisplayController.OnDisplaysChangedListener,
View.OnDragListener, ComponentCallbacks2 {
@@ -90,6 +94,8 @@
private final DisplayController mDisplayController;
private final DragAndDropEventLogger mLogger;
private final IconProvider mIconProvider;
+ private final GlobalDragListener mGlobalDragListener;
+ private final Transitions mTransitions;
private SplitScreenController mSplitScreen;
private ShellExecutor mMainExecutor;
private ArrayList<DragAndDropListener> mListeners = new ArrayList<>();
@@ -112,6 +118,8 @@
DisplayController displayController,
UiEventLogger uiEventLogger,
IconProvider iconProvider,
+ GlobalDragListener globalDragListener,
+ Transitions transitions,
ShellExecutor mainExecutor) {
mContext = context;
mShellController = shellController;
@@ -119,6 +127,8 @@
mDisplayController = displayController;
mLogger = new DragAndDropEventLogger(uiEventLogger);
mIconProvider = iconProvider;
+ mGlobalDragListener = globalDragListener;
+ mTransitions = transitions;
mMainExecutor = mainExecutor;
shellInit.addInitCallback(this::onInit, this);
}
@@ -136,6 +146,7 @@
mShellController.addExternalInterface(KEY_EXTRA_SHELL_DRAG_AND_DROP,
this::createExternalInterface, this);
mShellCommandHandler.addDumpCallback(this::dump, this);
+ mGlobalDragListener.setListener(this);
}
private ExternalInterfaceBinder createExternalInterface() {
@@ -322,6 +333,14 @@
return true;
}
+ @Override
+ public void onCrossWindowDrop(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
+ // Bring the task forward when an item is dropped on it
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reorder(taskInfo.token, true /* onTop */);
+ mTransitions.startTransition(WindowManager.TRANSIT_TO_FRONT, wct, null);
+ }
+
/**
* Handles dropping on the drop target.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/UnhandledDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
similarity index 67%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/UnhandledDragController.kt
rename to libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
index ccf48d0..7f7cfb9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/UnhandledDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
@@ -15,12 +15,13 @@
*/
package com.android.wm.shell.draganddrop
+import android.app.ActivityManager
import android.os.RemoteException
import android.util.Log
import android.view.DragEvent
import android.view.IWindowManager
+import android.window.IGlobalDragListener
import android.window.IUnhandledDragCallback
-import android.window.IUnhandledDragListener
import androidx.annotation.VisibleForTesting
import com.android.internal.protolog.common.ProtoLog
import com.android.wm.shell.common.ShellExecutor
@@ -30,25 +31,36 @@
/**
* Manages the listener and callbacks for unhandled global drags.
*/
-class UnhandledDragController(
- val wmService: IWindowManager,
- mainExecutor: ShellExecutor
+class GlobalDragListener(
+ private val wmService: IWindowManager,
+ private val mainExecutor: ShellExecutor
) {
- private var callback: UnhandledDragAndDropCallback? = null
+ private var callback: GlobalDragListenerCallback? = null
- private val unhandledDragListener: IUnhandledDragListener =
- object : IUnhandledDragListener.Stub() {
+ private val globalDragListener: IGlobalDragListener =
+ object : IGlobalDragListener.Stub() {
+ override fun onCrossWindowDrop(taskInfo: ActivityManager.RunningTaskInfo) {
+ mainExecutor.execute() {
+ this@GlobalDragListener.onCrossWindowDrop(taskInfo)
+ }
+ }
+
override fun onUnhandledDrop(event: DragEvent, callback: IUnhandledDragCallback) {
mainExecutor.execute() {
- this@UnhandledDragController.onUnhandledDrop(event, callback)
+ this@GlobalDragListener.onUnhandledDrop(event, callback)
}
}
}
/**
- * Listener called when an unhandled drag is started.
+ * Callbacks for global drag events.
*/
- interface UnhandledDragAndDropCallback {
+ interface GlobalDragListenerCallback {
+ /**
+ * Called when a global drag is successfully handled by another window.
+ */
+ fun onCrossWindowDrop(taskInfo: ActivityManager.RunningTaskInfo) {}
+
/**
* Called when a global drag is unhandled (ie. dropped outside of all visible windows, or
* dropped on a window that does not want to handle it).
@@ -62,7 +74,7 @@
/**
* Sets a listener for callbacks when an unhandled drag happens.
*/
- fun setListener(listener: UnhandledDragAndDropCallback?) {
+ fun setListener(listener: GlobalDragListenerCallback?) {
val updateWm = (callback == null && listener != null)
|| (callback != null && listener == null)
callback = listener
@@ -71,8 +83,8 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
"%s unhandled drag listener",
if (callback != null) "Registering" else "Unregistering")
- wmService.setUnhandledDragListener(
- if (callback != null) unhandledDragListener else null)
+ wmService.setGlobalDragListener(
+ if (callback != null) globalDragListener else null)
} catch (e: RemoteException) {
Log.e(TAG, "Failed to set unhandled drag listener")
}
@@ -80,11 +92,19 @@
}
@VisibleForTesting
+ fun onCrossWindowDrop(taskInfo: ActivityManager.RunningTaskInfo) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "onCrossWindowDrop: %s", taskInfo)
+ callback?.onCrossWindowDrop(taskInfo)
+ }
+
+ @VisibleForTesting
fun onUnhandledDrop(dragEvent: DragEvent, wmCallback: IUnhandledDragCallback) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
"onUnhandledDrop: %s", dragEvent)
if (callback == null) {
wmCallback.notifyUnhandledDropComplete(false)
+ return
}
callback?.onUnhandledDrop(dragEvent) {
@@ -95,6 +115,6 @@
}
companion object {
- private val TAG = UnhandledDragController::class.java.simpleName
+ private val TAG = GlobalDragListener::class.java.simpleName
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index d173175..32442f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -191,7 +191,7 @@
try {
ActivityTaskManager.getService().onPictureInPictureUiStateChanged(
new PictureInPictureUiState.Builder()
- .setEnteringPip(true)
+ .setTransitioningToPip(true)
.build());
} catch (RemoteException | IllegalStateException e) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -210,7 +210,7 @@
try {
ActivityTaskManager.getService().onPictureInPictureUiStateChanged(
new PictureInPictureUiState.Builder()
- .setEnteringPip(false)
+ .setTransitioningToPip(false)
.build());
} catch (RemoteException | IllegalStateException e) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 4b12134..4684077 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -48,7 +48,6 @@
import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.Pair;
-import android.util.Size;
import android.view.DisplayInfo;
import android.view.InsetsState;
import android.view.SurfaceControl;
@@ -1042,22 +1041,7 @@
/** Save the state to restore to on re-entry. */
public void saveReentryState(Rect pipBounds) {
float snapFraction = mPipBoundsAlgorithm.getSnapFraction(pipBounds);
-
- if (!mPipBoundsState.hasUserResizedPip()) {
- mPipBoundsState.saveReentryState(null /* bounds */, snapFraction);
- return;
- }
-
- Size reentrySize = new Size(pipBounds.width(), pipBounds.height());
-
- // TODO: b/279937014 Investigate why userResizeBounds are empty with shell transitions on
- // fallback to using the userResizeBounds if userResizeBounds are not empty
- if (!mTouchHandler.getUserResizeBounds().isEmpty()) {
- Rect userResizeBounds = mTouchHandler.getUserResizeBounds();
- reentrySize = new Size(userResizeBounds.width(), userResizeBounds.height());
- }
-
- mPipBoundsState.saveReentryState(reentrySize, snapFraction);
+ mPipBoundsState.saveReentryState(snapFraction);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 5e79681..8a0e797 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -1170,7 +1170,11 @@
mPendingTransitions.add(0, active);
}
- /** Start a new transition directly. */
+ /**
+ * Start a new transition directly.
+ * @param handler if null, the transition will be dispatched to the registered set of transition
+ * handlers to be handled
+ */
public IBinder startTransition(@WindowManager.TransitionType int type,
@NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Directly starting a new transition "
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
new file mode 100644
index 0000000..8c0a524
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.wm.shell.flicker.pip
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test auto entering pip using a source rect hint.
+ *
+ * To run this test: `atest AutoEnterPipWithSourceRectHintTest`
+ *
+ * Actions:
+ * ```
+ * Launch an app in full screen
+ * Select "Auto-enter PiP" radio button
+ * Press "Set SourceRectHint" to create a temporary view that is used as the source rect hint
+ * Press Home button or swipe up to go Home and put [pipApp] in pip mode
+ * ```
+ *
+ * Notes:
+ * ```
+ * 1. All assertions are inherited from [AutoEnterPipOnGoToHomeTest]
+ * 2. Part of the test setup occurs automatically via
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ * ```
+ */
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class AutoEnterPipWithSourceRectHintTest(flicker: LegacyFlickerTest) :
+ AutoEnterPipOnGoToHomeTest(flicker) {
+ override val defaultEnterPip: FlickerBuilder.() -> Unit = {
+ setup {
+ pipApp.launchViaIntent(wmHelper)
+ pipApp.setSourceRectHint()
+ pipApp.enableAutoEnterForPipActivity()
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun pipOverlayNotShown() {
+ val overlay = ComponentNameMatcher.PIP_CONTENT_OVERLAY
+ flicker.assertLayers {
+ this.notContains(overlay)
+ }
+ }
+ @Presubmit
+ @Test
+ override fun pipOverlayLayerAppearThenDisappear() {
+ // we don't use overlay when entering with sourceRectHint
+ }
+
+ @Presubmit
+ @Test
+ override fun pipLayerOrOverlayRemainInsideVisibleBounds() {
+ // TODO (b/323511194): Looks like there is some bounciness with pip when using
+ // auto enter and sourceRectHint that causes the app to move outside of the display
+ // bounds during the transition.
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
index 54f36f6..a64ebd3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
@@ -45,12 +45,14 @@
import com.android.internal.logging.UiEventLogger;
import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
import org.junit.Test;
@@ -84,7 +86,9 @@
@Mock
private ShellExecutor mMainExecutor;
@Mock
- private WindowManager mWindowManager;
+ private Transitions mTransitions;
+ @Mock
+ private GlobalDragListener mGlobalDragListener;
private DragAndDropController mController;
@@ -93,7 +97,7 @@
MockitoAnnotations.initMocks(this);
mController = new DragAndDropController(mContext, mShellInit, mShellController,
mShellCommandHandler, mDisplayController, mUiEventLogger, mIconProvider,
- mMainExecutor);
+ mGlobalDragListener, mTransitions, mMainExecutor);
mController.onInit();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/UnhandledDragControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/GlobalDragListenerTest.kt
similarity index 65%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/UnhandledDragControllerTest.kt
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/GlobalDragListenerTest.kt
index 522f052..e731b06 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/UnhandledDragControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/GlobalDragListenerTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.draganddrop
+import android.app.ActivityManager.RunningTaskInfo
import android.os.RemoteException
import android.view.DragEvent
import android.view.DragEvent.ACTION_DROP
@@ -25,19 +26,17 @@
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.draganddrop.UnhandledDragController.UnhandledDragAndDropCallback
+import com.android.wm.shell.draganddrop.GlobalDragListener.GlobalDragListenerCallback
import java.util.function.Consumer
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
-import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.verify
/**
* Tests for the unhandled drag controller.
@@ -45,35 +44,31 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class UnhandledDragControllerTest : ShellTestCase() {
- @Mock
- private lateinit var mIWindowManager: IWindowManager
+ private val mIWindowManager = mock<IWindowManager>()
+ private val mMainExecutor = mock<ShellExecutor>()
- @Mock
- private lateinit var mMainExecutor: ShellExecutor
-
- private lateinit var mController: UnhandledDragController
+ private lateinit var mController: GlobalDragListener
@Before
@Throws(RemoteException::class)
fun setUp() {
- MockitoAnnotations.initMocks(this)
- mController = UnhandledDragController(mIWindowManager, mMainExecutor)
+ mController = GlobalDragListener(mIWindowManager, mMainExecutor)
}
@Test
fun setListener_registersUnregistersWithWM() {
- mController.setListener(object : UnhandledDragAndDropCallback {})
- mController.setListener(object : UnhandledDragAndDropCallback {})
- mController.setListener(object : UnhandledDragAndDropCallback {})
+ mController.setListener(object : GlobalDragListenerCallback {})
+ mController.setListener(object : GlobalDragListenerCallback {})
+ mController.setListener(object : GlobalDragListenerCallback {})
verify(mIWindowManager, Mockito.times(1))
- .setUnhandledDragListener(ArgumentMatchers.any())
+ .setGlobalDragListener(ArgumentMatchers.any())
reset(mIWindowManager)
mController.setListener(null)
mController.setListener(null)
mController.setListener(null)
verify(mIWindowManager, Mockito.times(1))
- .setUnhandledDragListener(ArgumentMatchers.isNull())
+ .setGlobalDragListener(ArgumentMatchers.isNull())
}
@Test
@@ -81,7 +76,7 @@
// Simulate an unhandled drop
val dropEvent = DragEvent.obtain(ACTION_DROP, 0f, 0f, 0f, 0f, null, null, null,
null, null, false)
- val wmCallback = mock(IUnhandledDragCallback::class.java)
+ val wmCallback = mock<IUnhandledDragCallback>()
mController.onUnhandledDrop(dropEvent, wmCallback)
verify(wmCallback).notifyUnhandledDropComplete(ArgumentMatchers.eq(false))
@@ -92,7 +87,7 @@
val lastDragEvent = arrayOfNulls<DragEvent>(1)
// Set a listener to listen for unhandled drops
- mController.setListener(object : UnhandledDragAndDropCallback {
+ mController.setListener(object : GlobalDragListenerCallback {
override fun onUnhandledDrop(dragEvent: DragEvent,
onFinishedCallback: Consumer<Boolean>) {
lastDragEvent[0] = dragEvent
@@ -102,14 +97,31 @@
})
// Simulate an unhandled drop
- val dragSurface = mock(SurfaceControl::class.java)
+ val dragSurface = mock<SurfaceControl>()
val dropEvent = DragEvent.obtain(ACTION_DROP, 0f, 0f, 0f, 0f, null, null, null,
dragSurface, null, false)
- val wmCallback = mock(IUnhandledDragCallback::class.java)
+ val wmCallback = mock<IUnhandledDragCallback>()
mController.onUnhandledDrop(dropEvent, wmCallback)
verify(wmCallback).notifyUnhandledDropComplete(ArgumentMatchers.eq(true))
verify(dragSurface).release()
assertEquals(lastDragEvent.get(0), dropEvent)
}
+
+ @Test
+ fun onCrossWindowDrop() {
+ val lastTaskInfo = arrayOfNulls<RunningTaskInfo>(1)
+
+ // Set a listener to listen for unhandled drops
+ mController.setListener(object : GlobalDragListenerCallback {
+ override fun onCrossWindowDrop(taskInfo: RunningTaskInfo) {
+ lastTaskInfo[0] = taskInfo
+ }
+ })
+
+ // Simulate a cross-window drop
+ val taskInfo = mock<RunningTaskInfo>()
+ mController.onCrossWindowDrop(taskInfo)
+ assertEquals(lastTaskInfo.get(0), taskInfo)
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
index 46259a8..080b0ae 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
@@ -354,14 +354,17 @@
}
@Test
- public void getEntryDestinationBounds_reentryStateExists_restoreLastSize() {
+ public void getEntryDestinationBounds_reentryStateExists_restoreProportionalSize() {
mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+ final Size maxSize = mSizeSpecSource.getMaxSize(DEFAULT_ASPECT_RATIO);
+ mPipBoundsState.setMaxSize(maxSize.getWidth(), maxSize.getHeight());
final Rect reentryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
reentryBounds.scale(1.25f);
+ mPipBoundsState.setBounds(reentryBounds); // this updates the bounds scale used in reentry
+
final float reentrySnapFraction = mPipBoundsAlgorithm.getSnapFraction(reentryBounds);
- mPipBoundsState.saveReentryState(
- new Size(reentryBounds.width(), reentryBounds.height()), reentrySnapFraction);
+ mPipBoundsState.saveReentryState(reentrySnapFraction);
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
assertEquals(reentryBounds.width(), destinationBounds.width());
@@ -375,8 +378,7 @@
reentryBounds.offset(0, -100);
final float reentrySnapFraction = mPipBoundsAlgorithm.getSnapFraction(reentryBounds);
- mPipBoundsState.saveReentryState(
- new Size(reentryBounds.width(), reentryBounds.height()), reentrySnapFraction);
+ mPipBoundsState.saveReentryState(reentrySnapFraction);
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
index db98abb..304da75f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -114,22 +114,19 @@
@Test
public void testSetReentryState() {
- final Size size = new Size(100, 100);
final float snapFraction = 0.5f;
- mPipBoundsState.saveReentryState(size, snapFraction);
+ mPipBoundsState.saveReentryState(snapFraction);
final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState();
- assertEquals(size, state.getSize());
assertEquals(snapFraction, state.getSnapFraction(), 0.01);
}
@Test
public void testClearReentryState() {
- final Size size = new Size(100, 100);
final float snapFraction = 0.5f;
- mPipBoundsState.saveReentryState(size, snapFraction);
+ mPipBoundsState.saveReentryState(snapFraction);
mPipBoundsState.clearReentryState();
assertNull(mPipBoundsState.getReentryState());
@@ -138,20 +135,19 @@
@Test
public void testSetLastPipComponentName_notChanged_doesNotClearReentryState() {
mPipBoundsState.setLastPipComponentName(mTestComponentName1);
- mPipBoundsState.saveReentryState(DEFAULT_SIZE, DEFAULT_SNAP_FRACTION);
+ mPipBoundsState.saveReentryState(DEFAULT_SNAP_FRACTION);
mPipBoundsState.setLastPipComponentName(mTestComponentName1);
final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState();
assertNotNull(state);
- assertEquals(DEFAULT_SIZE, state.getSize());
assertEquals(DEFAULT_SNAP_FRACTION, state.getSnapFraction(), 0.01);
}
@Test
public void testSetLastPipComponentName_changed_clearReentryState() {
mPipBoundsState.setLastPipComponentName(mTestComponentName1);
- mPipBoundsState.saveReentryState(DEFAULT_SIZE, DEFAULT_SNAP_FRACTION);
+ mPipBoundsState.saveReentryState(DEFAULT_SNAP_FRACTION);
mPipBoundsState.setLastPipComponentName(mTestComponentName2);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 4eb5193..5d968d3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -45,7 +45,6 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.Size;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.WindowManagerShellWrapper;
@@ -256,40 +255,13 @@
}
@Test
- public void saveReentryState_noUserResize_doesNotSaveSize() {
+ public void saveReentryState_savesPipBoundsState() {
final Rect bounds = new Rect(0, 0, 10, 10);
when(mMockPipBoundsAlgorithm.getSnapFraction(bounds)).thenReturn(1.0f);
- when(mMockPipBoundsState.hasUserResizedPip()).thenReturn(false);
mPipController.saveReentryState(bounds);
- verify(mMockPipBoundsState).saveReentryState(null, 1.0f);
- }
-
- @Test
- public void saveReentryState_nonEmptyUserResizeBounds_savesSize() {
- final Rect bounds = new Rect(0, 0, 10, 10);
- final Rect resizedBounds = new Rect(0, 0, 30, 30);
- when(mMockPipBoundsAlgorithm.getSnapFraction(bounds)).thenReturn(1.0f);
- when(mMockPipTouchHandler.getUserResizeBounds()).thenReturn(resizedBounds);
- when(mMockPipBoundsState.hasUserResizedPip()).thenReturn(true);
-
- mPipController.saveReentryState(bounds);
-
- verify(mMockPipBoundsState).saveReentryState(new Size(30, 30), 1.0f);
- }
-
- @Test
- public void saveReentryState_emptyUserResizeBounds_savesSize() {
- final Rect bounds = new Rect(0, 0, 10, 10);
- final Rect resizedBounds = new Rect(0, 0, 0, 0);
- when(mMockPipBoundsAlgorithm.getSnapFraction(bounds)).thenReturn(1.0f);
- when(mMockPipTouchHandler.getUserResizeBounds()).thenReturn(resizedBounds);
- when(mMockPipBoundsState.hasUserResizedPip()).thenReturn(true);
-
- mPipController.saveReentryState(bounds);
-
- verify(mMockPipBoundsState).saveReentryState(new Size(10, 10), 1.0f);
+ verify(mMockPipBoundsState).saveReentryState(1.0f);
}
@Test
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index b662901..0222782 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -697,6 +697,19 @@
});
}
+ private void onDisconnectRequested(ServiceCallbacks callback) {
+ mHandler.post(
+ () -> {
+ Log.i(TAG, "onDisconnectRequest for " + mServiceComponent);
+
+ if (!isCurrent(callback, "onDisconnectRequest")) {
+ return;
+ }
+ forceCloseConnection();
+ mCallback.onDisconnected();
+ });
+ }
+
/**
* Return true if {@code callback} is the current ServiceCallbacks. Also logs if it's not.
*/
@@ -880,6 +893,19 @@
*/
public void onConnectionFailed() {
}
+
+ /**
+ * Invoked after disconnecting by request of the {@link MediaBrowserService}.
+ *
+ * <p>The default implementation of this method calls {@link #onConnectionFailed()}.
+ *
+ * @hide
+ */
+ // TODO: b/185136506 - Consider publishing this API in the next window for API changes, if
+ // the need arises.
+ public void onDisconnected() {
+ onConnectionFailed();
+ }
}
/**
@@ -1112,6 +1138,14 @@
mediaBrowser.onLoadChildren(this, parentId, list, options);
}
}
+
+ @Override
+ public void onDisconnect() {
+ MediaBrowser mediaBrowser = mMediaBrowser.get();
+ if (mediaBrowser != null) {
+ mediaBrowser.onDisconnectRequested(this);
+ }
+ }
}
private static class Subscription {
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 8dba040..6cf9c6f 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -104,3 +104,10 @@
description: "Enable new MediaRouter2 API to enable watch companion apps to scan while the phone screen is off."
bug: "281072508"
}
+
+flag {
+ name: "enable_null_session_in_media_browser_service"
+ namespace: "media_solutions"
+ description: "Enables apps owning a MediaBrowserService to disconnect all connected browsers."
+ bug: "263520343"
+}
diff --git a/media/java/android/media/midi/package.html b/media/java/android/media/midi/package.html
index ae0c2ab..45b4370 100644
--- a/media/java/android/media/midi/package.html
+++ b/media/java/android/media/midi/package.html
@@ -478,7 +478,7 @@
<intent-filter>
<action android:name="android.media.midi.MidiUmpDeviceService" />
</intent-filter>
- <meta-data android:name="android.media.midi.MidiUmpDeviceService"
+ <property android:name="android.media.midi.MidiUmpDeviceService"
android:resource="@xml/<strong>echo_device_info</strong>" />
</service>
</pre>
diff --git a/media/java/android/media/tv/SignalingDataResponse.java b/media/java/android/media/tv/SignalingDataResponse.java
index be172ec..51fa6a2 100644
--- a/media/java/android/media/tv/SignalingDataResponse.java
+++ b/media/java/android/media/tv/SignalingDataResponse.java
@@ -73,6 +73,10 @@
/**
* Gets a list of types of metadata that are contained in this response.
*
+ * <p> This list correlates to all the available types that can be found within
+ * {@link #getSignalingDataInfoList()}. This list is determined by the types specified in
+ * {@link SignalingDataRequest#getSignalingDataTypes()}.
+ *
* <p> A list of types available are defined in {@link SignalingDataRequest}.
* For more information about these types, see A/344:2023-5 9.2.10 - Query Signaling Data API.
*
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index f332f81..84d08db 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -964,7 +964,11 @@
/**
* Called when the TV App sends the selected track info as a response to
- * {@link #requestSelectedTrackInfo()}
+ * {@link #requestSelectedTrackInfo()}.
+ *
+ * <p> When a selected track changes as a result of a new selection,
+ * {@link #onTrackSelected(int, String)} should be used instead to communicate the specific
+ * track selection.
*
* @param tracks A list of {@link TvTrackInfo} that are currently selected
*/
@@ -1383,6 +1387,8 @@
* <p> Normally, track info cannot be synchronized until the channel has
* been changed. This is used when the session of the {@link TvInteractiveAppService}
* is newly created and the normal synchronization has not happened yet.
+ *
+ * <p> The track info will be returned in {@link #onSelectedTrackInfo(List)}
*/
@FlaggedApi(Flags.FLAG_TIAF_V_APIS)
@CallSuper
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 29a3b98..635572d 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -585,7 +585,8 @@
}
/**
- * Sends the currently selected track info to the TV Interactive App.
+ * Sends the currently selected track info to the TV Interactive App in response to a
+ * {@link TvInteractiveAppCallback#onRequestSelectedTrackInfo(String)} request.
*
* @param tracks list of {@link TvTrackInfo} of the currently selected track(s)
*/
diff --git a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
index a877207..fbb7cfd 100644
--- a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
+++ b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
@@ -24,4 +24,11 @@
@UnsupportedAppUsage
void onConnectFailed();
void onLoadChildren(String mediaId, in ParceledListSlice list, in Bundle options);
+ /**
+ * Invoked when the browser service cuts off the connection with the browser.
+ *
+ * <p>The browser must also clean up any state associated with this connection, as if the
+ * service had been destroyed.
+ */
+ void onDisconnect();
}
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index d17b341..39ef528 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -38,10 +38,13 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
+import com.android.media.flags.Flags;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -51,6 +54,7 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
/**
* Base class for media browser services.
@@ -96,6 +100,7 @@
private static final int RESULT_ERROR = -1;
private static final int RESULT_OK = 0;
+ private final ServiceBinder mBinder;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -105,7 +110,7 @@
private final Handler mHandler = new Handler();
- private final ServiceState mServiceState = new ServiceState();
+ private final AtomicReference<ServiceState> mServiceState;
// Holds the connection record associated with the currently executing callback operation, if
// any. See getCurrentBrowserInfo for an example. Must only be accessed on mHandler.
@@ -216,16 +221,21 @@
}
private static class ServiceBinder extends IMediaBrowserService.Stub {
- private WeakReference<ServiceState> mServiceState;
+ private final AtomicReference<WeakReference<ServiceState>> mServiceState;
private ServiceBinder(ServiceState serviceState) {
- mServiceState = new WeakReference(serviceState);
+ mServiceState = new AtomicReference<>();
+ setServiceState(serviceState);
+ }
+
+ public void setServiceState(ServiceState serviceState) {
+ mServiceState.set(new WeakReference<>(serviceState));
}
@Override
public void connect(final String pkg, final Bundle rootHints,
final IMediaBrowserServiceCallbacks callbacks) {
- ServiceState serviceState = mServiceState.get();
+ ServiceState serviceState = mServiceState.get().get();
if (serviceState == null) {
return;
}
@@ -243,7 +253,7 @@
@Override
public void disconnect(final IMediaBrowserServiceCallbacks callbacks) {
- ServiceState serviceState = mServiceState.get();
+ ServiceState serviceState = mServiceState.get().get();
if (serviceState == null) {
return;
}
@@ -260,7 +270,7 @@
@Override
public void addSubscription(final String id, final IBinder token, final Bundle options,
final IMediaBrowserServiceCallbacks callbacks) {
- ServiceState serviceState = mServiceState.get();
+ ServiceState serviceState = mServiceState.get().get();
if (serviceState == null) {
return;
}
@@ -278,7 +288,7 @@
@Override
public void removeSubscription(final String id, final IBinder token,
final IMediaBrowserServiceCallbacks callbacks) {
- ServiceState serviceState = mServiceState.get();
+ ServiceState serviceState = mServiceState.get().get();
if (serviceState == null) {
return;
}
@@ -294,7 +304,7 @@
@Override
public void getMediaItem(final String mediaId, final ResultReceiver receiver,
final IMediaBrowserServiceCallbacks callbacks) {
- ServiceState serviceState = mServiceState.get();
+ ServiceState serviceState = mServiceState.get().get();
if (serviceState == null) {
return;
}
@@ -304,17 +314,23 @@
}
}
+ /** Default constructor. */
+ public MediaBrowserService() {
+ mServiceState = new AtomicReference<>(new ServiceState());
+ mBinder = new ServiceBinder(mServiceState.get());
+ }
+
@Override
public void onCreate() {
super.onCreate();
- mServiceState.mBinder = new ServiceBinder(mServiceState);
}
@Override
public IBinder onBind(Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction())) {
- return mServiceState.mBinder;
+ return mBinder;
}
+
return null;
}
@@ -428,21 +444,33 @@
/**
* Call to set the media session.
- * <p>
- * This should be called as soon as possible during the service's startup.
- * It may only be called once.
+ *
+ * <p>This should be called as soon as possible during the service's startup. It may only be
+ * called once.
*
* @param token The token for the service's {@link MediaSession}.
*/
+ // TODO: b/185136506 - Update the javadoc to reflect API changes when
+ // enableNullSessionInMediaBrowserService makes it to nextfood.
public void setSessionToken(final MediaSession.Token token) {
+ ServiceState serviceState = mServiceState.get();
if (token == null) {
- throw new IllegalArgumentException("Session token may not be null.");
- }
- if (mServiceState.mSession != null) {
+ if (!Flags.enableNullSessionInMediaBrowserService()) {
+ throw new IllegalArgumentException("Session token may not be null.");
+ } else if (serviceState.mSession != null) {
+ ServiceState newServiceState = new ServiceState();
+ mBinder.setServiceState(newServiceState);
+ mServiceState.set(newServiceState);
+ serviceState.release();
+ } else {
+ // Nothing to do. The session is already null.
+ }
+ } else if (serviceState.mSession != null) {
throw new IllegalStateException("The session token has already been set.");
+ } else {
+ serviceState.mSession = token;
+ mHandler.post(() -> serviceState.notifySessionTokenInitializedOnHandler(token));
}
- mServiceState.mSession = token;
- mHandler.post(() -> mServiceState.notifySessionTokenInitializedOnHandler(token));
}
/**
@@ -450,7 +478,7 @@
* or if it has been destroyed.
*/
public @Nullable MediaSession.Token getSessionToken() {
- return mServiceState.mSession;
+ return mServiceState.get().mSession;
}
/**
@@ -521,7 +549,7 @@
if (parentId == null) {
throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
}
- mHandler.post(() -> mServiceState.notifyChildrenChangeOnHandler(parentId, options));
+ mHandler.post(() -> mServiceState.get().notifyChildrenChangeOnHandler(parentId, options));
}
/**
@@ -623,15 +651,38 @@
// Fields accessed from any caller thread.
@Nullable private MediaSession.Token mSession;
- @Nullable private ServiceBinder mBinder;
// Fields accessed from mHandler only.
@NonNull private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
+ public ServiceBinder getBinder() {
+ return mBinder;
+ }
+
public void postOnHandler(Runnable runnable) {
mHandler.post(runnable);
}
+ public void release() {
+ mHandler.postAtFrontOfQueue(this::clearConnectionsOnHandler);
+ }
+
+ private void clearConnectionsOnHandler() {
+ Iterator<ConnectionRecord> iterator = mConnections.values().iterator();
+ while (iterator.hasNext()) {
+ ConnectionRecord record = iterator.next();
+ iterator.remove();
+ try {
+ record.callbacks.onDisconnect();
+ } catch (RemoteException exception) {
+ Log.w(
+ TAG,
+ TextUtils.formatSimple("onDisconnectRequest for %s failed", record.pkg),
+ exception);
+ }
+ }
+ }
+
public void removeConnectionRecordOnHandler(IMediaBrowserServiceCallbacks callbacks) {
IBinder b = callbacks.asBinder();
// Clear out the old subscriptions. We are getting new ones.
@@ -796,8 +847,7 @@
@Override
void onResultSent(
List<MediaBrowser.MediaItem> list, @ResultFlags int flag) {
- if (mServiceState.mConnections.get(connection.callbacks.asBinder())
- != connection) {
+ if (mConnections.get(connection.callbacks.asBinder()) != connection) {
if (DBG) {
Log.d(
TAG,
diff --git a/native/android/input.cpp b/native/android/input.cpp
index 6efb028..4708e69 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -87,7 +87,7 @@
const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent) {
std::unique_ptr<KeyEvent> event = std::make_unique<KeyEvent>();
- *event = android::android_view_KeyEvent_toNative(env, keyEvent);
+ *event = android::android_view_KeyEvent_obtainAsCopy(env, keyEvent);
return event.release();
}
@@ -323,9 +323,9 @@
static_cast<const MotionEvent&>(
*aInputEvent));
case AINPUT_EVENT_TYPE_KEY:
- return android::android_view_KeyEvent_fromNative(env,
- static_cast<const KeyEvent&>(
- *aInputEvent));
+ return android::android_view_KeyEvent_obtainAsCopy(env,
+ static_cast<const KeyEvent&>(
+ *aInputEvent));
default:
LOG_ALWAYS_FATAL("Unexpected event type %d in AInputEvent_toJava.", eventType);
}
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index f111327..e244520 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -228,20 +228,10 @@
method public final android.os.IBinder onBind(android.content.Intent);
method public abstract void onDeactivated(int);
method public abstract byte[] processCommandApdu(byte[], android.os.Bundle);
- method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void processPollingFrames(@NonNull java.util.List<android.os.Bundle>);
+ method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void processPollingFrames(@NonNull java.util.List<android.nfc.cardemulation.PollingFrame>);
method public final void sendResponseApdu(byte[]);
field public static final int DEACTIVATION_DESELECTED = 1; // 0x1
field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String KEY_POLLING_LOOP_DATA = "android.nfc.cardemulation.DATA";
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String KEY_POLLING_LOOP_GAIN = "android.nfc.cardemulation.GAIN";
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String KEY_POLLING_LOOP_TIMESTAMP = "android.nfc.cardemulation.TIMESTAMP";
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String KEY_POLLING_LOOP_TYPE = "android.nfc.cardemulation.TYPE";
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_A = 65; // 0x0041 'A'
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_B = 66; // 0x0042 'B'
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_F = 70; // 0x0046 'F'
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_OFF = 88; // 0x0058 'X'
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_ON = 79; // 0x004f 'O'
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_UNKNOWN = 85; // 0x0055 'U'
field public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_APDU_SERVICE";
field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.host_apdu_service";
}
@@ -274,6 +264,23 @@
field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.off_host_apdu_service";
}
+ @FlaggedApi("android.nfc.nfc_read_polling_loop") public final class PollingFrame implements android.os.Parcelable {
+ ctor public PollingFrame(int, @Nullable byte[], int, int);
+ method public int describeContents();
+ method @NonNull public byte[] getData();
+ method public int getGain();
+ method public int getTimestamp();
+ method public int getType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.PollingFrame> CREATOR;
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_A = 65; // 0x41
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_B = 66; // 0x42
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_F = 70; // 0x46
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_OFF = 88; // 0x58
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_ON = 79; // 0x4f
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_UNKNOWN = 85; // 0x55
+ }
+
}
package android.nfc.tech {
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index c5b7582..e5752d1 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -38,6 +38,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
+import android.nfc.cardemulation.PollingFrame;
import android.nfc.tech.MifareClassic;
import android.nfc.tech.Ndef;
import android.nfc.tech.NfcA;
@@ -2799,7 +2800,8 @@
*/
@TestApi
@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
- public void notifyPollingLoop(@NonNull Bundle frame) {
+ public void notifyPollingLoop(@NonNull PollingFrame pollingFrame) {
+ Bundle frame = pollingFrame.toBundle();
try {
if (sService == null) {
attemptDeadServiceRecovery(null);
diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java
index 363788e..61037a2 100644
--- a/nfc/java/android/nfc/cardemulation/HostApduService.java
+++ b/nfc/java/android/nfc/cardemulation/HostApduService.java
@@ -244,85 +244,6 @@
public static final String KEY_DATA = "data";
/**
- * KEY_POLLING_LOOP_TYPE is the Bundle key for the type of
- * polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final String KEY_POLLING_LOOP_TYPE = "android.nfc.cardemulation.TYPE";
-
- /**
- * POLLING_LOOP_TYPE_A is the value associated with the key
- * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
- * when the polling loop is for NFC-A.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final char POLLING_LOOP_TYPE_A = 'A';
-
- /**
- * POLLING_LOOP_TYPE_B is the value associated with the key
- * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
- * when the polling loop is for NFC-B.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final char POLLING_LOOP_TYPE_B = 'B';
-
- /**
- * POLLING_LOOP_TYPE_F is the value associated with the key
- * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
- * when the polling loop is for NFC-F.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final char POLLING_LOOP_TYPE_F = 'F';
-
- /**
- * POLLING_LOOP_TYPE_ON is the value associated with the key
- * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
- * when the polling loop turns on.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final char POLLING_LOOP_TYPE_ON = 'O';
-
- /**
- * POLLING_LOOP_TYPE_OFF is the value associated with the key
- * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
- * when the polling loop turns off.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final char POLLING_LOOP_TYPE_OFF = 'X';
-
- /**
- * POLLING_LOOP_TYPE_UNKNOWN is the value associated with the key
- * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
- * when the polling loop frame isn't recognized.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final char POLLING_LOOP_TYPE_UNKNOWN = 'U';
-
- /**
- * KEY_POLLING_LOOP_DATA is the Bundle key for the raw data of captured from
- * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
- * when the frame type isn't recognized.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final String KEY_POLLING_LOOP_DATA = "android.nfc.cardemulation.DATA";
-
- /**
- * KEY_POLLING_LOOP_GAIN is the Bundle key for the field strength of
- * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
- * when the frame type isn't recognized.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final String KEY_POLLING_LOOP_GAIN = "android.nfc.cardemulation.GAIN";
-
- /**
- * KEY_POLLING_LOOP_TIMESTAMP is the Bundle key for the timestamp of
- * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
- * when the frame type isn't recognized.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final String KEY_POLLING_LOOP_TIMESTAMP = "android.nfc.cardemulation.TIMESTAMP";
-
- /**
* @hide
*/
public static final String KEY_POLLING_LOOP_FRAMES_BUNDLE =
@@ -407,7 +328,12 @@
ArrayList<Bundle> frames =
msg.getData().getParcelableArrayList(KEY_POLLING_LOOP_FRAMES_BUNDLE,
Bundle.class);
- processPollingFrames(frames);
+ ArrayList<PollingFrame> pollingFrames =
+ new ArrayList<PollingFrame>(frames.size());
+ for (Bundle frame : frames) {
+ pollingFrames.add(new PollingFrame(frame));
+ }
+ processPollingFrames(pollingFrames);
break;
default:
super.handleMessage(msg);
@@ -482,7 +408,7 @@
* @param frame A description of the polling frame.
*/
@FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public void processPollingFrames(@NonNull List<Bundle> frame) {
+ public void processPollingFrames(@NonNull List<PollingFrame> frame) {
}
/**
diff --git a/nfc/java/android/nfc/cardemulation/PollingFrame.java b/nfc/java/android/nfc/cardemulation/PollingFrame.java
new file mode 100644
index 0000000..3383f3b
--- /dev/null
+++ b/nfc/java/android/nfc/cardemulation/PollingFrame.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2023 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 android.nfc.cardemulation;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HexFormat;
+import java.util.List;
+
+/**
+ * Polling Frames represent data about individual frames of an NFC polling loop. These frames will
+ * be deliverd to subclasses of {@link HostApduService} that have registered filters with
+ * {@link CardEmulation#registerPollingLoopFilterForService(ComponentName, String)} that match a
+ * given frame in a loop and will be delivered through calls to
+ * {@link HostApduService#processPollingFrames(List)}.
+ */
+@FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+public final class PollingFrame implements Parcelable{
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "POLLING_LOOP_TYPE_"}, value = { POLLING_LOOP_TYPE_A, POLLING_LOOP_TYPE_B,
+ POLLING_LOOP_TYPE_F, POLLING_LOOP_TYPE_OFF, POLLING_LOOP_TYPE_ON })
+ @Retention(RetentionPolicy.SOURCE)
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public @interface PollingFrameType {}
+
+ /**
+ * POLLING_LOOP_TYPE_A is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
+ * when the polling loop is for NFC-A.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int POLLING_LOOP_TYPE_A = 'A';
+
+ /**
+ * POLLING_LOOP_TYPE_B is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
+ * when the polling loop is for NFC-B.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int POLLING_LOOP_TYPE_B = 'B';
+
+ /**
+ * POLLING_LOOP_TYPE_F is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
+ * when the polling loop is for NFC-F.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int POLLING_LOOP_TYPE_F = 'F';
+
+ /**
+ * POLLING_LOOP_TYPE_ON is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
+ * when the polling loop turns on.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int POLLING_LOOP_TYPE_ON = 'O';
+
+ /**
+ * POLLING_LOOP_TYPE_OFF is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
+ * when the polling loop turns off.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int POLLING_LOOP_TYPE_OFF = 'X';
+
+ /**
+ * POLLING_LOOP_TYPE_UNKNOWN is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
+ * when the polling loop frame isn't recognized.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int POLLING_LOOP_TYPE_UNKNOWN = 'U';
+
+ /**
+ * KEY_POLLING_LOOP_TYPE is the Bundle key for the type of
+ * polling loop frame in the Bundle included in MSG_POLLING_LOOP.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String KEY_POLLING_LOOP_TYPE = "android.nfc.cardemulation.TYPE";
+
+ /**
+ * KEY_POLLING_LOOP_DATA is the Bundle key for the raw data of captured from
+ * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String KEY_POLLING_LOOP_DATA = "android.nfc.cardemulation.DATA";
+
+ /**
+ * KEY_POLLING_LOOP_GAIN is the Bundle key for the field strength of
+ * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String KEY_POLLING_LOOP_GAIN = "android.nfc.cardemulation.GAIN";
+
+ /**
+ * KEY_POLLING_LOOP_TIMESTAMP is the Bundle key for the timestamp of
+ * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String KEY_POLLING_LOOP_TIMESTAMP = "android.nfc.cardemulation.TIMESTAMP";
+
+
+ @PollingFrameType
+ private final int mType;
+ private final byte[] mData;
+ private final int mGain;
+ private final int mTimestamp;
+
+ public static final @NonNull Parcelable.Creator<PollingFrame> CREATOR =
+ new Parcelable.Creator<>() {
+ @Override
+ public PollingFrame createFromParcel(Parcel source) {
+ return new PollingFrame(source.readBundle());
+ }
+
+ @Override
+ public PollingFrame[] newArray(int size) {
+ return new PollingFrame[size];
+ }
+ };
+
+ PollingFrame(Bundle frame) {
+ mType = frame.getInt(KEY_POLLING_LOOP_TYPE);
+ byte[] data = frame.getByteArray(KEY_POLLING_LOOP_DATA);
+ mData = (data == null) ? new byte[0] : data;
+ mGain = frame.getByte(KEY_POLLING_LOOP_GAIN);
+ mTimestamp = frame.getInt(KEY_POLLING_LOOP_TIMESTAMP);
+ }
+
+ public PollingFrame(@PollingFrameType int type, @Nullable byte[] data,
+ int gain, int timestamp) {
+ mType = type;
+ mData = data == null ? new byte[0] : data;
+ mGain = gain;
+ mTimestamp = timestamp;
+ }
+
+ /**
+ * Returns the type of frame for this polling loop frame.
+ * The possible return values are:
+ * <ul>
+ * <li>{@link POLLING_LOOP_TYPE_ON}</li>
+ * <li>{@link POLLING_LOOP_TYPE_OFF}</li>
+ * <li>{@link POLLING_LOOP_TYPE_A}</li>
+ * <li>{@link POLLING_LOOP_TYPE_B}</li>
+ * <li>{@link POLLING_LOOP_TYPE_F}</li>
+ * </ul>
+ */
+ public @PollingFrameType int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the raw data from the polling type frame.
+ */
+ public @NonNull byte[] getData() {
+ return mData;
+ }
+
+ /**
+ * Returns the gain representing the field strength of the NFC field when this polling loop
+ * frame was observed.
+ */
+ public int getGain() {
+ return mGain;
+ }
+
+ /**
+ * Returns the timestamp of when the polling loop frame was observed in milliseconds. These
+ * timestamps are relative and not absolute and should only be used fro comparing the timing of
+ * frames relative to each other.
+ * @return the timestamp in milliseconds
+ */
+ public int getTimestamp() {
+ return mTimestamp;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeBundle(toBundle());
+ }
+
+ /**
+ *
+ * @hide
+ * @return a Bundle representing this frame
+ */
+ public Bundle toBundle() {
+ Bundle frame = new Bundle();
+ frame.putInt(KEY_POLLING_LOOP_TYPE, getType());
+ frame.putByte(KEY_POLLING_LOOP_GAIN, (byte) getGain());
+ frame.putByteArray(KEY_POLLING_LOOP_DATA, getData());
+ frame.putInt(KEY_POLLING_LOOP_TIMESTAMP, getTimestamp());
+ return frame;
+ }
+
+ @Override
+ public String toString() {
+ return "PollingFrame { Type: " + (char) getType()
+ + ", gain: " + getGain()
+ + ", timestamp: " + Integer.toUnsignedString(getTimestamp())
+ + ", data: [" + HexFormat.ofDelimiter(" ").formatHex(getData()) + "] }";
+ }
+}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt
index 6f70cfb..6902b6f 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt
@@ -31,10 +31,7 @@
fun updateRequest(intent: Intent)
/** Sends an error encountered during the UI. */
- fun sendError(
- @BaseDialogResult.ResultCode resultCode: Int,
- errorMessage: String? = null,
- )
+ fun sendError(@BaseDialogResult.ResultCode resultCode: Int)
/**
* Sends a response to the system service. The response
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
index 7f59ba3..ab70394 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
@@ -48,9 +48,13 @@
override fun updateRequest(intent: Intent) {
- val request = intent.parse(
- context = context,
- )
+ val request: Request
+ try {
+ request = intent.parse(context)
+ } catch (e: Exception) {
+ sendError(BaseDialogResult.RESULT_CODE_DATA_PARSING_FAILURE)
+ return
+ }
Log.d(TAG, "Request parsed: $request, client instance: $this")
if (request is Request.Cancel || request is Request.Close) {
if (request.token != null && request.token != _requests.value?.token) {
@@ -61,8 +65,9 @@
_requests.value = request
}
- override fun sendError(resultCode: Int, errorMessage: String?) {
- TODO("b/300422310 - [Wear] Implement UI for cancellation request with message")
+ override fun sendError(resultCode: Int) {
+ Log.w(TAG, "Error occurred, resultCode: $resultCode, current request: ${ requests.value }")
+ requests.value?.sendCancellationCode(resultCode)
}
override fun sendResult(result: UserSelectionDialogResult) {
@@ -108,7 +113,7 @@
return entryInfo.shouldTerminateUiUponSuccessfulProviderResult
}
- private fun Request.Get.sendCancellationCode(cancelCode: Int) {
+ private fun Request.sendCancellationCode(cancelCode: Int) {
sendCancellationCode(
cancelCode = cancelCode,
requestToken = token,
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
index 4a1c260..fd99275 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
@@ -25,6 +25,8 @@
*/
sealed class Request private constructor(
open val token: IBinder?,
+ open val resultReceiver: ResultReceiver? = null,
+ open val finalResponseReceiver: ResultReceiver? = null,
) {
/**
@@ -48,10 +50,10 @@
*/
data class Get(
override val token: IBinder?,
- val resultReceiver: ResultReceiver?,
- val finalResponseReceiver: ResultReceiver?,
+ override val resultReceiver: ResultReceiver?,
+ override val finalResponseReceiver: ResultReceiver?,
val providerInfos: List<ProviderInfo>,
- ) : Request(token)
+ ) : Request(token, resultReceiver, finalResponseReceiver)
/**
* Request to start the create credentials flow.
*/
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
index 6bd166e..8c5c085 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
@@ -19,5 +19,6 @@
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
+/** [Application] of credential selector. */
@HiltAndroidApp(Application::class)
class CredentialSelectorApp : Hilt_CredentialSelectorApp()
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index bfe80c4..463c4d1 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -17,6 +17,7 @@
package com.android.credentialmanager
import android.content.Intent
+import android.credentials.selection.BaseDialogResult
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.android.credentialmanager.CredentialSelectorUiState.Get
@@ -28,6 +29,10 @@
import com.android.credentialmanager.model.get.CredentialEntryInfo
import com.android.credentialmanager.ui.mappers.toGet
import android.util.Log
+import com.android.credentialmanager.CredentialSelectorUiState.Cancel
+import com.android.credentialmanager.CredentialSelectorUiState.Close
+import com.android.credentialmanager.CredentialSelectorUiState.Create
+import com.android.credentialmanager.CredentialSelectorUiState.Idle
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -50,21 +55,21 @@
) { request, isPrimary, shouldClose ->
if (shouldClose) {
Log.d(TAG, "Request finished, closing ")
- return@combine CredentialSelectorUiState.Close
+ return@combine Close
}
when (request) {
- null -> CredentialSelectorUiState.Idle
- is Request.Cancel -> CredentialSelectorUiState.Cancel(request.appName)
- is Request.Close -> CredentialSelectorUiState.Close
- is Request.Create -> CredentialSelectorUiState.Create
+ null -> Idle
+ is Request.Cancel -> Cancel(request.appName)
+ is Request.Close -> Close
+ is Request.Create -> Create
is Request.Get -> request.toGet(isPrimary)
}
}
.stateIn(
viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
- initialValue = CredentialSelectorUiState.Idle,
+ initialValue = Idle,
)
fun updateRequest(intent: Intent) {
@@ -75,16 +80,14 @@
Log.d(TAG, "OnBackPressed")
when (uiState.value) {
is Get.MultipleEntry -> isPrimaryScreen.value = true
- else -> {
- shouldClose.value = true
- // TODO("b/300422310 - [Wear] Implement UI for cancellation request with message")
- }
+ is Create, Close, is Cancel, Idle -> shouldClose.value = true
+ is Get.SingleEntry, is Get.SingleEntryPerAccount -> cancel()
}
}
override fun cancel() {
+ credentialManagerClient.sendError(BaseDialogResult.RESULT_CODE_DIALOG_USER_CANCELED)
shouldClose.value = true
- // TODO("b/300422310 - [Wear] Implement UI for cancellation request with message")
}
override fun openSecondaryScreen() {
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
index 332b816..f9a58871 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
@@ -120,7 +120,6 @@
}
is CredentialSelectorUiState.Cancel -> {
- // TODO: b/300422310 - Implement cancel with message flow
onCloseApp()
}
diff --git a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
index 5b39f4e..18e8fc3 100644
--- a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
@@ -219,7 +219,6 @@
}
}
-
/**
* Shows restricted setting dialog.
*
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 460a6f7..e185367 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -28,7 +28,7 @@
import com.android.settingslib.spa.gallery.dialog.NavDialogProvider
import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuBoxPageProvider
-import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuCheckBoxProvider
+import com.android.settingslib.spa.gallery.editor.SettingsDropdownCheckBoxProvider
import com.android.settingslib.spa.gallery.home.HomePageProvider
import com.android.settingslib.spa.gallery.itemList.ItemListPageProvider
import com.android.settingslib.spa.gallery.itemList.ItemOperatePageProvider
@@ -100,7 +100,7 @@
EditorMainPageProvider,
SettingsOutlinedTextFieldPageProvider,
SettingsExposedDropdownMenuBoxPageProvider,
- SettingsExposedDropdownMenuCheckBoxProvider,
+ SettingsDropdownCheckBoxProvider,
SettingsTextFieldPasswordPageProvider,
SearchScaffoldPageProvider,
SuwScaffoldPageProvider,
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
index 4875ea9..9f2158a 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
@@ -37,7 +37,7 @@
.build(),
SettingsExposedDropdownMenuBoxPageProvider.buildInjectEntry().setLink(fromPage = owner)
.build(),
- SettingsExposedDropdownMenuCheckBoxProvider.buildInjectEntry().setLink(fromPage = owner)
+ SettingsDropdownCheckBoxProvider.buildInjectEntry().setLink(fromPage = owner)
.build(),
SettingsTextFieldPasswordPageProvider.buildInjectEntry().setLink(fromPage = owner)
.build(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsDropdownCheckBoxProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsDropdownCheckBoxProvider.kt
new file mode 100644
index 0000000..33ab75d
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsDropdownCheckBoxProvider.kt
@@ -0,0 +1,134 @@
+/*
+ * 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.settingslib.spa.gallery.editor
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckBox
+import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckOption
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+private const val TITLE = "Sample SettingsDropdownCheckBox"
+
+object SettingsDropdownCheckBoxProvider : SettingsPageProvider {
+ override val name = "SettingsDropdownCheckBox"
+
+ override fun getTitle(arguments: Bundle?): String {
+ return TITLE
+ }
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ RegularScaffold(title = TITLE) {
+ SettingsDropdownCheckBox(
+ label = "SettingsDropdownCheckBox",
+ options = remember {
+ listOf(
+ SettingsDropdownCheckOption("Item 1"),
+ SettingsDropdownCheckOption("Item 2"),
+ SettingsDropdownCheckOption("Item 3"),
+ )
+ },
+ )
+ SettingsDropdownCheckBox(
+ label = "Empty list",
+ options = emptyList(),
+ )
+ SettingsDropdownCheckBox(
+ label = "Disabled",
+ options = remember {
+ listOf(
+ SettingsDropdownCheckOption("Item 1", selected = mutableStateOf(true)),
+ SettingsDropdownCheckOption("Item 2"),
+ SettingsDropdownCheckOption("Item 3"),
+ )
+ },
+ enabled = false,
+ )
+ SettingsDropdownCheckBox(
+ label = "With disabled item",
+ options = remember {
+ listOf(
+ SettingsDropdownCheckOption("Item 1"),
+ SettingsDropdownCheckOption("Item 2"),
+ SettingsDropdownCheckOption(
+ text = "Disabled item 1",
+ changeable = false,
+ selected = mutableStateOf(true),
+ ),
+ SettingsDropdownCheckOption("Disabled item 2", changeable = false),
+ )
+ },
+ )
+ SettingsDropdownCheckBox(
+ label = "With select all",
+ options = remember {
+ listOf(
+ SettingsDropdownCheckOption("All", isSelectAll = true),
+ SettingsDropdownCheckOption("Item 1"),
+ SettingsDropdownCheckOption("Item 2"),
+ SettingsDropdownCheckOption("Item 3"),
+ )
+ },
+ )
+ SettingsDropdownCheckBox(
+ label = "With disabled item and select all",
+ options =
+ remember {
+ listOf(
+ SettingsDropdownCheckOption("All", isSelectAll = true, changeable = false),
+ SettingsDropdownCheckOption("Item 1"),
+ SettingsDropdownCheckOption("Item 2"),
+ SettingsDropdownCheckOption(
+ text = "Disabled item 1",
+ changeable = false,
+ selected = mutableStateOf(true),
+ ),
+ SettingsDropdownCheckOption("Disabled item 2", changeable = false),
+ )
+ },
+ )
+ }
+ }
+
+ fun buildInjectEntry(): SettingsEntryBuilder {
+ return SettingsEntryBuilder.createInject(owner = createSettingsPage()).setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun SettingsDropdownCheckBoxPagePreview() {
+ SettingsTheme {
+ SettingsDropdownCheckBoxProvider.Page(null)
+ }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt
deleted file mode 100644
index d289646..0000000
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2023 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.settingslib.spa.gallery.editor
-
-import android.os.Bundle
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.tooling.preview.Preview
-import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
-import com.android.settingslib.spa.framework.common.SettingsPageProvider
-import com.android.settingslib.spa.framework.common.createSettingsPage
-import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.theme.SettingsTheme
-import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuCheckBox
-import com.android.settingslib.spa.widget.preference.Preference
-import com.android.settingslib.spa.widget.preference.PreferenceModel
-import com.android.settingslib.spa.widget.scaffold.RegularScaffold
-
-private const val TITLE = "Sample SettingsExposedDropdownMenuCheckBox"
-
-object SettingsExposedDropdownMenuCheckBoxProvider : SettingsPageProvider {
- override val name = "SettingsExposedDropdownMenuCheckBox"
- private const val exposedDropdownMenuCheckBoxLabel = "ExposedDropdownMenuCheckBoxLabel"
- private val options = listOf("item1", "item2", "item3")
- private val selectedOptionsState1 = mutableStateListOf(0, 1)
-
- override fun getTitle(arguments: Bundle?): String {
- return TITLE
- }
-
- @Composable
- override fun Page(arguments: Bundle?) {
- RegularScaffold(title = TITLE) {
- SettingsExposedDropdownMenuCheckBox(
- label = exposedDropdownMenuCheckBoxLabel,
- options = options,
- selectedOptionsState = remember { selectedOptionsState1 },
- enabled = true,
- onSelectedOptionStateChange = {},
- )
- }
- }
-
- fun buildInjectEntry(): SettingsEntryBuilder {
- return SettingsEntryBuilder.createInject(owner = createSettingsPage()).setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = TITLE
- override val onClick = navigator(name)
- })
- }
- }
-}
-
-@Preview(showBackground = true)
-@Composable
-private fun SettingsExposedDropdownMenuCheckBoxPagePreview() {
- SettingsTheme {
- SettingsExposedDropdownMenuCheckBoxProvider.Page(null)
- }
-}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBox.kt
new file mode 100644
index 0000000..57963e6
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBox.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2023 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.settingslib.spa.widget.editor
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExposedDropdownMenuBox
+import androidx.compose.material3.ExposedDropdownMenuDefaults
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsOpacity.alphaForEnabled
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckOption.Companion.changeable
+
+data class SettingsDropdownCheckOption(
+ /** The displayed text of this option. */
+ val text: String,
+
+ /** If true, check / uncheck this item will check / uncheck all enabled options. */
+ val isSelectAll: Boolean = false,
+
+ /** If not changeable, cannot check or uncheck this option. */
+ val changeable: Boolean = true,
+
+ /** The selected state of this option. */
+ val selected: MutableState<Boolean> = mutableStateOf(false),
+
+ /** Get called when the option is clicked, no matter if it's changeable. */
+ val onClick: () -> Unit = {},
+) {
+ companion object {
+ val List<SettingsDropdownCheckOption>.changeable: Boolean
+ get() = filter { !it.isSelectAll }.any { it.changeable }
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun SettingsDropdownCheckBox(
+ label: String,
+ options: List<SettingsDropdownCheckOption>,
+ emptyText: String = "",
+ enabled: Boolean = true,
+ errorMessage: String? = null,
+ onSelectedStateChange: () -> Unit = {},
+) {
+ var dropDownWidth by remember { mutableIntStateOf(0) }
+ var expanded by remember { mutableStateOf(false) }
+ val changeable = enabled && options.changeable
+ ExposedDropdownMenuBox(
+ expanded = expanded,
+ onExpandedChange = { expanded = changeable && it },
+ modifier = Modifier
+ .width(350.dp)
+ .padding(SettingsDimension.textFieldPadding)
+ .onSizeChanged { dropDownWidth = it.width },
+ ) {
+ OutlinedTextField(
+ // The `menuAnchor` modifier must be passed to the text field for correctness.
+ modifier = Modifier
+ .menuAnchor()
+ .fillMaxWidth(),
+ value = getDisplayText(options) ?: emptyText,
+ onValueChange = {},
+ label = { Text(text = label) },
+ trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded) },
+ readOnly = true,
+ enabled = changeable,
+ isError = errorMessage != null,
+ supportingText = errorMessage?.let { { Text(text = it) } },
+ )
+ ExposedDropdownMenu(
+ expanded = expanded,
+ modifier = Modifier.width(with(LocalDensity.current) { dropDownWidth.toDp() }),
+ onDismissRequest = { expanded = false },
+ ) {
+ for (option in options) {
+ CheckboxItem(option) {
+ option.onClick()
+ if (option.changeable) {
+ checkboxItemOnClick(options, option)
+ onSelectedStateChange()
+ }
+ }
+ }
+ }
+ }
+}
+
+private fun getDisplayText(options: List<SettingsDropdownCheckOption>): String? {
+ val selectedOptions = options.filter { it.selected.value }
+ if (selectedOptions.isEmpty()) return null
+ return selectedOptions.filter { it.isSelectAll }.ifEmpty { selectedOptions }
+ .joinToString { it.text }
+}
+
+private fun checkboxItemOnClick(
+ options: List<SettingsDropdownCheckOption>,
+ clickedOption: SettingsDropdownCheckOption,
+) {
+ if (!clickedOption.changeable) return
+ val newChecked = !clickedOption.selected.value
+ if (clickedOption.isSelectAll) {
+ for (option in options.filter { it.changeable }) option.selected.value = newChecked
+ } else {
+ clickedOption.selected.value = newChecked
+ }
+ val (selectAllOptions, regularOptions) = options.partition { it.isSelectAll }
+ val isAllRegularOptionsChecked = regularOptions.all { it.selected.value }
+ selectAllOptions.forEach { it.selected.value = isAllRegularOptionsChecked }
+}
+
+@Composable
+private fun CheckboxItem(
+ option: SettingsDropdownCheckOption,
+ onClick: (SettingsDropdownCheckOption) -> Unit,
+) {
+ TextButton(
+ onClick = { onClick(option) },
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Checkbox(
+ checked = option.selected.value,
+ onCheckedChange = null,
+ enabled = option.changeable,
+ )
+ Text(text = option.text, modifier = Modifier.alphaForEnabled(option.changeable))
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun ActionButtonsPreview() {
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2")
+ val item3 = SettingsDropdownCheckOption("item3")
+ val options = listOf(item1, item2, item3)
+ SettingsTheme {
+ SettingsDropdownCheckBox(
+ label = "label",
+ options = options,
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
deleted file mode 100644
index e704505..0000000
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2023 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.settingslib.spa.widget.editor
-
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
-import androidx.compose.material3.Checkbox
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.ExposedDropdownMenuBox
-import androidx.compose.material3.ExposedDropdownMenuDefaults
-import androidx.compose.material3.OutlinedTextField
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.runtime.snapshots.SnapshotStateList
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.onSizeChanged
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import com.android.settingslib.spa.framework.theme.SettingsDimension
-import com.android.settingslib.spa.framework.theme.SettingsTheme
-
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-fun SettingsExposedDropdownMenuCheckBox(
- label: String,
- options: List<String>,
- selectedOptionsState: SnapshotStateList<Int>,
- emptyVal: String = "",
- enabled: Boolean,
- errorMessage: String? = null,
- onSelectedOptionStateChange: () -> Unit,
-) {
- var dropDownWidth by remember { mutableIntStateOf(0) }
- var expanded by remember { mutableStateOf(false) }
- val allIndex = options.indexOf("*")
- ExposedDropdownMenuBox(
- expanded = expanded,
- onExpandedChange = { expanded = it },
- modifier = Modifier
- .width(350.dp)
- .padding(SettingsDimension.textFieldPadding)
- .onSizeChanged { dropDownWidth = it.width },
- ) {
- OutlinedTextField(
- // The `menuAnchor` modifier must be passed to the text field for correctness.
- modifier = Modifier
- .menuAnchor()
- .fillMaxWidth(),
- value = if (selectedOptionsState.size == 0) emptyVal
- else if (selectedOptionsState.contains(allIndex)) "*"
- else selectedOptionsState.joinToString { options[it] },
- onValueChange = {},
- label = { Text(text = label) },
- trailingIcon = {
- ExposedDropdownMenuDefaults.TrailingIcon(
- expanded = expanded
- )
- },
- readOnly = true,
- enabled = enabled,
- isError = errorMessage != null,
- supportingText = {
- if (errorMessage != null) {
- Text(text = errorMessage)
- }
- }
- )
- if (options.isNotEmpty()) {
- ExposedDropdownMenu(
- expanded = expanded,
- modifier = Modifier.width(with(LocalDensity.current) { dropDownWidth.toDp() }),
- onDismissRequest = { expanded = false },
- ) {
- options.forEachIndexed { index, option ->
- CheckboxItem(
- selectedOptionsState,
- index,
- allIndex,
- onSelectedOptionStateChange,
- option,
- )
- }
- }
- }
- }
-}
-
-@Composable
-private fun CheckboxItem(
- selectedOptionsState: SnapshotStateList<Int>,
- index: Int,
- allIndex: Int,
- onSelectedOptionStateChange: () -> Unit,
- option: String
-) {
- TextButton(
- modifier = Modifier.fillMaxWidth(),
- onClick = {
- if (selectedOptionsState.contains(index)) {
- if (index == allIndex) {
- selectedOptionsState.clear()
- } else {
- selectedOptionsState.remove(index)
- selectedOptionsState.remove(allIndex)
- }
- } else {
- selectedOptionsState.add(index)
- }
- onSelectedOptionStateChange()
- }) {
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround),
- verticalAlignment = Alignment.CenterVertically
- ) {
- Checkbox(
- checked = selectedOptionsState.contains(index),
- onCheckedChange = null,
- )
- Text(text = option)
- }
- }
-}
-
-@Preview
-@Composable
-private fun ActionButtonsPreview() {
- val options = listOf("item1", "item2", "item3")
- val selectedOptionsState = remember { mutableStateListOf(0, 1) }
- SettingsTheme {
- SettingsExposedDropdownMenuCheckBox(
- label = "label",
- options = options,
- selectedOptionsState = selectedOptionsState,
- enabled = true,
- onSelectedOptionStateChange = {})
- }
-}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBoxTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBoxTest.kt
new file mode 100644
index 0000000..72b7b98
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBoxTest.kt
@@ -0,0 +1,145 @@
+/*
+ * 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.settingslib.spa.widget.editor
+
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.hasAnyAncestor
+import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.isPopup
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.hasRole
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsDropdownCheckBoxTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun dropdownCheckBox_displayed() {
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2")
+ val item3 = SettingsDropdownCheckOption("item3")
+ composeTestRule.setContent {
+ SettingsDropdownCheckBox(
+ label = LABEL,
+ options = listOf(item1, item2, item3),
+ )
+ }
+
+ composeTestRule.onNodeWithText(LABEL).assertIsDisplayed()
+ }
+
+ @Test
+ fun dropdownCheckBox_expanded() {
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2")
+ val item3 = SettingsDropdownCheckOption("item3")
+ composeTestRule.setContent {
+ SettingsDropdownCheckBox(
+ label = LABEL,
+ options = listOf(item1, item2, item3),
+ )
+ }
+ composeTestRule.onOption(item3).assertDoesNotExist()
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+
+ composeTestRule.onOption(item3).assertIsDisplayed()
+ }
+
+ @Test
+ fun dropdownCheckBox_valueAdded() {
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2")
+ val item3 = SettingsDropdownCheckOption("item3")
+ composeTestRule.setContent {
+ SettingsDropdownCheckBox(
+ label = LABEL,
+ options = listOf(item1, item2, item3),
+ )
+ }
+ composeTestRule.onDropdownBox(item3.text).assertDoesNotExist()
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+ composeTestRule.onOption(item3).performClick()
+
+ composeTestRule.onDropdownBox(item3.text).assertIsDisplayed()
+ assertThat(item3.selected.value).isTrue()
+ }
+
+ @Test
+ fun dropdownCheckBox_valueDeleted() {
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2", selected = mutableStateOf(true))
+ val item3 = SettingsDropdownCheckOption("item3")
+ composeTestRule.setContent {
+ SettingsDropdownCheckBox(
+ label = LABEL,
+ options = listOf(item1, item2, item3),
+ )
+ }
+ composeTestRule.onDropdownBox(item2.text).assertIsDisplayed()
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+ composeTestRule.onOption(item2).performClick()
+
+ composeTestRule.onDropdownBox(item2.text).assertDoesNotExist()
+ assertThat(item2.selected.value).isFalse()
+ }
+
+ @Test
+ fun dropdownCheckBox_withSelectAll() {
+ val selectAll = SettingsDropdownCheckOption("All", isSelectAll = true)
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2")
+ composeTestRule.setContent {
+ SettingsDropdownCheckBox(
+ label = LABEL,
+ options = listOf(selectAll, item1, item2),
+ )
+ }
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+ composeTestRule.onOption(selectAll).performClick()
+
+ composeTestRule.onDropdownBox(selectAll.text).assertIsDisplayed()
+ composeTestRule.onDropdownBox(item1.text).assertDoesNotExist()
+ composeTestRule.onDropdownBox(item2.text).assertDoesNotExist()
+ assertThat(item1.selected.value).isTrue()
+ assertThat(item2.selected.value).isTrue()
+ }
+
+ private companion object {
+ const val LABEL = "Label"
+ }
+}
+
+private fun ComposeContentTestRule.onDropdownBox(text: String) =
+ onNode(hasRole(Role.DropdownList) and hasText(text))
+
+private fun ComposeContentTestRule.onOption(option: SettingsDropdownCheckOption) =
+ onNode(hasAnyAncestor(isPopup()) and hasText(option.text))
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt
deleted file mode 100644
index 2b78ed7..0000000
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2023 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.settingslib.spa.widget.editor
-
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.test.SemanticsNodeInteraction
-import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.hasText
-import androidx.compose.ui.test.isFocused
-import androidx.compose.ui.test.junit4.ComposeContentTestRule
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithText
-import androidx.compose.ui.test.performClick
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class SettingsExposedDropdownMenuCheckBoxTest {
- @get:Rule
- val composeTestRule = createComposeRule()
- private val item1 = "item1"
- private val item2 = "item2"
- private val item3 = "item3"
- private val options = listOf(item1, item2, item3)
- private val selectedOptionsState1 = mutableStateListOf(0, 1)
- private val exposedDropdownMenuCheckBoxLabel = "ExposedDropdownMenuCheckBoxLabel"
-
- @Test
- fun exposedDropdownMenuCheckBox_displayed() {
- composeTestRule.setContent {
- SettingsExposedDropdownMenuCheckBox(
- label = exposedDropdownMenuCheckBoxLabel,
- options = options,
- selectedOptionsState = remember { selectedOptionsState1 },
- enabled = true,
- ) {}
- }
- composeTestRule.onNodeWithText(
- exposedDropdownMenuCheckBoxLabel, substring = true
- ).assertIsDisplayed()
- }
-
- @Test
- fun exposedDropdownMenuCheckBox_expanded() {
- composeTestRule.setContent {
- SettingsExposedDropdownMenuCheckBox(
- label = exposedDropdownMenuCheckBoxLabel,
- options = options,
- selectedOptionsState = remember { selectedOptionsState1 },
- enabled = true,
- ) {}
- }
- composeTestRule.onNodeWithText(item3, substring = true).assertDoesNotExist()
- composeTestRule.onNodeWithText(exposedDropdownMenuCheckBoxLabel, substring = true)
- .performClick()
- composeTestRule.onNodeWithText(item3, substring = true).assertIsDisplayed()
- }
-
- @Test
- fun exposedDropdownMenuCheckBox_valueAdded() {
- composeTestRule.setContent {
- SettingsExposedDropdownMenuCheckBox(
- label = exposedDropdownMenuCheckBoxLabel,
- options = options,
- selectedOptionsState = remember { selectedOptionsState1 },
- enabled = true,
- ) {}
- }
- composeTestRule.onNodeWithText(item3, substring = true).assertDoesNotExist()
- composeTestRule.onNodeWithText(exposedDropdownMenuCheckBoxLabel, substring = true)
- .performClick()
- composeTestRule.onNodeWithText(item3, substring = true).performClick()
- composeTestRule.onFocusedText(item3).assertIsDisplayed()
- }
-
- @Test
- fun exposedDropdownMenuCheckBox_valueDeleted() {
- composeTestRule.setContent {
- SettingsExposedDropdownMenuCheckBox(
- label = exposedDropdownMenuCheckBoxLabel,
- options = options,
- selectedOptionsState = remember { selectedOptionsState1 },
- enabled = true,
- ) {}
- }
- composeTestRule.onNodeWithText(item2, substring = true).assertIsDisplayed()
- composeTestRule.onNodeWithText(exposedDropdownMenuCheckBoxLabel, substring = true)
- .performClick()
- composeTestRule.onNotFocusedText(item2).performClick()
- composeTestRule.onFocusedText(item2).assertDoesNotExist()
- }
-}
-
-fun ComposeContentTestRule.onFocusedText(text: String): SemanticsNodeInteraction =
- onNode(isFocused() and hasText(text, substring = true))
-
-fun ComposeContentTestRule.onNotFocusedText(text: String): SemanticsNodeInteraction =
- onNode(!isFocused() and hasText(text, substring = true))
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/SemanticsMatcher.kt b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/SemanticsMatcher.kt
new file mode 100644
index 0000000..856bed6
--- /dev/null
+++ b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/SemanticsMatcher.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.settingslib.spa.testutils
+
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.getOrNull
+import androidx.compose.ui.test.SemanticsMatcher
+
+fun hasRole(role: Role) = SemanticsMatcher("${SemanticsProperties.Role.name} has $role") {
+ it.config.getOrNull(SemanticsProperties.Role) == role
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
index 9432d59..6b1893c 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
@@ -31,7 +31,6 @@
data class EnhancedConfirmation(
val key: String,
- val uid: Int,
val packageName: String,
)
data class Restrictions(
@@ -91,7 +90,7 @@
restrictions.enhancedConfirmation?.let { ec ->
RestrictedLockUtilsInternal
.checkIfRequiresEnhancedConfirmation(context, ec.key,
- ec.uid, ec.packageName)
+ ec.packageName)
?.let { intent -> return BlockedByEcmImpl(context = context, intent = intent) }
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index 74b556e..27e00c0 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -159,7 +159,6 @@
keys = switchRestrictionKeys,
enhancedConfirmation = enhancedConfirmationKey?.let { EnhancedConfirmation(
key = it,
- uid = checkNotNull(applicationInfo).uid,
packageName = packageName) })
RestrictedSwitchPreference(switchModel, restrictions, restrictionsProviderFactory)
InfoPageAdditionalContent(record, isAllowed)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
index 4b47437..2e8b76a 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
@@ -150,15 +150,13 @@
@Composable
fun getSummary(record: T): () -> String {
- val restrictions = remember(record.app.userId,
- record.app.uid, record.app.packageName) {
+ val restrictions = remember(record.app.userId, record.app.packageName) {
Restrictions(
userId = record.app.userId,
keys = listModel.switchRestrictionKeys,
enhancedConfirmation = listModel.enhancedConfirmationKey?.let {
EnhancedConfirmation(
key = it,
- uid = record.app.uid,
packageName = record.app.packageName)
})
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt
index 00ba9b4..b88d1c5 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt
@@ -32,6 +32,7 @@
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByAdmin
+import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByEcm
import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
@@ -44,6 +45,7 @@
val composeTestRule = createComposeRule()
private val fakeBlockedByAdmin = FakeBlockedByAdmin()
+ private val fakeBlockedByEcm = FakeBlockedByEcm()
private val fakeRestrictionsProvider = FakeRestrictionsProvider()
@@ -141,6 +143,29 @@
assertThat(fakeBlockedByAdmin.sendShowAdminSupportDetailsIntentIsCalled).isTrue()
}
+ @Test
+ fun whenBlockedByEcm_disabled() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByEcm
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
+ composeTestRule.onNodeWithText(FakeBlockedByEcm.SUMMARY).assertIsDisplayed()
+ composeTestRule.onNode(isOn()).assertIsDisplayed()
+ }
+
+ @Test
+ fun whenBlockedByEcm_click() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByEcm
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ assertThat(fakeBlockedByEcm.showRestrictedSettingsDetailsIsCalled).isTrue()
+ }
+
private fun setContent(restrictions: Restrictions) {
composeTestRule.setContent {
RestrictedSwitchPreference(switchPreferenceModel, restrictions) { _, _ ->
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt
index 2ccf323..556adc7 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt
@@ -29,6 +29,7 @@
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByAdmin
+import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByEcm
import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
@@ -41,6 +42,7 @@
val composeTestRule = createComposeRule()
private val fakeBlockedByAdmin = FakeBlockedByAdmin()
+ private val fakeBlockedByEcm = FakeBlockedByEcm()
private val fakeRestrictionsProvider = FakeRestrictionsProvider()
@@ -129,6 +131,28 @@
assertThat(menuItemOnClickIsCalled).isFalse()
}
+ @Test
+ fun whenBlockedByEcm_disabled() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByEcm
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TEXT).assertIsDisplayed().assertIsEnabled()
+ }
+
+ @Test
+ fun whenBlockedByEcm_onClick_showEcmDetails() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByEcm
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ assertThat(fakeBlockedByEcm.showRestrictedSettingsDetailsIsCalled).isTrue()
+ assertThat(menuItemOnClickIsCalled).isFalse()
+ }
+
private fun setContent(restrictions: Restrictions) {
val fakeMoreOptionsScope = object : MoreOptionsScope() {
override fun dismiss() {}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt
index 93fa17d..f8ca2a0 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt
@@ -19,6 +19,7 @@
import androidx.compose.runtime.Composable
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin
+import com.android.settingslib.spaprivileged.model.enterprise.BlockedByEcm
import com.android.settingslib.spaprivileged.model.enterprise.RestrictedMode
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProvider
@@ -36,6 +37,18 @@
}
}
+class FakeBlockedByEcm : BlockedByEcm {
+ var showRestrictedSettingsDetailsIsCalled = false
+
+ override fun showRestrictedSettingsDetails() {
+ showRestrictedSettingsDetailsIsCalled = true
+ }
+
+ companion object {
+ const val SUMMARY = "Disabled"
+ }
+}
+
class FakeRestrictionsProvider : RestrictionsProvider {
var restrictedMode: RestrictedMode? = null
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
new file mode 100644
index 0000000..6578eb7
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 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.settingslib;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.preference.DropDownPreference;
+import androidx.preference.PreferenceViewHolder;
+
+public class RestrictedDropDownPreference extends DropDownPreference {
+ RestrictedPreferenceHelper mHelper;
+
+ public RestrictedDropDownPreference(@NonNull Context context) {
+ super(context);
+ mHelper = new RestrictedPreferenceHelper(context, this, null);
+ }
+
+ /**
+ * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+ * package. Marks the preference as disabled if so.
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
+ */
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName) {
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ mHelper.onBindViewHolder(holder);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ if (enabled && isDisabledByEcm()) {
+ mHelper.setDisabledByEcm(null);
+ return;
+ }
+
+ super.setEnabled(enabled);
+ }
+
+ @Override
+ public void performClick() {
+ if (!mHelper.performClick()) {
+ super.performClick();
+ }
+ }
+
+ public boolean isDisabledByEcm() {
+ return mHelper.isDisabledByEcm();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index d902457..f36da19 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -23,11 +23,11 @@
import static com.android.settingslib.Utils.getColorAttrDefaultColor;
-import android.Manifest;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
+import android.app.ecm.EnhancedConfirmationManager;
import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
@@ -42,12 +42,10 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager.EnforcingUser;
-import android.provider.Settings;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
-import android.util.ArraySet;
import android.util.Log;
import android.view.MenuItem;
import android.widget.TextView;
@@ -60,7 +58,6 @@
import com.android.internal.widget.LockPatternUtils;
import java.util.List;
-import java.util.Set;
/**
* Utility class to host methods usable in adding a restricted padlock icon and showing admin
@@ -70,24 +67,11 @@
private static final String LOG_TAG = "RestrictedLockUtils";
private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
- private static final Set<String> ECM_KEYS = new ArraySet<>();
// TODO(b/281701062): reference role name from role manager once its exposed.
private static final String ROLE_DEVICE_LOCK_CONTROLLER =
"android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER";
- static {
- if (android.security.Flags.extendEcmToAllSettings()) {
- ECM_KEYS.add(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW);
- ECM_KEYS.add(AppOpsManager.OPSTR_GET_USAGE_STATS);
- ECM_KEYS.add(AppOpsManager.OPSTR_LOADER_USAGE_STATS);
- ECM_KEYS.add(Manifest.permission.BIND_DEVICE_ADMIN);
- }
-
- ECM_KEYS.add(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS);
- ECM_KEYS.add(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
- }
-
/**
* @return drawables for displaying with settings that are locked by a device admin.
*/
@@ -112,32 +96,63 @@
*/
@Nullable
public static Intent checkIfRequiresEnhancedConfirmation(@NonNull Context context,
- @NonNull String restriction,
- int uid,
- @Nullable String packageName) {
- // TODO(b/297372999): Replace with call to mainline module once ready
+ @NonNull String settingIdentifier, @NonNull String packageName) {
- if (!ECM_KEYS.contains(restriction)) {
+ if (!android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ || !android.security.Flags.extendEcmToAllSettings()) {
return null;
}
- final AppOpsManager appOps = (AppOpsManager) context
- .getSystemService(Context.APP_OPS_SERVICE);
- final int mode = appOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
- uid, packageName, null, null);
- final boolean ecmEnabled = context.getResources().getBoolean(
- com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
- if (ecmEnabled && mode != AppOpsManager.MODE_ALLOWED) {
- final Intent intent = new Intent(Settings.ACTION_SHOW_RESTRICTED_SETTING_DIALOG);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
- intent.putExtra(Intent.EXTRA_UID, uid);
- return intent;
+ EnhancedConfirmationManager ecManager = (EnhancedConfirmationManager) context
+ .getSystemService(Context.ECM_ENHANCED_CONFIRMATION_SERVICE);
+ try {
+ if (ecManager.isRestricted(packageName, settingIdentifier)) {
+ return ecManager.createRestrictedSettingDialogIntent(
+ packageName, settingIdentifier);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "package not found: " + packageName, e);
}
return null;
}
/**
+ * <p>This is {@code true} when the setting is a protected setting (i.e., a sensitive resource),
+ * and the app is restricted (i.e., considered dangerous), and the user has not yet cleared the
+ * app's restriction status (i.e., by clicking "Allow restricted settings" for this app). *
+ */
+ public static boolean isEnhancedConfirmationRestricted(@NonNull Context context,
+ @NonNull String settingIdentifier, @NonNull String packageName) {
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ try {
+ return context.getSystemService(EnhancedConfirmationManager.class)
+ .isRestricted(packageName, settingIdentifier);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Exception when retrieving package:" + packageName, e);
+ return false;
+ }
+ } else {
+ try {
+ if (!settingIdentifier.equals(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE)) {
+ return false;
+ }
+ int uid = context.getPackageManager().getPackageUid(packageName, 0);
+ final int mode = context.getSystemService(AppOpsManager.class)
+ .noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
+ uid, packageName);
+ final boolean ecmEnabled = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
+ return ecmEnabled && mode != AppOpsManager.MODE_ALLOWED;
+ } catch (Exception e) {
+ // Fallback in case if app ops is not available in testing.
+ return false;
+ }
+ }
+ }
+
+ /**
* Checks if a restriction is enforced on a user and returns the enforced admin and
* admin userId.
*
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
index 50e3bd0..495410b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -23,6 +23,7 @@
import android.os.UserHandle;
import android.util.AttributeSet;
+import androidx.annotation.NonNull;
import androidx.core.content.res.TypedArrayUtils;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceViewHolder;
@@ -99,12 +100,12 @@
/**
* Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
* package. Marks the preference as disabled if so.
- * @param restriction The key identifying the setting
- * @param packageName the package to check the restriction for
- * @param uid the uid of the package
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
*/
- public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) {
- mHelper.checkEcmRestrictionAndSetDisabled(restriction, packageName, uid);
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName) {
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index a479269..734b92c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -31,6 +31,8 @@
import android.util.TypedValue;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
@@ -43,9 +45,17 @@
* by device admins via user restrictions.
*/
public class RestrictedPreferenceHelper {
+ private static final String TAG = "RestrictedPreferenceHelper";
+
private final Context mContext;
private final Preference mPreference;
String packageName;
+
+ /**
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
int uid;
private boolean mDisabledByAdmin;
@@ -148,14 +158,15 @@
return true;
}
if (mDisabledByEcm) {
- if (android.security.Flags.extendEcmToAllSettings()) {
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
mContext.startActivity(mDisabledByEcmIntent);
return true;
+ } else {
+ RestrictedLockUtilsInternal.sendShowRestrictedSettingDialogIntent(mContext,
+ packageName, uid);
+ return true;
}
-
- RestrictedLockUtilsInternal.sendShowRestrictedSettingDialogIntent(mContext, packageName,
- uid);
- return true;
}
return false;
}
@@ -184,14 +195,14 @@
/**
* Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
* package. Marks the preference as disabled if so.
- * @param restriction The key identifying the setting
- * @param packageName the package to check the restriction for
- * @param uid the uid of the package
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
*/
- public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) {
- updatePackageDetails(packageName, uid);
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName) {
+ updatePackageDetails(packageName, android.os.Process.INVALID_UID);
Intent intent = RestrictedLockUtilsInternal.checkIfRequiresEnhancedConfirmation(
- mContext, restriction, uid, packageName);
+ mContext, settingIdentifier, packageName);
setDisabledByEcm(intent);
}
@@ -240,7 +251,7 @@
* be disabled.
* @return true if the disabled state was changed.
*/
- public boolean setDisabledByEcm(Intent disabledIntent) {
+ public boolean setDisabledByEcm(@Nullable Intent disabledIntent) {
boolean disabled = disabledIntent != null;
boolean changed = false;
if (mDisabledByEcm != disabled) {
@@ -275,6 +286,10 @@
if (mPreference instanceof PrimarySwitchPreference) {
((PrimarySwitchPreference) mPreference).setSwitchEnabled(isEnabled);
}
+
+ if (!isEnabled && mDisabledByEcm) {
+ mPreference.setSummary(R.string.disabled_by_app_ops_text);
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 70ece0f..0c54c19 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -200,12 +200,12 @@
/**
* Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
* package. Marks the preference as disabled if so.
- * @param restriction The key identifying the setting
- * @param packageName the package to check the restriction for
- * @param uid the uid of the package
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
*/
- public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) {
- mHelper.checkEcmRestrictionAndSetDisabled(restriction, packageName, uid);
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName) {
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index a376c1f..50e2f9c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -161,11 +161,6 @@
protected abstract void startScanOnRouter();
- /**
- * Transfer MediaDevice for media without package name.
- */
- protected abstract boolean connectDeviceWithoutPackageName(@NonNull MediaDevice device);
-
protected abstract void transferToRoute(@NonNull MediaRoute2Info route);
protected abstract void selectRoute(
@@ -199,6 +194,12 @@
@NonNull
protected abstract List<RoutingSessionInfo> getRemoteSessions();
+ /**
+ * Returns a non-empty list containing the routing sessions associated to the target media app.
+ *
+ * <p> The first item of the list is always the {@link RoutingSessionInfo#isSystemSession()
+ * system session}, followed other remote sessions linked to the target media app.
+ */
@NonNull
protected abstract List<RoutingSessionInfo> getRoutingSessionsForPackage();
@@ -206,9 +207,6 @@
protected abstract RoutingSessionInfo getRoutingSessionById(@NonNull String sessionId);
@NonNull
- protected abstract List<MediaRoute2Info> getAllRoutes();
-
- @NonNull
protected abstract List<MediaRoute2Info> getAvailableRoutesFromRouter();
@NonNull
@@ -256,8 +254,8 @@
* @return If add device successful return {@code true}, otherwise return {@code false}
*/
boolean addDeviceToPlayMedia(MediaDevice device) {
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null || !info.getSelectableRoutes().contains(device.mRouteInfo.getId())) {
+ final RoutingSessionInfo info = getActiveRoutingSession();
+ if (!info.getSelectableRoutes().contains(device.mRouteInfo.getId())) {
Log.w(TAG, "addDeviceToPlayMedia() Ignoring selecting a non-selectable device : "
+ device.getName());
return false;
@@ -267,13 +265,11 @@
return true;
}
- private RoutingSessionInfo getRoutingSessionInfo() {
- final List<RoutingSessionInfo> sessionInfos = getRoutingSessionsForPackage();
-
- if (sessionInfos.isEmpty()) {
- return null;
- }
- return sessionInfos.get(sessionInfos.size() - 1);
+ @NonNull
+ private RoutingSessionInfo getActiveRoutingSession() {
+ // List is never empty.
+ final List<RoutingSessionInfo> sessions = getRoutingSessionsForPackage();
+ return sessions.get(sessions.size() - 1);
}
boolean isRoutingSessionAvailableForVolumeControl() {
@@ -311,8 +307,8 @@
* @return If device stop successful return {@code true}, otherwise return {@code false}
*/
boolean removeDeviceFromPlayMedia(MediaDevice device) {
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null || !info.getSelectedRoutes().contains(device.mRouteInfo.getId())) {
+ final RoutingSessionInfo info = getActiveRoutingSession();
+ if (!info.getSelectedRoutes().contains(device.mRouteInfo.getId())) {
Log.w(TAG, "removeDeviceFromMedia() Ignoring deselecting a non-deselectable device : "
+ device.getName());
return false;
@@ -326,13 +322,7 @@
* Release session to stop playing media on MediaDevice.
*/
boolean releaseSession() {
- final RoutingSessionInfo sessionInfo = getRoutingSessionInfo();
- if (sessionInfo == null) {
- Log.w(TAG, "releaseSession() Ignoring release session : " + mPackageName);
- return false;
- }
-
- releaseSession(sessionInfo);
+ releaseSession(getActiveRoutingSession());
return true;
}
@@ -342,12 +332,7 @@
*/
@NonNull
List<MediaDevice> getSelectableMediaDevices() {
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.w(TAG, "getSelectableMediaDevices() cannot find selectable MediaDevice from : "
- + mPackageName);
- return Collections.emptyList();
- }
+ final RoutingSessionInfo info = getActiveRoutingSession();
final List<MediaDevice> deviceList = new ArrayList<>();
for (MediaRoute2Info route : getSelectableRoutes(info)) {
@@ -364,12 +349,7 @@
*/
@NonNull
List<MediaDevice> getDeselectableMediaDevices() {
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.d(TAG, "getDeselectableMediaDevices() cannot find deselectable MediaDevice from : "
- + mPackageName);
- return Collections.emptyList();
- }
+ final RoutingSessionInfo info = getActiveRoutingSession();
final List<MediaDevice> deviceList = new ArrayList<>();
for (MediaRoute2Info route : getDeselectableRoutes(info)) {
@@ -387,13 +367,7 @@
*/
@NonNull
List<MediaDevice> getSelectedMediaDevices() {
- RoutingSessionInfo info = getRoutingSessionInfo();
-
- if (info == null) {
- Log.w(TAG, "getSelectedMediaDevices() cannot find selectable MediaDevice from : "
- + mPackageName);
- return Collections.emptyList();
- }
+ RoutingSessionInfo info = getActiveRoutingSession();
final List<MediaDevice> deviceList = new ArrayList<>();
for (MediaRoute2Info route : getSelectedRoutes(info)) {
@@ -427,15 +401,8 @@
* @param volume the value of volume
*/
void adjustSessionVolume(int volume) {
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.w(TAG, "adjustSessionVolume() can't found corresponding RoutingSession with : "
- + mPackageName);
- return;
- }
-
Log.d(TAG, "adjustSessionVolume() adjust volume: " + volume + ", with : " + mPackageName);
- setSessionVolume(info, volume);
+ setSessionVolume(getActiveRoutingSession(), volume);
}
/**
@@ -444,14 +411,7 @@
* @return maximum volume of the session, and return -1 if not found.
*/
public int getSessionVolumeMax() {
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.w(TAG, "getSessionVolumeMax() can't find corresponding RoutingSession with : "
- + mPackageName);
- return -1;
- }
-
- return info.getVolumeMax();
+ return getActiveRoutingSession().getVolumeMax();
}
/**
@@ -460,24 +420,11 @@
* @return current volume of the session, and return -1 if not found.
*/
public int getSessionVolume() {
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.w(TAG, "getSessionVolume() can't find corresponding RoutingSession with : "
- + mPackageName);
- return -1;
- }
-
- return info.getVolume();
+ return getActiveRoutingSession().getVolume();
}
CharSequence getSessionName() {
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.w(TAG, "Unable to get session name for package: " + mPackageName);
- return null;
- }
-
- return info.getName();
+ return getActiveRoutingSession().getName();
}
@TargetApi(Build.VERSION_CODES.R)
@@ -503,26 +450,24 @@
}
}
private synchronized List<MediaRoute2Info> getAvailableRoutes() {
- List<MediaRoute2Info> infos = new ArrayList<>();
- RoutingSessionInfo routingSessionInfo = getRoutingSessionInfo();
- List<MediaRoute2Info> selectedRouteInfos = new ArrayList<>();
- if (routingSessionInfo != null) {
- selectedRouteInfos = getSelectedRoutes(routingSessionInfo);
- infos.addAll(selectedRouteInfos);
- infos.addAll(getSelectableRoutes(routingSessionInfo));
- }
- final List<MediaRoute2Info> transferableRoutes =
- getTransferableRoutes(mPackageName);
+ List<MediaRoute2Info> availableRoutes = new ArrayList<>();
+ RoutingSessionInfo activeSession = getActiveRoutingSession();
+
+ List<MediaRoute2Info> selectedRoutes = getSelectedRoutes(activeSession);
+ availableRoutes.addAll(selectedRoutes);
+ availableRoutes.addAll(getSelectableRoutes(activeSession));
+
+ final List<MediaRoute2Info> transferableRoutes = getTransferableRoutes(mPackageName);
for (MediaRoute2Info transferableRoute : transferableRoutes) {
boolean alreadyAdded = false;
- for (MediaRoute2Info mediaRoute2Info : infos) {
+ for (MediaRoute2Info mediaRoute2Info : availableRoutes) {
if (TextUtils.equals(transferableRoute.getId(), mediaRoute2Info.getId())) {
alreadyAdded = true;
break;
}
}
if (!alreadyAdded) {
- infos.add(transferableRoute);
+ availableRoutes.add(transferableRoute);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
@@ -531,13 +476,13 @@
final List<RouteListingPreference.Item> preferenceRouteListing =
Api34Impl.composePreferenceRouteListing(
routeListingPreference);
- infos = Api34Impl.arrangeRouteListByPreference(selectedRouteInfos,
+ availableRoutes = Api34Impl.arrangeRouteListByPreference(selectedRoutes,
getAvailableRoutesFromRouter(),
preferenceRouteListing);
}
- return Api34Impl.filterDuplicatedIds(infos);
+ return Api34Impl.filterDuplicatedIds(availableRoutes);
} else {
- return infos;
+ return availableRoutes;
}
}
@@ -610,7 +555,7 @@
}
if (mediaDevice != null
- && getRoutingSessionInfo().getSelectedRoutes().contains(route.getId())) {
+ && getActiveRoutingSession().getSelectedRoutes().contains(route.getId())) {
mediaDevice.setState(STATE_SELECTED);
if (mCurrentConnectedDevice == null) {
mCurrentConnectedDevice = mediaDevice;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
index cf8906d..453e807 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
@@ -86,18 +86,6 @@
}
@Override
- protected boolean connectDeviceWithoutPackageName(@NonNull MediaDevice device) {
- final RoutingSessionInfo info = mRouterManager.getSystemRoutingSession(null);
- if (info != null) {
- // TODO: b/279555229 - provide real user handle and package name of a caller.
- mRouterManager.transfer(
- info, device.mRouteInfo, android.os.Process.myUserHandle(), mPackageName);
- return true;
- }
- return false;
- }
-
- @Override
protected void selectRoute(@NonNull MediaRoute2Info route, @NonNull RoutingSessionInfo info) {
mRouterManager.selectRoute(info, route);
}
@@ -174,12 +162,6 @@
@Override
@NonNull
- protected List<MediaRoute2Info> getAllRoutes() {
- return mRouterManager.getAllRoutes();
- }
-
- @Override
- @NonNull
protected List<MediaRoute2Info> getAvailableRoutesFromRouter() {
return mRouterManager.getAvailableRoutes(mPackageName);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
index 5b1c8ef..ea4de39 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
@@ -58,11 +58,6 @@
}
@Override
- protected boolean connectDeviceWithoutPackageName(@NonNull MediaDevice device) {
- return false;
- }
-
- @Override
protected void transferToRoute(@NonNull MediaRoute2Info route) {
// Do nothing.
}
@@ -136,12 +131,6 @@
@NonNull
@Override
- protected List<MediaRoute2Info> getAllRoutes() {
- return Collections.emptyList();
- }
-
- @NonNull
- @Override
protected List<MediaRoute2Info> getAvailableRoutesFromRouter() {
return Collections.emptyList();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
index c8c8b672..0f08605 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
@@ -114,17 +114,6 @@
}
@Override
- protected boolean connectDeviceWithoutPackageName(@NonNull MediaDevice device) {
- if (device.mRouteInfo == null) {
- return false;
- }
-
- RoutingController controller = mRouter.getSystemController();
- mRouter.transfer(controller, device.mRouteInfo);
- return true;
- }
-
- @Override
protected void transferToRoute(@NonNull MediaRoute2Info route) {
mRouter.transferTo(route);
}
@@ -241,12 +230,6 @@
@NonNull
@Override
- protected List<MediaRoute2Info> getAllRoutes() {
- return mRouter.getAllRoutes();
- }
-
- @NonNull
- @Override
protected List<MediaRoute2Info> getAvailableRoutesFromRouter() {
return mRouter.getRoutes();
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
index 701f008..7ad54e1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
@@ -17,6 +17,7 @@
package com.android.settingslib;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.doReturn;
@@ -28,6 +29,7 @@
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyResourcesManager;
import android.content.Context;
+import android.content.Intent;
import android.view.View;
import android.widget.TextView;
@@ -87,6 +89,19 @@
}
@Test
+ public void bindPreference_disabledByEcm_shouldDisplayDisabledSummary() {
+ final TextView summaryView = mock(TextView.class, RETURNS_DEEP_STUBS);
+ when(mViewHolder.itemView.findViewById(android.R.id.summary))
+ .thenReturn(summaryView);
+
+ mHelper.setDisabledByEcm(mock(Intent.class));
+ mHelper.onBindViewHolder(mViewHolder);
+
+ verify(mPreference).setSummary(R.string.disabled_by_app_ops_text);
+ verify(summaryView, never()).setVisibility(View.GONE);
+ }
+
+ @Test
public void bindPreference_notDisabled_shouldNotHideSummary() {
final TextView summaryView = mock(TextView.class, RETURNS_DEEP_STUBS);
when(mViewHolder.itemView.findViewById(android.R.id.summary))
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 290e63ca..c159d5e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -30,6 +30,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -124,7 +125,11 @@
@Test
public void stopScan_startFirst_callsUnregister() {
+ RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
mInfoMediaManager.mRouterManager = mRouterManager;
+ // Since test is running in Robolectric, return a fake session to avoid NPE.
+ when(mRouterManager.getRoutingSessions(anyString())).thenReturn(List.of(sessionInfo));
+
mInfoMediaManager.startScan();
mInfoMediaManager.stopScan();
@@ -500,18 +505,6 @@
}
@Test
- public void connectDeviceWithoutPackageName_noSession_returnFalse() {
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
- final MediaDevice device = new InfoMediaDevice(mContext, info);
-
- final List<RoutingSessionInfo> infos = new ArrayList<>();
-
- mShadowRouter2Manager.setRemoteSessions(infos);
-
- assertThat(mInfoMediaManager.connectDeviceWithoutPackageName(device)).isFalse();
- }
-
- @Test
public void onRoutesRemoved_getAvailableRoutes_shouldAddMediaDevice() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
@@ -678,17 +671,6 @@
}
@Test
- public void getSessionVolumeMax_routeSessionInfoIsNull_returnNotFound() {
- final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
- final RoutingSessionInfo info = null;
- routingSessionInfos.add(info);
-
- mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
-
- assertThat(mInfoMediaManager.getSessionVolumeMax()).isEqualTo(-1);
- }
-
- @Test
public void getSessionVolume_containPackageName_returnMaxVolume() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -703,17 +685,6 @@
}
@Test
- public void getSessionVolume_routeSessionInfoIsNull_returnNotFound() {
- final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
- final RoutingSessionInfo info = null;
- routingSessionInfos.add(info);
-
- mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
-
- assertThat(mInfoMediaManager.getSessionVolume()).isEqualTo(-1);
- }
-
- @Test
public void getRemoteSessions_returnsRemoteSessions() {
final List<RoutingSessionInfo> infos = new ArrayList<>();
infos.add(mock(RoutingSessionInfo.class));
@@ -735,17 +706,6 @@
}
@Test
- public void getSessionName_routeSessionInfoIsNull_returnNull() {
- final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
- final RoutingSessionInfo info = null;
- routingSessionInfos.add(info);
-
- mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
-
- assertThat(mInfoMediaManager.getSessionName()).isNull();
- }
-
- @Test
public void getSessionName_containPackageName_returnName() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 4ed1965..71f9ba27 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -15,6 +15,16 @@
}
flag {
+ name: "udfps_view_performance"
+ namespace: "systemui"
+ description: "Decrease screen off blocking calls by waiting until the device is finished going to sleep before adding the udfps view."
+ bug: "225183106"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "notification_async_group_header_inflation"
namespace: "systemui"
description: "Inflates the notification group summary header views from the background thread."
@@ -489,3 +499,33 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "clipboard_noninteractive_on_lockscreen"
+ namespace: "systemui"
+ description: "Prevents the interactive clipboard UI from appearing when device is locked"
+ bug: "317048495"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "trim_resources_with_background_trim_at_lock"
+ namespace: "systemui"
+ description: "Trim fonts and other caches when the device locks to lower memory consumption."
+ bug: "322143614"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "dedicated_notif_inflation_thread"
+ namespace: "systemui"
+ description: "Create a separate background thread for inflating notifications"
+ bug: "308967184"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index 7b21d09..dd043db 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -18,11 +18,14 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.animateSceneFloatAsState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
+import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.Edge
import com.android.systemui.scene.shared.model.SceneKey
@@ -87,10 +90,15 @@
}
@Composable
-private fun LockscreenScene(
+private fun SceneScope.LockscreenScene(
lockscreenContent: Lazy<LockscreenContent>,
modifier: Modifier = Modifier,
) {
+ animateSceneFloatAsState(
+ value = QuickSettings.SharedValues.SquishinessValues.LockscreenSceneStarting,
+ key = QuickSettings.SharedValues.TilesSquishiness,
+ )
+
lockscreenContent
.get()
.Content(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
index c418490..7a73c58 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
@@ -73,7 +73,7 @@
BurnInParameters(
clockControllerProvider = { clock },
topInset = topInset,
- statusViewTop = topmostTop,
+ minViewY = topmostTop,
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
index 735c433..61b2d4e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
@@ -21,8 +21,8 @@
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.media.controls.ui.MediaCarouselController
-import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController
+import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.util.animation.MeasurementInput
private object MediaCarousel {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
index de8f2ec..5d0b9ba 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.ui.composable
-import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
@@ -32,14 +31,12 @@
import com.android.compose.animation.scene.MovableElementScenePicker
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.ValueKey
import com.android.compose.modifiers.thenIf
-import com.android.compose.theme.colorAttr
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Companion.Collapsing
import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Expanding
-import com.android.systemui.res.R
-import com.android.systemui.scene.ui.composable.Gone
-import com.android.systemui.scene.ui.composable.Lockscreen
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Unsquishing
import com.android.systemui.scene.ui.composable.QuickSettings as QuickSettingsSceneKey
import com.android.systemui.scene.ui.composable.Shade
@@ -51,15 +48,24 @@
)
object Elements {
- // TODO RENAME
val Content =
ElementKey("QuickSettingsContent", scenePicker = MovableElementScenePicker(SCENES))
- val CollapsedGrid = ElementKey("QuickSettingsCollapsedGrid")
val FooterActions = ElementKey("QuickSettingsFooterActions")
}
+
+ object SharedValues {
+ val TilesSquishiness = ValueKey("QuickSettingsTileSquishiness")
+ object SquishinessValues {
+ val Default = 1f
+ val LockscreenSceneStarting = 0f
+ val GoneSceneStarting = 0.3f
+ }
+ }
}
-private fun SceneScope.stateForQuickSettingsContent(): QSSceneAdapter.State {
+private fun SceneScope.stateForQuickSettingsContent(
+ squishiness: Float = QuickSettings.SharedValues.SquishinessValues.Default
+): QSSceneAdapter.State {
return when (val transitionState = layoutState.transitionState) {
is TransitionState.Idle -> {
when (transitionState.currentScene) {
@@ -73,10 +79,10 @@
when {
fromScene == Shade && toScene == QuickSettingsSceneKey -> Expanding(progress)
fromScene == QuickSettingsSceneKey && toScene == Shade -> Collapsing(progress)
- toScene == Shade -> QSSceneAdapter.State.QQS
- toScene == QuickSettingsSceneKey -> QSSceneAdapter.State.QS
- toScene == Gone -> QSSceneAdapter.State.CLOSED
- toScene == Lockscreen -> QSSceneAdapter.State.CLOSED
+ fromScene == Shade || toScene == Shade -> Unsquishing(squishiness)
+ fromScene == QuickSettingsSceneKey || toScene == QuickSettingsSceneKey -> {
+ QSSceneAdapter.State.QS
+ }
else ->
error(
"Bad transition for QuickSettings: fromScene=$fromScene," +
@@ -90,14 +96,24 @@
/**
* This composable will show QuickSettingsContent in the correct state (as determined by its
* [SceneScope]).
+ *
+ * If adding to scenes not in:
+ * * QuickSettingsScene
+ * * ShadeScene
+ *
+ * amend:
+ * * [stateForQuickSettingsContent],
+ * * [QuickSettings.SCENES],
+ * * this doc.
*/
@Composable
fun SceneScope.QuickSettings(
qsSceneAdapter: QSSceneAdapter,
heightProvider: () -> Int,
modifier: Modifier = Modifier,
+ squishiness: Float = QuickSettings.SharedValues.SquishinessValues.Default,
) {
- val contentState = stateForQuickSettingsContent()
+ val contentState = stateForQuickSettingsContent(squishiness)
MovableElement(
key = QuickSettings.Elements.Content,
@@ -136,7 +152,7 @@
modifier.fillMaxWidth().thenIf(isCustomizing) { Modifier.fillMaxHeight() }
) {
AndroidView(
- modifier = Modifier.fillMaxWidth().background(colorAttr(R.attr.underSurface)),
+ modifier = Modifier.fillMaxWidth(),
factory = { _ ->
qsSceneAdapter.setState(state)
view
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index d36345a3..66cef86 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -54,6 +54,7 @@
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.animateSceneFloatAsState
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.compose.modifiers.sysuiResTag
@@ -128,6 +129,7 @@
remember(lifecycleOwner, viewModel) {
viewModel.getFooterActionsViewModel(lifecycleOwner)
}
+ animateSceneFloatAsState(value = 1f, key = QuickSettings.SharedValues.TilesSquishiness)
// ############## SCROLLING ################
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index f90f29d..9ca751e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -19,9 +19,12 @@
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.animateSceneFloatAsState
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.Edge
import com.android.systemui.scene.shared.model.SceneKey
@@ -63,6 +66,10 @@
override fun SceneScope.Content(
modifier: Modifier,
) {
+ animateSceneFloatAsState(
+ value = QuickSettings.SharedValues.SquishinessValues.GoneSceneStarting,
+ key = QuickSettings.SharedValues.TilesSquishiness,
+ )
Spacer(modifier.fillMaxSize())
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt
index 6f115d8..30c82f4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt
@@ -13,7 +13,9 @@
) {
spec = tween(durationMillis = DefaultDuration.times(durationScale).inWholeMilliseconds.toInt())
- fractionRange(start = .58f) { fade(ShadeHeader.Elements.CollapsedContent) }
+ fractionRange(start = .58f) { fade(ShadeHeader.Elements.CollapsedContentStart) }
+ fractionRange(start = .58f) { fade(ShadeHeader.Elements.CollapsedContentEnd) }
+ fractionRange(start = .58f) { fade(ShadeHeader.Elements.PrivacyChip) }
translate(QuickSettings.Elements.Content, y = -ShadeHeader.Dimensions.CollapsedHeight * .66f)
translate(Notifications.Elements.NotificationScrim, Edge.Top, false)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToShadeTransition.kt
index e71f996..48ab68a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToShadeTransition.kt
@@ -1,11 +1,11 @@
package com.android.systemui.scene.ui.composable.transitions
import androidx.compose.animation.core.tween
-import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.shade.ui.composable.Shade
+import com.android.systemui.shade.ui.composable.ShadeHeader
import kotlin.time.Duration.Companion.milliseconds
fun TransitionBuilder.lockscreenToShadeTransition(
@@ -13,15 +13,12 @@
) {
spec = tween(durationMillis = DefaultDuration.times(durationScale).inWholeMilliseconds.toInt())
- fractionRange(end = 0.5f) {
- fade(Shade.Elements.BackgroundScrim)
- translate(
- QuickSettings.Elements.CollapsedGrid,
- Edge.Top,
- startsOutsideLayoutBounds = false,
- )
+ fractionRange(end = 0.5f) { fade(Shade.Elements.BackgroundScrim) }
+ translate(QuickSettings.Elements.Content, y = -ShadeHeader.Dimensions.CollapsedHeight * .66f)
+ fractionRange(start = 0.5f) {
+ fade(QuickSettings.Elements.Content)
+ fade(Notifications.Elements.NotificationScrim)
}
- fractionRange(start = 0.5f) { fade(Notifications.Elements.NotificationScrim) }
}
private val DefaultDuration = 500.milliseconds
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt
index d5c2a03..3c15da1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt
@@ -13,10 +13,15 @@
translate(Notifications.Elements.NotificationScrim, Edge.Bottom)
timestampRange(endMillis = 83) { fade(QuickSettings.Elements.FooterActions) }
- translate(ShadeHeader.Elements.CollapsedContent, y = ShadeHeader.Dimensions.CollapsedHeight)
+ translate(
+ ShadeHeader.Elements.CollapsedContentStart,
+ y = ShadeHeader.Dimensions.CollapsedHeight
+ )
+ translate(ShadeHeader.Elements.CollapsedContentEnd, y = ShadeHeader.Dimensions.CollapsedHeight)
translate(ShadeHeader.Elements.ExpandedContent, y = (-ShadeHeader.Dimensions.ExpandedHeight))
- fractionRange(end = .14f) { fade(ShadeHeader.Elements.CollapsedContent) }
+ fractionRange(end = .14f) { fade(ShadeHeader.Elements.CollapsedContentStart) }
+ fractionRange(end = .14f) { fade(ShadeHeader.Elements.CollapsedContentEnd) }
fractionRange(start = .58f) { fade(ShadeHeader.Elements.ExpandedContent) }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index b11edf7..00b494b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -20,6 +20,7 @@
import android.view.ContextThemeWrapper
import android.view.ViewGroup
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
@@ -48,6 +49,7 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.LowestZIndexScenePicker
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.ValueKey
import com.android.compose.animation.scene.animateSceneFloatAsState
@@ -57,9 +59,11 @@
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
+import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.res.R
import com.android.systemui.scene.ui.composable.QuickSettings
import com.android.systemui.scene.ui.composable.Shade as ShadeKey
+import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.CollapsedHeight
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager
@@ -72,7 +76,9 @@
object ShadeHeader {
object Elements {
val ExpandedContent = ElementKey("ShadeHeaderExpandedContent")
- val CollapsedContent = ElementKey("ShadeHeaderCollapsedContent")
+ val CollapsedContentStart = ElementKey("ShadeHeaderCollapsedContentStart")
+ val CollapsedContentEnd = ElementKey("ShadeHeaderCollapsedContentEnd")
+ val PrivacyChip = ElementKey("PrivacyChip", scenePicker = LowestZIndexScenePicker)
}
object Keys {
@@ -106,15 +112,16 @@
cutoutLocation != CutoutLocation.CENTER || formatProgress.value > 0.5f
}
}
+ val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsState()
// This layout assumes it is globally positioned at (0, 0) and is the
// same size as the screen.
Layout(
- modifier = modifier.element(ShadeHeader.Elements.CollapsedContent),
+ modifier = modifier,
contents =
listOf(
{
- Row {
+ Row(modifier = Modifier.element(ShadeHeader.Elements.CollapsedContentStart)) {
AndroidView(
factory = { context ->
Clock(
@@ -132,31 +139,44 @@
}
},
{
- Row(horizontalArrangement = Arrangement.End) {
- SystemIconContainer {
- when (LocalWindowSizeClass.current.widthSizeClass) {
- WindowWidthSizeClass.Medium,
- WindowWidthSizeClass.Expanded ->
- ShadeCarrierGroup(
- viewModel = viewModel,
- modifier = Modifier.align(Alignment.CenterVertically),
- )
- }
- StatusIcons(
+ if (isPrivacyChipVisible) {
+ Box(modifier = Modifier.height(CollapsedHeight).fillMaxWidth()) {
+ PrivacyChip(
viewModel = viewModel,
- createTintedIconManager = createTintedIconManager,
- statusBarIconController = statusBarIconController,
- useExpandedFormat = useExpandedFormat,
- modifier =
- Modifier.align(Alignment.CenterVertically)
- .padding(end = 6.dp)
- .weight(1f, fill = false)
+ modifier = Modifier.align(Alignment.CenterEnd),
)
- BatteryIcon(
- createBatteryMeterViewController = createBatteryMeterViewController,
- useExpandedFormat = useExpandedFormat,
- modifier = Modifier.align(Alignment.CenterVertically),
- )
+ }
+ } else {
+ Row(
+ horizontalArrangement = Arrangement.End,
+ modifier = Modifier.element(ShadeHeader.Elements.CollapsedContentEnd)
+ ) {
+ SystemIconContainer {
+ when (LocalWindowSizeClass.current.widthSizeClass) {
+ WindowWidthSizeClass.Medium,
+ WindowWidthSizeClass.Expanded ->
+ ShadeCarrierGroup(
+ viewModel = viewModel,
+ modifier = Modifier.align(Alignment.CenterVertically),
+ )
+ }
+ StatusIcons(
+ viewModel = viewModel,
+ createTintedIconManager = createTintedIconManager,
+ statusBarIconController = statusBarIconController,
+ useExpandedFormat = useExpandedFormat,
+ modifier =
+ Modifier.align(Alignment.CenterVertically)
+ .padding(end = 6.dp)
+ .weight(1f, fill = false)
+ )
+ BatteryIcon(
+ createBatteryMeterViewController =
+ createBatteryMeterViewController,
+ useExpandedFormat = useExpandedFormat,
+ modifier = Modifier.align(Alignment.CenterVertically),
+ )
+ }
}
}
},
@@ -223,67 +243,77 @@
.unsafeCompositionState(initialValue = 1f)
val useExpandedFormat by
remember(formatProgress) { derivedStateOf { formatProgress.value > 0.5f } }
+ val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsState()
- Column(
- verticalArrangement = Arrangement.Bottom,
- modifier =
- modifier
- .element(ShadeHeader.Elements.ExpandedContent)
- .fillMaxWidth()
- .defaultMinSize(minHeight = ShadeHeader.Dimensions.ExpandedHeight)
- ) {
- Row {
- AndroidView(
- factory = { context ->
- Clock(ContextThemeWrapper(context, R.style.TextAppearance_QS_Status), null)
- },
- modifier =
- Modifier.align(Alignment.CenterVertically)
- // use graphicsLayer instead of Modifier.scale to anchor transform to
- // the (start, top) corner
- .graphicsLayer(
- scaleX = 2.57f,
- scaleY = 2.57f,
- transformOrigin =
- TransformOrigin(
- when (LocalLayoutDirection.current) {
- LayoutDirection.Ltr -> 0f
- LayoutDirection.Rtl -> 1f
- },
- 0.5f
- )
- ),
- )
- Spacer(modifier = Modifier.weight(1f))
- ShadeCarrierGroup(
- viewModel = viewModel,
- modifier = Modifier.align(Alignment.CenterVertically),
- )
- }
- Spacer(modifier = Modifier.width(5.dp))
- Row {
- VariableDayDate(
- viewModel = viewModel,
- modifier = Modifier.widthIn(max = 90.dp).align(Alignment.CenterVertically),
- )
- Spacer(modifier = Modifier.weight(1f))
- SystemIconContainer {
- StatusIcons(
+ Box(modifier = modifier) {
+ if (isPrivacyChipVisible) {
+ Box(modifier = Modifier.height(CollapsedHeight).fillMaxWidth()) {
+ PrivacyChip(
viewModel = viewModel,
- createTintedIconManager = createTintedIconManager,
- statusBarIconController = statusBarIconController,
- useExpandedFormat = useExpandedFormat,
+ modifier = Modifier.align(Alignment.CenterEnd),
+ )
+ }
+ }
+ Column(
+ verticalArrangement = Arrangement.Bottom,
+ modifier =
+ Modifier.element(ShadeHeader.Elements.ExpandedContent)
+ .fillMaxWidth()
+ .defaultMinSize(minHeight = ShadeHeader.Dimensions.ExpandedHeight)
+ ) {
+ Row {
+ AndroidView(
+ factory = { context ->
+ Clock(ContextThemeWrapper(context, R.style.TextAppearance_QS_Status), null)
+ },
modifier =
Modifier.align(Alignment.CenterVertically)
- .padding(end = 6.dp)
- .weight(1f, fill = false),
+ // use graphicsLayer instead of Modifier.scale to anchor transform to
+ // the (start, top) corner
+ .graphicsLayer(
+ scaleX = 2.57f,
+ scaleY = 2.57f,
+ transformOrigin =
+ TransformOrigin(
+ when (LocalLayoutDirection.current) {
+ LayoutDirection.Ltr -> 0f
+ LayoutDirection.Rtl -> 1f
+ },
+ 0.5f
+ )
+ ),
)
- BatteryIcon(
- useExpandedFormat = useExpandedFormat,
- createBatteryMeterViewController = createBatteryMeterViewController,
+ Spacer(modifier = Modifier.weight(1f))
+ ShadeCarrierGroup(
+ viewModel = viewModel,
modifier = Modifier.align(Alignment.CenterVertically),
)
}
+ Spacer(modifier = Modifier.width(5.dp))
+ Row {
+ VariableDayDate(
+ viewModel = viewModel,
+ modifier = Modifier.widthIn(max = 90.dp).align(Alignment.CenterVertically),
+ )
+ Spacer(modifier = Modifier.weight(1f))
+ SystemIconContainer {
+ StatusIcons(
+ viewModel = viewModel,
+ createTintedIconManager = createTintedIconManager,
+ statusBarIconController = statusBarIconController,
+ useExpandedFormat = useExpandedFormat,
+ modifier =
+ Modifier.align(Alignment.CenterVertically)
+ .padding(end = 6.dp)
+ .weight(1f, fill = false),
+ )
+ BatteryIcon(
+ useExpandedFormat = useExpandedFormat,
+ createBatteryMeterViewController = createBatteryMeterViewController,
+ modifier = Modifier.align(Alignment.CenterVertically),
+ )
+ }
+ }
}
}
}
@@ -359,7 +389,14 @@
) {
val carrierIconSlots =
listOf(stringResource(id = com.android.internal.R.string.status_bar_mobile))
+ val cameraSlot = stringResource(id = com.android.internal.R.string.status_bar_camera)
+ val micSlot = stringResource(id = com.android.internal.R.string.status_bar_microphone)
+ val locationSlot = stringResource(id = com.android.internal.R.string.status_bar_location)
+
val isSingleCarrier by viewModel.isSingleCarrier.collectAsState()
+ val isPrivacyChipEnabled by viewModel.isPrivacyChipEnabled.collectAsState()
+ val isMicCameraIndicationEnabled by viewModel.isMicCameraIndicationEnabled.collectAsState()
+ val isLocationIndicationEnabled by viewModel.isLocationIndicationEnabled.collectAsState()
AndroidView(
factory = { context ->
@@ -382,6 +419,25 @@
} else {
iconContainer.addIgnoredSlots(carrierIconSlots)
}
+
+ if (isPrivacyChipEnabled) {
+ if (isMicCameraIndicationEnabled) {
+ iconContainer.addIgnoredSlot(cameraSlot)
+ iconContainer.addIgnoredSlot(micSlot)
+ } else {
+ iconContainer.removeIgnoredSlot(cameraSlot)
+ iconContainer.removeIgnoredSlot(micSlot)
+ }
+ if (isLocationIndicationEnabled) {
+ iconContainer.addIgnoredSlot(locationSlot)
+ } else {
+ iconContainer.removeIgnoredSlot(locationSlot)
+ }
+ } else {
+ iconContainer.removeIgnoredSlot(cameraSlot)
+ iconContainer.removeIgnoredSlot(micSlot)
+ iconContainer.removeIgnoredSlot(locationSlot)
+ }
},
modifier = modifier,
)
@@ -394,7 +450,28 @@
) {
// TODO(b/298524053): add hover state for this container
Row(
- modifier = modifier.height(ShadeHeader.Dimensions.CollapsedHeight),
+ modifier = modifier.height(CollapsedHeight),
content = content,
)
}
+
+@Composable
+private fun SceneScope.PrivacyChip(
+ viewModel: ShadeHeaderViewModel,
+ modifier: Modifier = Modifier,
+) {
+ val privacyList by viewModel.privacyItems.collectAsState()
+
+ AndroidView(
+ factory = { context ->
+ val view =
+ OngoingPrivacyChip(context, null).also { privacyChip ->
+ privacyChip.privacyList = privacyList
+ privacyChip.setOnClickListener { viewModel.onPrivacyChipClicked(privacyChip) }
+ }
+ view
+ },
+ update = { it.privacyList = privacyList },
+ modifier = modifier.element(ShadeHeader.Elements.PrivacyChip),
+ )
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 25df3e4..ff6e895 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -28,6 +28,7 @@
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@@ -40,14 +41,15 @@
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexScenePicker
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.animateSceneFloatAsState
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.media.controls.ui.MediaCarouselController
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.media.controls.ui.MediaHost
-import com.android.systemui.media.controls.ui.MediaHostState
import com.android.systemui.media.controls.ui.composable.MediaCarousel
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL
import com.android.systemui.notifications.ui.composable.NotificationScrollingStack
import com.android.systemui.qs.ui.composable.QuickSettings
@@ -73,7 +75,6 @@
object Shade {
object Elements {
- val QuickSettings = ElementKey("ShadeQuickSettings")
val MediaCarousel = ElementKey("ShadeMediaCarousel")
val BackgroundScrim =
ElementKey("ShadeBackgroundScrim", scenePicker = LowestZIndexScenePicker)
@@ -160,6 +161,8 @@
val density = LocalDensity.current
val layoutWidth = remember { mutableStateOf(0) }
val maxNotifScrimTop = remember { mutableStateOf(0f) }
+ val tileSquishiness by
+ animateSceneFloatAsState(value = 1f, key = QuickSettings.SharedValues.TilesSquishiness)
Box(
modifier =
@@ -190,7 +193,11 @@
)
QuickSettings(
viewModel.qsSceneAdapter,
- { viewModel.qsSceneAdapter.qqsHeight },
+ {
+ (viewModel.qsSceneAdapter.qqsHeight * tileSquishiness)
+ .roundToInt()
+ },
+ squishiness = tileSquishiness,
)
if (viewModel.isMediaVisible()) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt
index 0cf4367..d401261 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt
@@ -47,7 +47,7 @@
@Composable
override fun VolumePanelComposeScope.Content(modifier: Modifier) {
Row(
- modifier = modifier.height(48.dp).fillMaxWidth(),
+ modifier = modifier.height(if (isLargeScreen) 54.dp else 48.dp).fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
index 86eb849..2285128 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
@@ -52,7 +52,7 @@
if (layout.footerComponents.isNotEmpty()) {
Row(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
- horizontalArrangement = Arrangement.spacedBy(20.dp),
+ horizontalArrangement = Arrangement.spacedBy(if (isLargeScreen) 28.dp else 20.dp),
) {
for (component in layout.footerComponents) {
AnimatedVisibility(component.isVisible) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt
index 10731c7..8df8d2e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt
@@ -27,8 +27,8 @@
val orientation: Int
get() = state.orientation
- /** Is true when Volume Panel is using wide-screen layout and false the otherwise. */
- val isWideScreen: Boolean
+ /** Is true when Volume Panel is using large-screen layout and false the otherwise. */
+ val isLargeScreen: Boolean
get() = state.isWideScreen
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
index dd63420..8a1e6a8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
@@ -19,6 +19,7 @@
import android.content.res.Configuration
import androidx.compose.foundation.clickable
import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
@@ -26,6 +27,7 @@
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
@@ -43,6 +45,8 @@
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelState
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
+private val padding = 24.dp
+
@Composable
fun VolumePanelRoot(
viewModel: VolumePanelViewModel,
@@ -84,7 +88,18 @@
shape = RoundedCornerShape(topStart = radius, topEnd = radius),
color = MaterialTheme.colorScheme.surfaceContainer,
) {
- Column { components?.let { componentsState -> Components(componentsState) } }
+ components?.let { componentsState ->
+ Components(
+ componentsState,
+ Modifier.padding(
+ start = padding,
+ top = padding,
+ end = padding,
+ bottom = 20.dp,
+ )
+ .navigationBarsPadding()
+ )
+ }
}
}
}
@@ -92,36 +107,35 @@
}
@Composable
-private fun VolumePanelComposeScope.Components(components: ComponentsLayout) {
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- VerticalVolumePanelContent(
- components,
- modifier = Modifier.padding(24.dp),
- )
- } else {
- HorizontalVolumePanelContent(
- components,
- modifier =
- Modifier.padding(start = 24.dp, top = 24.dp, end = 24.dp, bottom = 20.dp)
- .heightIn(max = 236.dp),
- )
+private fun VolumePanelComposeScope.Components(
+ layout: ComponentsLayout,
+ modifier: Modifier = Modifier
+) {
+ var columnModifier = modifier.widthIn(max = 800.dp)
+ if (!isLargeScreen && orientation != Configuration.ORIENTATION_PORTRAIT) {
+ columnModifier = columnModifier.heightIn(max = 332.dp)
}
+ Column(modifier = columnModifier, verticalArrangement = Arrangement.spacedBy(padding)) {
+ if (orientation == Configuration.ORIENTATION_PORTRAIT || isLargeScreen) {
+ VerticalVolumePanelContent(layout)
+ } else {
+ HorizontalVolumePanelContent(layout)
+ }
+ BottomBar(layout = layout, modifier = Modifier)
+ }
+}
- if (components.bottomBarComponent.isVisible) {
- val horizontalPadding =
- dimensionResource(R.dimen.volume_panel_bottom_bar_horizontal_padding)
+@Composable
+private fun VolumePanelComposeScope.BottomBar(
+ layout: ComponentsLayout,
+ modifier: Modifier = Modifier
+) {
+ if (layout.bottomBarComponent.isVisible) {
Box(
- modifier =
- Modifier.fillMaxWidth()
- .navigationBarsPadding()
- .padding(
- start = horizontalPadding,
- end = horizontalPadding,
- bottom = dimensionResource(R.dimen.volume_panel_bottom_bar_bottom_padding),
- ),
+ modifier = modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
- with(components.bottomBarComponent.component as ComposeVolumePanelUiComponent) {
+ with(layout.bottomBarComponent.component as ComposeVolumePanelUiComponent) {
Content(Modifier)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
index 45f98be..1cdc2b6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
@@ -21,8 +21,8 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.util.mockito.KotlinArgumentCaptor
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
index 824733b..5a7cbf6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
@@ -67,7 +67,7 @@
@Test
fun isCommunalEnabled_false() =
- testScope.runTest { assertThat(underTest.isCommunalEnabled).isFalse() }
+ testScope.runTest { assertThat(underTest.isCommunalEnabled.value).isFalse() }
@Test
fun isCommunalAvailable_whenStorageUnlock_false() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 3ac19e4..53af4a0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -123,7 +123,7 @@
testScope.runTest {
userRepository.setSelectedUserInfo(mainUser)
runCurrent()
- assertThat(underTest.isCommunalEnabled).isTrue()
+ assertThat(underTest.isCommunalEnabled.value).isTrue()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index ddb8582..352bacc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -38,7 +38,7 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository
import com.android.systemui.testKosmos
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index b299ca7..cc322d0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -43,8 +43,8 @@
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
index 74fa465..b0f59fe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -127,7 +127,7 @@
@Test
fun translationAndScale_whenFullyDozing() =
testScope.runTest {
- burnInParameters = burnInParameters.copy(statusViewTop = 100)
+ burnInParameters = burnInParameters.copy(minViewY = 100)
val translationX by collectLastValue(underTest.translationX(burnInParameters))
val translationY by collectLastValue(underTest.translationY(burnInParameters))
val scale by collectLastValue(underTest.scale(burnInParameters))
@@ -182,11 +182,77 @@
}
@Test
- fun translationAndScale_whenFullyDozing_staysOutOfTopInset() =
+ fun translationAndScale_whenFullyDozing_MigrationFlagOff_staysOutOfTopInset() =
testScope.runTest {
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+
burnInParameters =
burnInParameters.copy(
- statusViewTop = 100,
+ minViewY = 100,
+ topInset = 80,
+ )
+ val translationX by collectLastValue(underTest.translationX(burnInParameters))
+ val translationY by collectLastValue(underTest.translationY(burnInParameters))
+ val scale by collectLastValue(underTest.scale(burnInParameters))
+
+ // Set to dozing (on AOD)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = 1f,
+ transitionState = TransitionState.FINISHED
+ ),
+ validateStep = false,
+ )
+
+ // Trigger a change to the burn-in model
+ burnInFlow.value =
+ BurnInModel(
+ translationX = 20,
+ translationY = -30,
+ scale = 0.5f,
+ )
+ assertThat(translationX).isEqualTo(20)
+ // -20 instead of -30, due to inset of 80
+ assertThat(translationY).isEqualTo(-20)
+ assertThat(scale)
+ .isEqualTo(
+ BurnInScaleViewModel(
+ scale = 0.5f,
+ scaleClockOnly = true,
+ )
+ )
+
+ // Set to the beginning of GONE->AOD transition
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = 0f,
+ transitionState = TransitionState.STARTED
+ ),
+ validateStep = false,
+ )
+ assertThat(translationX).isEqualTo(0)
+ assertThat(translationY).isEqualTo(0)
+ assertThat(scale)
+ .isEqualTo(
+ BurnInScaleViewModel(
+ scale = 1f,
+ scaleClockOnly = true,
+ )
+ )
+ }
+
+ @Test
+ fun translationAndScale_whenFullyDozing_MigrationFlagOn_staysOutOfTopInset() =
+ testScope.runTest {
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+
+ burnInParameters =
+ burnInParameters.copy(
+ minViewY = 100,
topInset = 80,
)
val translationX by collectLastValue(underTest.translationX(burnInParameters))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
similarity index 78%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
index c381749..31b67b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
@@ -27,6 +27,7 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,6 +43,7 @@
val kosmos = testKosmos()
val testScope = kosmos.testScope
val repository = kosmos.fakeKeyguardTransitionRepository
+ val shadeRepository = kosmos.fakeShadeRepository
val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
val underTest = kosmos.aodToLockscreenTransitionViewModel
@@ -59,6 +61,38 @@
}
@Test
+ fun notificationAlpha_whenShadeIsExpanded_equalsOne() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.notificationAlpha)
+
+ shadeRepository.setQsExpansion(0.5f)
+ runCurrent()
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(alpha).isEqualTo(1f)
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(alpha).isEqualTo(1f)
+ repository.sendTransitionStep(step(1f))
+ assertThat(alpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun notificationAlpha_whenShadeIsNotExpanded_usesTransitionValue() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.notificationAlpha)
+
+ shadeRepository.setQsExpansion(0f)
+ runCurrent()
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(alpha).isEqualTo(0f)
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(alpha).isEqualTo(0.5f)
+ repository.sendTransitionStep(step(1f))
+ assertThat(alpha).isEqualTo(1f)
+ }
+
+ @Test
fun lockscreenAlphaStartsFromViewStateAccessorAlpha() =
testScope.runTest {
val viewState = ViewStateAccessor(alpha = { 0.5f })
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index e04cbfd..503fd34 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -39,6 +39,7 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.dozeParameters
import com.android.systemui.statusbar.phone.screenOffAnimationController
@@ -72,6 +73,7 @@
private val deviceEntryRepository = kosmos.fakeDeviceEntryRepository
private val notificationsKeyguardInteractor = kosmos.notificationsKeyguardInteractor
private val dozeParameters = kosmos.dozeParameters
+ private val shadeRepository = kosmos.fakeShadeRepository
private val underTest by lazy { kosmos.keyguardRootViewModel }
private val viewState = ViewStateAccessor()
@@ -308,4 +310,22 @@
assertThat(alpha).isEqualTo(1.0f)
}
+
+ @Test
+ fun alpha_emitsOnShadeExpansion() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.alpha(viewState))
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ testScope,
+ )
+
+ shadeRepository.setQsExpansion(0f)
+ assertThat(alpha).isEqualTo(1f)
+
+ shadeRepository.setQsExpansion(0.5f)
+ assertThat(alpha).isEqualTo(0f)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesResourceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesResourceRepositoryTest.kt
new file mode 100644
index 0000000..62c9163
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesResourceRepositoryTest.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.systemui.qs.pipeline.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MinimumTilesResourceRepositoryTest : SysuiTestCase() {
+
+ val testableResources = context.orCreateTestableResources
+
+ @Test
+ fun minimumQSTiles_followsConfig() {
+ val minTwo = 2
+ testableResources.addOverride(R.integer.quick_settings_min_num_tiles, minTwo)
+ val underTest = MinimumTilesResourceRepository(context.resources)
+ assertThat(underTest.minNumberOfTiles).isEqualTo(minTwo)
+
+ val minSix = 6
+ testableResources.addOverride(R.integer.quick_settings_min_num_tiles, minSix)
+ val otherUnderTest = MinimumTilesResourceRepository(context.resources)
+ assertThat(otherUnderTest.minNumberOfTiles).isEqualTo(minSix)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
index 3418977..37d4721 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
@@ -20,11 +20,11 @@
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.res.R
import com.android.systemui.retail.data.repository.FakeRetailModeRepository
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
@@ -187,6 +187,22 @@
assertThat(loadTilesForUser(0)).isEqualTo(DEFAULT_TILES)
}
+ @Test
+ fun prependDefault() =
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tilesSpecs(0))
+
+ val startingTiles = listOf(TileSpec.create("e"), TileSpec.create("f"))
+
+ underTest.setTiles(0, startingTiles)
+ runCurrent()
+
+ underTest.prependDefault(0)
+
+ assertThat(tiles!!)
+ .containsExactlyElementsIn(DEFAULT_TILES.toTileSpecs() + startingTiles)
+ }
+
private fun TestScope.storeTilesForUser(specs: String, forUser: Int) {
secureSettings.putStringForUser(SETTING, specs, forUser)
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index 1e2784a..634c5fa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -40,6 +40,7 @@
import com.android.systemui.qs.pipeline.data.repository.FakeCustomTileAddedRepository
import com.android.systemui.qs.pipeline.data.repository.FakeInstalledTilesComponentRepository
import com.android.systemui.qs.pipeline.data.repository.FakeTileSpecRepository
+import com.android.systemui.qs.pipeline.data.repository.MinimumTilesFixedRepository
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
import com.android.systemui.qs.pipeline.domain.model.TileModel
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
@@ -82,6 +83,7 @@
FakeCustomTileAddedRepository()
private val pipelineFlags = QSPipelineFlagsRepository()
private val tileLifecycleManagerFactory = TLMFactory()
+ private val minimumTilesRepository = MinimumTilesFixedRepository()
@Mock private lateinit var customTileStatePersister: CustomTileStatePersister
@@ -114,6 +116,7 @@
tileSpecRepository = tileSpecRepository,
installedTilesComponentRepository = installedTilesPackageRepository,
userRepository = userRepository,
+ minimumTilesRepository = minimumTilesRepository,
customTileStatePersister = customTileStatePersister,
tileFactory = tileFactory,
newQSTileFactory = { newQSTileFactory },
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
new file mode 100644
index 0000000..90c8304
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
@@ -0,0 +1,147 @@
+/*
+ * 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.systemui.qs.pipeline.domain.interactor
+
+import android.content.ComponentName
+import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.FakeQSFactory
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.repository.FakeDefaultTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.MinimumTilesFixedRepository
+import com.android.systemui.qs.pipeline.data.repository.fakeDefaultTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.fakeMinimumTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.fakeRestoreRepository
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.qsTileFactory
+import com.android.systemui.settings.fakeUserTracker
+import com.android.systemui.settings.userTracker
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * This integration test is for testing the solution to b/324575996. In particular, when restoring
+ * from a device that uses different specs for tiles, we may end up with empty (or mostly empty) QS.
+ * In that case, we want to prepend the default tiles instead.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class NoLowNumberOfTilesTest : SysuiTestCase() {
+
+ private val USER_0_INFO =
+ UserInfo(
+ 0,
+ "zero",
+ "",
+ UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
+ )
+
+ private val defaultTiles =
+ listOf(
+ TileSpec.create("internet"),
+ TileSpec.create("bt"),
+ )
+
+ private val kosmos =
+ Kosmos().apply {
+ fakeMinimumTilesRepository = MinimumTilesFixedRepository(minNumberOfTiles = 2)
+ fakeUserTracker.set(listOf(USER_0_INFO), 0)
+ qsTileFactory = FakeQSFactory(::tileCreator)
+ fakeDefaultTilesRepository = FakeDefaultTilesRepository(defaultTiles)
+ }
+
+ private val currentUser: Int
+ get() = kosmos.userTracker.userId
+
+ private val goodTile = TileSpec.create("correct")
+
+ private val restoredTiles =
+ listOf(
+ TileSpec.create("OEM:internet"),
+ TileSpec.create("OEM:bt"),
+ TileSpec.create("OEM:dnd"),
+ // This is not an installed component so a tile won't be created
+ TileSpec.create(ComponentName.unflattenFromString("oem/.tile")!!),
+ TileSpec.create("OEM:flashlight"),
+ goodTile,
+ )
+
+ @Before
+ fun setUp() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_QS_NEW_PIPELINE)
+
+ with(kosmos) {
+ restoreReconciliationInteractor.start()
+ autoAddInteractor.init(kosmos.currentTilesInteractor)
+ }
+ }
+
+ @Test
+ fun noLessThanTwoTilesAfterOEMRestore_prependedDefault() =
+ with(kosmos) {
+ testScope.runTest {
+ val tiles by collectLastValue(currentTilesInteractor.currentTiles)
+ runCurrent()
+
+ assertThat(tiles!!).isNotEmpty()
+
+ val restoreData = RestoreData(restoredTiles, emptySet(), currentUser)
+ fakeRestoreRepository.onDataRestored(restoreData)
+ runCurrent()
+
+ assertThat(tiles!!.map { it.spec }).isEqualTo(defaultTiles + listOf(goodTile))
+ }
+ }
+
+ @Test
+ fun noEmptyTilesAfterSettingTilesToUnknownNames() =
+ with(kosmos) {
+ testScope.runTest {
+ val tiles by collectLastValue(currentTilesInteractor.currentTiles)
+ runCurrent()
+
+ assertThat(tiles!!).isNotEmpty()
+
+ val badTiles = listOf(TileSpec.create("OEM:unknown_tile"))
+ currentTilesInteractor.setTiles(badTiles)
+ runCurrent()
+
+ assertThat(tiles!!.map { it.spec }).isEqualTo(defaultTiles)
+ }
+ }
+
+ private fun tileCreator(spec: String): QSTile? {
+ return if (spec.contains("OEM")) {
+ null // We don't know how to create OEM spec tiles
+ } else {
+ FakeQSTile(currentUser)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
index f8573cc2..3c0ab24 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
@@ -259,6 +259,37 @@
}
@Test
+ fun state_unsquishing() =
+ testScope.runTest {
+ val qsImpl by collectLastValue(underTest.qsImpl)
+ val squishiness = 0.342f
+
+ underTest.inflate(context)
+ runCurrent()
+ clearInvocations(qsImpl!!)
+
+ underTest.setState(QSSceneAdapter.State.Unsquishing(squishiness))
+ with(qsImpl!!) {
+ verify(this).setQsVisible(true)
+ verify(this)
+ .setQsExpansion(
+ /* expansion= */ 0f,
+ /* panelExpansionFraction= */ 1f,
+ /* proposedTranslation= */ 0f,
+ /* squishinessFraction= */ squishiness,
+ )
+ verify(this).setListening(true)
+ verify(this).setExpanded(true)
+ verify(this)
+ .setTransitionToFullShadeProgress(
+ /* isTransitioningToFullShade= */ false,
+ /* qsTransitionFraction= */ 1f,
+ /* qsSquishinessFraction = */ squishiness,
+ )
+ }
+ }
+
+ @Test
fun customizing_QS() =
testScope.runTest {
val customizing by collectLastValue(underTest.isCustomizing)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt
index d1bc686..e281383 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt
@@ -29,6 +29,12 @@
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class QSSceneAdapterTest : SysuiTestCase() {
+
+ @Test
+ fun expanding_squishiness1() {
+ assertThat(QSSceneAdapter.State.Expanding(0.3f).squishiness).isEqualTo(1f)
+ }
+
@Test
fun expandingSpecialValues() {
assertThat(QSSceneAdapter.State.QQS).isEqualTo(QSSceneAdapter.State.Expanding(0f))
@@ -41,4 +47,11 @@
assertThat(Collapsing(collapsingProgress))
.isEqualTo(QSSceneAdapter.State.Expanding(1 - collapsingProgress))
}
+
+ @Test
+ fun unsquishing_expansionSameAsQQS() {
+ val squishiness = 0.6f
+ assertThat(QSSceneAdapter.State.Unsquishing(squishiness).expansion)
+ .isEqualTo(QSSceneAdapter.State.QQS.expansion)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index d47da3e..82862e0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -26,11 +26,11 @@
import com.android.systemui.qs.FooterActionsController
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
-import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.UserAction
import com.android.systemui.scene.shared.model.UserActionResult
+import com.android.systemui.shade.domain.interactor.privacyChipInteractor
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
@@ -58,7 +58,6 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
private val qsFlexiglassAdapter = FakeQSSceneAdapter({ mock() })
@@ -95,9 +94,9 @@
ShadeHeaderViewModel(
applicationScope = testScope.backgroundScope,
context = context,
- sceneInteractor = sceneInteractor,
mobileIconsInteractor = mobileIconsInteractor,
mobileIconsViewModel = mobileIconsViewModel,
+ privacyChipInteractor = kosmos.privacyChipInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 9f89d34..53b1d0e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -50,7 +50,7 @@
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.model.SysUiState
import com.android.systemui.model.sceneContainerPlugin
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -65,6 +65,7 @@
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.settings.FakeDisplayTracker
+import com.android.systemui.shade.domain.interactor.privacyChipInteractor
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
@@ -229,9 +230,9 @@
ShadeHeaderViewModel(
applicationScope = testScope.backgroundScope,
context = context,
- sceneInteractor = sceneInteractor,
mobileIconsInteractor = mobileIconsInteractor,
mobileIconsViewModel = mobileIconsViewModel,
+ privacyChipInteractor = kosmos.privacyChipInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryTest.kt
new file mode 100644
index 0000000..613f256
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryTest.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2023 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.systemui.shade.data.repository
+
+import android.content.Intent
+import android.safetycenter.SafetyCenterManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.privacy.PrivacyApplication
+import com.android.systemui.privacy.PrivacyConfig
+import com.android.systemui.privacy.PrivacyItem
+import com.android.systemui.privacy.PrivacyItemController
+import com.android.systemui.privacy.PrivacyType
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.kotlinArgumentCaptor
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations.initMocks
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PrivacyChipRepositoryTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val broadcastDispatcher = kosmos.broadcastDispatcher
+
+ @Mock private lateinit var privacyConfig: PrivacyConfig
+ @Mock private lateinit var privacyItemController: PrivacyItemController
+ @Mock private lateinit var safetyCenterManager: SafetyCenterManager
+
+ lateinit var underTest: PrivacyChipRepositoryImpl
+
+ @Before
+ fun setUp() {
+ initMocks(this)
+ setUpUnderTest()
+ }
+
+ @Test
+ fun isSafetyCenterEnabled_startEnabled() =
+ testScope.runTest {
+ setUpUnderTest(true)
+
+ val actual by collectLastValue(underTest.isSafetyCenterEnabled)
+ runCurrent()
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isSafetyCenterEnabled_startDisabled() =
+ testScope.runTest {
+ setUpUnderTest(false)
+
+ val actual by collectLastValue(underTest.isSafetyCenterEnabled)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun isSafetyCenterEnabled_updates() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isSafetyCenterEnabled)
+ runCurrent()
+
+ assertThat(actual).isFalse()
+
+ whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(true)
+
+ broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED),
+ )
+
+ runCurrent()
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun privacyItems_updates() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.privacyItems)
+ runCurrent()
+
+ val callback =
+ withArgCaptor<PrivacyItemController.Callback> {
+ verify(privacyItemController).addCallback(capture())
+ }
+
+ callback.onPrivacyItemsChanged(emptyList())
+ assertThat(actual).isEmpty()
+
+ val privacyItems =
+ listOf(
+ PrivacyItem(
+ privacyType = PrivacyType.TYPE_CAMERA,
+ application = PrivacyApplication("", 0)
+ ),
+ )
+ callback.onPrivacyItemsChanged(privacyItems)
+ assertThat(actual).isEqualTo(privacyItems)
+ }
+
+ @Test
+ fun isMicCameraIndicationEnabled_updates() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isMicCameraIndicationEnabled)
+ runCurrent()
+
+ val captor = kotlinArgumentCaptor<PrivacyConfig.Callback>()
+ verify(privacyConfig, times(2)).addCallback(captor.capture())
+ val callback = captor.allValues[0]
+
+ callback.onFlagMicCameraChanged(false)
+ assertThat(actual).isFalse()
+
+ callback.onFlagMicCameraChanged(true)
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isLocationIndicationEnabled_updates() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isLocationIndicationEnabled)
+ runCurrent()
+
+ val captor = kotlinArgumentCaptor<PrivacyConfig.Callback>()
+ verify(privacyConfig, times(2)).addCallback(captor.capture())
+ val callback = captor.allValues[1]
+
+ callback.onFlagLocationChanged(false)
+ assertThat(actual).isFalse()
+
+ callback.onFlagLocationChanged(true)
+ assertThat(actual).isTrue()
+ }
+
+ private fun setUpUnderTest(isSafetyCenterEnabled: Boolean = false) {
+ whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(isSafetyCenterEnabled)
+
+ underTest =
+ PrivacyChipRepositoryImpl(
+ applicationScope = kosmos.applicationCoroutineScope,
+ privacyConfig = privacyConfig,
+ privacyItemController = privacyItemController,
+ backgroundDispatcher = kosmos.testDispatcher,
+ broadcastDispatcher = broadcastDispatcher,
+ safetyCenterManager = safetyCenterManager,
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorTest.kt
new file mode 100644
index 0000000..f0293a8e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorTest.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2023 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.systemui.shade.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.privacy.PrivacyApplication
+import com.android.systemui.privacy.PrivacyItem
+import com.android.systemui.privacy.PrivacyType
+import com.android.systemui.privacy.privacyDialogController
+import com.android.systemui.privacy.privacyDialogControllerV2
+import com.android.systemui.shade.data.repository.fakePrivacyChipRepository
+import com.android.systemui.shade.data.repository.privacyChipRepository
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations.initMocks
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PrivacyChipInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val privacyChipRepository = kosmos.fakePrivacyChipRepository
+ private val privacyDialogController = kosmos.privacyDialogController
+ private val privacyDialogControllerV2 = kosmos.privacyDialogControllerV2
+ @Mock private lateinit var privacyChip: OngoingPrivacyChip
+
+ val underTest = kosmos.privacyChipInteractor
+
+ @Before
+ fun setUp() {
+ initMocks(this)
+ whenever(privacyChip.context).thenReturn(this.context)
+ }
+
+ @Test
+ fun isChipVisible_updates() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isChipVisible)
+
+ privacyChipRepository.setPrivacyItems(emptyList())
+ runCurrent()
+
+ assertThat(actual).isFalse()
+
+ val privacyItems =
+ listOf(
+ PrivacyItem(
+ privacyType = PrivacyType.TYPE_CAMERA,
+ application = PrivacyApplication("", 0)
+ ),
+ )
+ privacyChipRepository.setPrivacyItems(privacyItems)
+ runCurrent()
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isChipEnabled_noIndicationEnabled() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isChipEnabled)
+
+ privacyChipRepository.setIsMicCameraIndicationEnabled(false)
+ privacyChipRepository.setIsLocationIndicationEnabled(false)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun isChipEnabled_micCameraIndicationEnabled() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isChipEnabled)
+
+ privacyChipRepository.setIsMicCameraIndicationEnabled(true)
+ privacyChipRepository.setIsLocationIndicationEnabled(false)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isChipEnabled_locationIndicationEnabled() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isChipEnabled)
+
+ privacyChipRepository.setIsMicCameraIndicationEnabled(false)
+ privacyChipRepository.setIsLocationIndicationEnabled(true)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isChipEnabled_allIndicationEnabled() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isChipEnabled)
+
+ privacyChipRepository.setIsMicCameraIndicationEnabled(true)
+ privacyChipRepository.setIsLocationIndicationEnabled(true)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun onPrivacyChipClicked_safetyCenterEnabled() =
+ testScope.runTest {
+ privacyChipRepository.setIsSafetyCenterEnabled(true)
+
+ underTest.onPrivacyChipClicked(privacyChip)
+
+ verify(privacyDialogControllerV2).showDialog(any(), any())
+ verify(privacyDialogController, never()).showDialog(any())
+ }
+
+ @Test
+ fun onPrivacyChipClicked_safetyCenterDisabled() =
+ testScope.runTest {
+ privacyChipRepository.setIsSafetyCenterEnabled(false)
+
+ underTest.onPrivacyChipClicked(privacyChip)
+
+ verify(privacyDialogController).showDialog(any())
+ verify(privacyDialogControllerV2, never()).showDialog(any(), any())
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index c0aaab3..1ef3076 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
@@ -8,7 +8,7 @@
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.shade.domain.interactor.privacyChipInteractor
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
@@ -31,7 +31,6 @@
class ShadeHeaderViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
@@ -62,9 +61,9 @@
ShadeHeaderViewModel(
applicationScope = testScope.backgroundScope,
context = context,
- sceneInteractor = sceneInteractor,
mobileIconsInteractor = mobileIconsInteractor,
mobileIconsViewModel = mobileIconsViewModel,
+ privacyChipInteractor = kosmos.privacyChipInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index 799e8f0..33c2d4e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -27,10 +27,11 @@
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.shade.domain.interactor.privacyChipInteractor
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
@@ -96,9 +97,9 @@
ShadeHeaderViewModel(
applicationScope = testScope.backgroundScope,
context = context,
- sceneInteractor = sceneInteractor,
mobileIconsInteractor = mobileIconsInteractor,
mobileIconsViewModel = mobileIconsViewModel,
+ privacyChipInteractor = kosmos.privacyChipInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 3e0082c..0641c61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -681,7 +681,7 @@
@Test
fun shadeCollapseFadeIn() =
testScope.runTest {
- val fadeIn by collectLastValue(underTest.shadeCollpaseFadeIn)
+ val fadeIn by collectLastValue(underTest.shadeCollapseFadeIn)
// Start on lockscreen without the shade
underTest.setShadeCollapseFadeInComplete(false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
index aa6b4ac..c804fc6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -1138,7 +1138,7 @@
private fun setCameraProtectionBounds(protectionBounds: Rect) {
val protectionInfo =
- mock<CameraProtectionInfo> { whenever(this.cutoutBounds).thenReturn(protectionBounds) }
+ mock<CameraProtectionInfo> { whenever(this.bounds).thenReturn(protectionBounds) }
whenever(sysUICutout.cameraProtection).thenReturn(protectionInfo)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
new file mode 100644
index 0000000..e31cdcd
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.systemui.volume.panel.component.anc.data.repository
+
+import android.bluetooth.BluetoothDevice
+import android.net.Uri
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.media.BluetoothMediaDevice
+import com.android.settingslib.media.MediaDevice
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.volume.localMediaRepository
+import com.android.systemui.volume.localMediaRepositoryFactory
+import com.android.systemui.volume.panel.component.anc.FakeSliceFactory
+import com.android.systemui.volume.panel.component.anc.sliceViewManager
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AncSliceRepositoryTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: AncSliceRepository
+
+ @Before
+ fun setup() {
+ with(kosmos) {
+ val slice = FakeSliceFactory.createSlice(hasError = false, hasSliceItem = true)
+ whenever(sliceViewManager.bindSlice(any<Uri>())).thenReturn(slice)
+
+ underTest =
+ AncSliceRepositoryImpl(
+ localMediaRepositoryFactory,
+ testScope.testScheduler,
+ sliceViewManager,
+ )
+ }
+ }
+
+ @Test
+ fun noConnectedDevice_noSlice() {
+ with(kosmos) {
+ testScope.runTest {
+ localMediaRepository.updateCurrentConnectedDevice(null)
+
+ val slice by collectLastValue(underTest.ancSlice(1))
+ runCurrent()
+
+ assertThat(slice).isNull()
+ }
+ }
+ }
+
+ @Test
+ fun connectedDevice_sliceReturned() {
+ with(kosmos) {
+ testScope.runTest {
+ localMediaRepository.updateCurrentConnectedDevice(createMediaDevice())
+
+ val slice by collectLastValue(underTest.ancSlice(1))
+ runCurrent()
+
+ assertThat(slice).isNotNull()
+ }
+ }
+ }
+
+ private fun createMediaDevice(sliceUri: String = "content://test.slice"): MediaDevice {
+ val bluetoothDevice: BluetoothDevice = mock {
+ whenever(getMetadata(any()))
+ .thenReturn(
+ ("<HEARABLE_CONTROL_SLICE_WITH_WIDTH>" +
+ sliceUri +
+ "</HEARABLE_CONTROL_SLICE_WITH_WIDTH>")
+ .toByteArray()
+ )
+ }
+ val cachedBluetoothDevice: CachedBluetoothDevice = mock {
+ whenever(device).thenReturn(bluetoothDevice)
+ }
+ return mock<BluetoothMediaDevice> {
+ whenever(cachedDevice).thenReturn(cachedBluetoothDevice)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteriaTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteriaTest.kt
new file mode 100644
index 0000000..553aed8
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteriaTest.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.systemui.volume.panel.component.anc.domain
+
+import android.net.Uri
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.volume.panel.component.anc.FakeSliceFactory
+import com.android.systemui.volume.panel.component.anc.ancSliceInteractor
+import com.android.systemui.volume.panel.component.anc.ancSliceRepository
+import com.android.systemui.volume.panel.component.anc.sliceViewManager
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AncAvailabilityCriteriaTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: AncAvailabilityCriteria
+
+ @Before
+ fun setup() {
+ with(kosmos) {
+ whenever(sliceViewManager.bindSlice(any<Uri>())).thenReturn(mock {})
+
+ underTest = AncAvailabilityCriteria(ancSliceInteractor)
+ }
+ }
+
+ @Test
+ fun noSlice_unavailable() {
+ with(kosmos) {
+ testScope.runTest {
+ ancSliceRepository.putSlice(1, null)
+
+ val isAvailable by collectLastValue(underTest.isAvailable())
+ runCurrent()
+
+ assertThat(isAvailable).isFalse()
+ }
+ }
+ }
+
+ @Test
+ fun hasSlice_available() {
+ with(kosmos) {
+ testScope.runTest {
+ ancSliceRepository.putSlice(
+ 1,
+ FakeSliceFactory.createSlice(hasError = false, hasSliceItem = true)
+ )
+
+ val isAvailable by collectLastValue(underTest.isAvailable())
+ runCurrent()
+
+ assertThat(isAvailable).isTrue()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt
new file mode 100644
index 0000000..53f0bc9
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.systemui.volume.panel.component.anc.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.panel.component.anc.FakeSliceFactory
+import com.android.systemui.volume.panel.component.anc.ancSliceRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AncSliceInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: AncSliceInteractor
+
+ @Before
+ fun setup() {
+ with(kosmos) {
+ underTest = AncSliceInteractor(ancSliceRepository, testScope.backgroundScope)
+ }
+ }
+
+ @Test
+ fun errorSlice_returnsNull() {
+ with(kosmos) {
+ testScope.runTest {
+ ancSliceRepository.putSlice(
+ 1,
+ FakeSliceFactory.createSlice(hasError = true, hasSliceItem = true)
+ )
+
+ val slice by collectLastValue(underTest.ancSlice)
+ runCurrent()
+
+ assertThat(slice).isNull()
+ }
+ }
+ }
+
+ @Test
+ fun noSliceItem_returnsNull() {
+ with(kosmos) {
+ testScope.runTest {
+ ancSliceRepository.putSlice(
+ 1,
+ FakeSliceFactory.createSlice(hasError = false, hasSliceItem = false)
+ )
+
+ val slice by collectLastValue(underTest.ancSlice)
+ runCurrent()
+
+ assertThat(slice).isNull()
+ }
+ }
+ }
+
+ @Test
+ fun sliceItem_noError_returnsSlice() {
+ with(kosmos) {
+ testScope.runTest {
+ ancSliceRepository.putSlice(
+ 1,
+ FakeSliceFactory.createSlice(hasError = false, hasSliceItem = true)
+ )
+
+ val slice by collectLastValue(underTest.ancSlice)
+ runCurrent()
+
+ assertThat(slice).isNotNull()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
index f4d34f4..8a0dd12 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
@@ -27,7 +27,7 @@
<com.android.keyguard.KeyguardStatusView
android:id="@+id/clock"
android:orientation="vertical"
- android:layout_width="410dp"
+ android:layout_width="@dimen/keyguard_presentation_width"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 4e540de..186bd7c 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -173,4 +173,6 @@
<dimen name="sfps_progress_bar_thickness">6dp</dimen>
<!-- Padding from the edge of the screen for the progress bar -->
<dimen name="sfps_progress_bar_padding_from_edge">7dp</dimen>
+
+ <dimen name="keyguard_presentation_width">410dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/drawable/media_squiggly_progress.xml b/packages/SystemUI/res/drawable/media_squiggly_progress.xml
index 9cd3f62..ae797f7 100644
--- a/packages/SystemUI/res/drawable/media_squiggly_progress.xml
+++ b/packages/SystemUI/res/drawable/media_squiggly_progress.xml
@@ -14,4 +14,4 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.media.controls.ui.SquigglyProgress />
\ No newline at end of file
+<com.android.systemui.media.controls.ui.drawable.SquigglyProgress />
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_media_background.xml b/packages/SystemUI/res/drawable/qs_media_background.xml
index 217656da..830c882 100644
--- a/packages/SystemUI/res/drawable/qs_media_background.xml
+++ b/packages/SystemUI/res/drawable/qs_media_background.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<com.android.systemui.media.controls.ui.IlluminationDrawable
+<com.android.systemui.media.controls.ui.drawable.IlluminationDrawable
xmlns:systemui="http://schemas.android.com/apk/res-auto"
systemui:highlight="15"
systemui:cornerRadius="@dimen/notification_corner_radius" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_media_light_source.xml b/packages/SystemUI/res/drawable/qs_media_light_source.xml
index 849349a..0b42dba 100644
--- a/packages/SystemUI/res/drawable/qs_media_light_source.xml
+++ b/packages/SystemUI/res/drawable/qs_media_light_source.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.media.controls.ui.LightSourceDrawable
+<com.android.systemui.media.controls.ui.drawable.LightSourceDrawable
xmlns:systemui="http://schemas.android.com/apk/res-auto"
systemui:rippleMinSize="25dp"
systemui:rippleMaxSize="135dp" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_carousel.xml b/packages/SystemUI/res/layout/media_carousel.xml
index 715c869..825ece85 100644
--- a/packages/SystemUI/res/layout/media_carousel.xml
+++ b/packages/SystemUI/res/layout/media_carousel.xml
@@ -24,7 +24,7 @@
android:clipToPadding="false"
android:forceHasOverlappingRendering="false"
android:theme="@style/MediaPlayer">
- <com.android.systemui.media.controls.ui.MediaScrollView
+ <com.android.systemui.media.controls.ui.view.MediaScrollView
android:id="@+id/media_carousel_scroller"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -42,7 +42,7 @@
>
<!-- QSMediaPlayers will be added here dynamically -->
</LinearLayout>
- </com.android.systemui.media.controls.ui.MediaScrollView>
+ </com.android.systemui.media.controls.ui.view.MediaScrollView>
<com.android.systemui.qs.PageIndicator
android:id="@+id/media_page_indicator"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/scene_window_root.xml b/packages/SystemUI/res/layout/scene_window_root.xml
index 0dcd15b..bb8de4c 100644
--- a/packages/SystemUI/res/layout/scene_window_root.xml
+++ b/packages/SystemUI/res/layout/scene_window_root.xml
@@ -24,7 +24,7 @@
android:id="@+id/scene_window_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fitsSystemWindows="true">
+ android:fitsSystemWindows="false">
<include layout="@layout/super_notification_shade"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 65120a9..5436642 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -608,8 +608,6 @@
<dimen name="volume_panel_slice_horizontal_padding">24dp</dimen>
<dimen name="volume_panel_corner_radius">52dp</dimen>
- <dimen name="volume_panel_bottom_bar_horizontal_padding">24dp</dimen>
- <dimen name="volume_panel_bottom_bar_bottom_padding">20dp</dimen>
<!-- Size of each item in the ringer selector drawer. -->
<dimen name="volume_ringer_drawer_item_size">42dp</dimen>
diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
index dec7d79..630610d 100644
--- a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
@@ -19,13 +19,22 @@
import android.app.Presentation
import android.content.Context
import android.graphics.Color
+import android.graphics.Rect
import android.os.Bundle
import android.view.Display
+import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.WindowManager
+import android.widget.FrameLayout
+import android.widget.FrameLayout.LayoutParams
import com.android.keyguard.dagger.KeyguardStatusViewComponent
+import com.android.systemui.Flags.migrateClocksToBlueprint
+import com.android.systemui.plugins.clocks.ClockController
+import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.res.R
+import com.android.systemui.shared.clocks.ClockRegistry
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -37,6 +46,8 @@
@Assisted display: Display,
context: Context,
private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
+ private val clockRegistry: ClockRegistry,
+ private val clockEventController: ClockEventController,
) :
Presentation(
context,
@@ -45,18 +56,126 @@
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
) {
+ private lateinit var rootView: FrameLayout
+ private var clock: View? = null
private lateinit var keyguardStatusViewController: KeyguardStatusViewController
- private lateinit var clock: KeyguardStatusView
+ private lateinit var faceController: ClockFaceController
+ private lateinit var clockFrame: FrameLayout
+
+ private val clockChangedListener =
+ object : ClockRegistry.ClockChangeListener {
+ override fun onCurrentClockChanged() {
+ setClock(clockRegistry.createCurrentClock())
+ }
+
+ override fun onAvailableClocksChanged() {}
+ }
+
+ private val layoutChangeListener =
+ object : View.OnLayoutChangeListener {
+ override fun onLayoutChange(
+ view: View,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ clock?.let {
+ faceController.events.onTargetRegionChanged(
+ Rect(it.left, it.top, it.width, it.height)
+ )
+ }
+ }
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ if (migrateClocksToBlueprint()) {
+ onCreateV2()
+ } else {
+ onCreate()
+ }
+ }
+
+ fun onCreateV2() {
+ rootView = FrameLayout(getContext(), null)
+ rootView.setClipChildren(false)
+ setContentView(rootView)
+
+ setFullscreen()
+
+ setClock(clockRegistry.createCurrentClock())
+ }
+
+ fun onCreate() {
setContentView(
LayoutInflater.from(context)
.inflate(R.layout.keyguard_clock_presentation, /* root= */ null)
)
- val window = window ?: error("no window available.")
+ setFullscreen()
+
+ clock = requireViewById(R.id.clock)
+ keyguardStatusViewController =
+ keyguardStatusViewComponentFactory
+ .build(clock as KeyguardStatusView, display)
+ .keyguardStatusViewController
+ .apply {
+ setDisplayedOnSecondaryDisplay()
+ init()
+ }
+ }
+
+ override fun onAttachedToWindow() {
+ if (migrateClocksToBlueprint()) {
+ clockRegistry.registerClockChangeListener(clockChangedListener)
+ clockEventController.registerListeners(clock!!)
+
+ faceController.animations.enter()
+ }
+ }
+
+ override fun onDetachedFromWindow() {
+ if (migrateClocksToBlueprint()) {
+ clockEventController.unregisterListeners()
+ clockRegistry.unregisterClockChangeListener(clockChangedListener)
+ }
+
+ super.onDetachedFromWindow()
+ }
+
+ override fun onDisplayChanged() {
+ val window = window ?: error("no window available.")
+ window.getDecorView().requestLayout()
+ }
+
+ private fun setClock(clockController: ClockController) {
+ clock?.removeOnLayoutChangeListener(layoutChangeListener)
+ rootView.removeAllViews()
+
+ faceController = clockController.largeClock
+ clock = faceController.view.also { it.addOnLayoutChangeListener(layoutChangeListener) }
+ rootView.addView(
+ clock,
+ FrameLayout.LayoutParams(
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_presentation_width),
+ WRAP_CONTENT,
+ Gravity.CENTER,
+ )
+ )
+
+ clockEventController.clock = clockController
+ clockEventController.setLargeClockOnSecondaryDisplay(true)
+ faceController.events.onSecondaryDisplayChanged(true)
+ }
+
+ private fun setFullscreen() {
+ val window = window ?: error("no window available.")
// Logic to make the lock screen fullscreen
window.decorView.systemUiVisibility =
(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
@@ -65,16 +184,6 @@
window.attributes.fitInsetsTypes = 0
window.isNavigationBarContrastEnforced = false
window.navigationBarColor = Color.TRANSPARENT
-
- clock = requireViewById(R.id.clock)
- keyguardStatusViewController =
- keyguardStatusViewComponentFactory
- .build(clock, display)
- .keyguardStatusViewController
- .apply {
- setDisplayedOnSecondaryDisplay()
- init()
- }
}
/** [ConnectedDisplayKeyguardPresentation] factory. */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 8a6f101..0bb5c17 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -17,13 +17,10 @@
import android.app.Presentation;
import android.content.Context;
-import android.graphics.Color;
-import android.graphics.Rect;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.DisplayManager;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
-import android.os.Bundle;
import android.os.Trace;
import android.text.TextUtils;
import android.util.Log;
@@ -31,20 +28,14 @@
import android.view.Display;
import android.view.DisplayAddress;
import android.view.DisplayInfo;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import androidx.annotation.Nullable;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.keyguard.dagger.KeyguardStatusViewComponent;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.settings.DisplayTracker;
@@ -66,10 +57,8 @@
private final DisplayManager mDisplayService;
private final DisplayTracker mDisplayTracker;
private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
- private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
private final ConnectedDisplayKeyguardPresentation.Factory
mConnectedDisplayKeyguardPresentationFactory;
- private final FeatureFlags mFeatureFlags;
private final Context mContext;
private boolean mShowing;
@@ -106,18 +95,15 @@
@Inject
public KeyguardDisplayManager(Context context,
Lazy<NavigationBarController> navigationBarControllerLazy,
- KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
DisplayTracker displayTracker,
@Main Executor mainExecutor,
@UiBackground Executor uiBgExecutor,
DeviceStateHelper deviceStateHelper,
KeyguardStateController keyguardStateController,
ConnectedDisplayKeyguardPresentation.Factory
- connectedDisplayKeyguardPresentationFactory,
- FeatureFlags featureFlags) {
+ connectedDisplayKeyguardPresentationFactory) {
mContext = context;
mNavigationBarControllerLazy = navigationBarControllerLazy;
- mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
uiBgExecutor.execute(() -> mMediaRouter = mContext.getSystemService(MediaRouter.class));
mDisplayService = mContext.getSystemService(DisplayManager.class);
mDisplayTracker = displayTracker;
@@ -125,7 +111,6 @@
mDeviceStateHelper = deviceStateHelper;
mKeyguardStateController = keyguardStateController;
mConnectedDisplayKeyguardPresentationFactory = connectedDisplayKeyguardPresentationFactory;
- mFeatureFlags = featureFlags;
}
private boolean isKeyguardShowable(Display display) {
@@ -197,11 +182,7 @@
}
Presentation createPresentation(Display display) {
- if (mFeatureFlags.isEnabled(Flags.ENABLE_CLOCK_KEYGUARD_PRESENTATION)) {
- return mConnectedDisplayKeyguardPresentationFactory.create(display);
- } else {
- return new KeyguardPresentation(mContext, display, mKeyguardStatusViewComponentFactory);
- }
+ return mConnectedDisplayKeyguardPresentationFactory.create(display);
}
/**
@@ -347,92 +328,4 @@
&& mRearDisplayPhysicalAddress.equals(display.getAddress());
}
}
-
-
- @VisibleForTesting
- static final class KeyguardPresentation extends Presentation {
- private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
- private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
- private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- private KeyguardClockSwitchController mKeyguardClockSwitchController;
- private View mClock;
- private int mUsableWidth;
- private int mUsableHeight;
- private int mMarginTop;
- private int mMarginLeft;
- Runnable mMoveTextRunnable = new Runnable() {
- @Override
- public void run() {
- int x = mMarginLeft + (int) (Math.random() * (mUsableWidth - mClock.getWidth()));
- int y = mMarginTop + (int) (Math.random() * (mUsableHeight - mClock.getHeight()));
- mClock.setTranslationX(x);
- mClock.setTranslationY(y);
- mClock.postDelayed(mMoveTextRunnable, MOVE_CLOCK_TIMEOUT);
- }
- };
-
- KeyguardPresentation(Context context, Display display,
- KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) {
- super(context, display, R.style.Theme_SystemUI_KeyguardPresentation,
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
- setCancelable(false);
- }
-
- @Override
- public void cancel() {
- // Do not allow anything to cancel KeyguardPresentation except KeyguardDisplayManager.
- }
-
- @Override
- public void onDetachedFromWindow() {
- mClock.removeCallbacks(mMoveTextRunnable);
- }
-
- @Override
- public void onDisplayChanged() {
- updateBounds();
- getWindow().getDecorView().requestLayout();
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- updateBounds();
-
- setContentView(LayoutInflater.from(getContext())
- .inflate(R.layout.keyguard_presentation, null));
-
- // Logic to make the lock screen fullscreen
- getWindow().getDecorView().setSystemUiVisibility(
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
- getWindow().getAttributes().setFitInsetsTypes(0 /* types */);
- getWindow().setNavigationBarContrastEnforced(false);
- getWindow().setNavigationBarColor(Color.TRANSPARENT);
-
- mClock = findViewById(R.id.clock);
-
- // Avoid screen burn in
- mClock.post(mMoveTextRunnable);
-
- mKeyguardClockSwitchController = mKeyguardStatusViewComponentFactory
- .build(findViewById(R.id.clock), getDisplay())
- .getKeyguardClockSwitchController();
-
- mKeyguardClockSwitchController.setOnlyClock(true);
- mKeyguardClockSwitchController.init();
- }
-
- private void updateBounds() {
- final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
- .getBounds();
- mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
- mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
- mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
- mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
index e8499d3..ca83724 100644
--- a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
@@ -143,7 +143,7 @@
private fun notifyCameraActive(info: CameraProtectionInfo) {
listeners.forEach {
- it.onApplyCameraProtection(info.cutoutProtectionPath, info.cutoutBounds)
+ it.onApplyCameraProtection(info.cutoutProtectionPath, info.bounds)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt b/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt
index 6314bd9..9357056 100644
--- a/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt
@@ -23,6 +23,6 @@
val logicalCameraId: String,
val physicalCameraId: String?,
val cutoutProtectionPath: Path,
- val cutoutBounds: Rect,
+ val bounds: Rect,
val displayUniqueId: String?,
)
diff --git a/packages/SystemUI/src/com/android/systemui/SysUICutoutProvider.kt b/packages/SystemUI/src/com/android/systemui/SysUICutoutProvider.kt
index aad9341..7309599 100644
--- a/packages/SystemUI/src/com/android/systemui/SysUICutoutProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/SysUICutoutProvider.kt
@@ -17,8 +17,12 @@
package com.android.systemui
import android.content.Context
+import android.graphics.Rect
+import android.util.RotationUtils
+import android.view.Display
import android.view.DisplayCutout
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.display.naturalBounds
import javax.inject.Inject
@SysUISingleton
@@ -33,15 +37,43 @@
cameraProtectionLoader.loadCameraProtectionInfoList()
}
- fun cutoutInfoForCurrentDisplay(): SysUICutoutInformation? {
+ /**
+ * Returns the [SysUICutoutInformation] for the current display and the current rotation.
+ *
+ * This means that the bounds of the display cutout and the camera protection will be
+ * adjusted/rotated for the current rotation.
+ */
+ fun cutoutInfoForCurrentDisplayAndRotation(): SysUICutoutInformation? {
val display = context.display
val displayCutout: DisplayCutout = display.cutout ?: return null
+ return SysUICutoutInformation(displayCutout, getCameraProtectionForDisplay(display))
+ }
+
+ private fun getCameraProtectionForDisplay(display: Display): CameraProtectionInfo? {
val displayUniqueId: String? = display.uniqueId
if (displayUniqueId.isNullOrEmpty()) {
- return SysUICutoutInformation(displayCutout, cameraProtection = null)
+ return null
}
- val cameraProtection: CameraProtectionInfo? =
+ val cameraProtection: CameraProtectionInfo =
cameraProtectionList.firstOrNull { it.displayUniqueId == displayUniqueId }
- return SysUICutoutInformation(displayCutout, cameraProtection)
+ ?: return null
+ val adjustedBoundsForRotation =
+ calculateCameraProtectionBoundsForRotation(display, cameraProtection.bounds)
+ return cameraProtection.copy(bounds = adjustedBoundsForRotation)
+ }
+
+ private fun calculateCameraProtectionBoundsForRotation(
+ display: Display,
+ originalProtectionBounds: Rect,
+ ): Rect {
+ val displayNaturalBounds = display.naturalBounds
+ val rotatedBoundsOut = Rect(originalProtectionBounds)
+ RotationUtils.rotateBounds(
+ /* inOutBounds = */ rotatedBoundsOut,
+ /* parentWidth = */ displayNaturalBounds.width(),
+ /* parentHeight = */ displayNaturalBounds.height(),
+ /* rotation = */ display.rotation
+ )
+ return rotatedBoundsOut
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 25ccc16..357eca3 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -93,11 +93,13 @@
@Override
public void onSessionEnded() {
mLastProximityEvent = null;
+ mHistoryTracker.removeBeliefListener(mBeliefListener);
mClassifiers.forEach(FalsingClassifier::onSessionEnded);
}
@Override
public void onSessionStarted() {
+ mHistoryTracker.addBeliefListener(mBeliefListener);
mClassifiers.forEach(FalsingClassifier::onSessionStarted);
}
};
@@ -200,7 +202,6 @@
mDataProvider.addSessionListener(mSessionListener);
mDataProvider.addGestureCompleteListener(mGestureFinalizedListener);
- mHistoryTracker.addBeliefListener(mBeliefListener);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index 5b1082a..3819e61 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -27,6 +27,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
@@ -39,6 +40,7 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.kotlin.BooleanFlowOperators;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
@@ -67,6 +69,7 @@
private final StatusBarStateController mStatusBarStateController;
private final KeyguardStateController mKeyguardStateController;
private final Lazy<ShadeInteractor> mShadeInteractorLazy;
+ private final Lazy<CommunalInteractor> mCommunalInteractorLazy;
private final BatteryController mBatteryController;
private final DockManager mDockManager;
private final DelayableExecutor mMainExecutor;
@@ -76,6 +79,7 @@
private int mState;
private boolean mShowingAod;
+ private boolean mShowingCommunalHub;
private boolean mScreenOn;
private boolean mSessionStarted;
private MotionEvent mPendingDownEvent;
@@ -145,7 +149,8 @@
@Main DelayableExecutor mainExecutor,
JavaAdapter javaAdapter,
SystemClock systemClock,
- Lazy<SelectedUserInteractor> userInteractor) {
+ Lazy<SelectedUserInteractor> userInteractor,
+ Lazy<CommunalInteractor> communalInteractorLazy) {
mFalsingDataProvider = falsingDataProvider;
mFalsingManager = falsingManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -160,6 +165,7 @@
mJavaAdapter = javaAdapter;
mSystemClock = systemClock;
mUserInteractor = userInteractor;
+ mCommunalInteractorLazy = communalInteractorLazy;
}
@Override
@@ -176,6 +182,13 @@
mShadeInteractorLazy.get().isQsExpanded(),
this::onQsExpansionChanged
);
+ final CommunalInteractor communalInteractor = mCommunalInteractorLazy.get();
+ mJavaAdapter.alwaysCollectFlow(
+ BooleanFlowOperators.INSTANCE.and(
+ communalInteractor.isCommunalEnabled(),
+ communalInteractor.isCommunalShowing()),
+ this::onShowingCommunalHubChanged
+ );
mBatteryController.addCallback(mBatteryListener);
mDockManager.addListener(mDockEventListener);
@@ -205,6 +218,12 @@
}
}
+ private void onShowingCommunalHubChanged(boolean isShowing) {
+ logDebug("REAL: onShowingCommunalHubChanged(" + isShowing + ")");
+ mShowingCommunalHub = isShowing;
+ updateSessionActive();
+ }
+
@Override
public boolean shouldEnforceBouncer() {
return false;
@@ -333,7 +352,10 @@
}
private boolean shouldSessionBeActive() {
- return mScreenOn && (mState == StatusBarState.KEYGUARD) && !mShowingAod;
+ return mScreenOn
+ && (mState == StatusBarState.KEYGUARD)
+ && !mShowingAod
+ && !mShowingCommunalHub;
}
private void updateSessionActive() {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index e0ce3db..c7a47b1 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -18,12 +18,14 @@
import static android.content.ClipDescription.CLASSIFICATION_COMPLETE;
+import static com.android.systemui.Flags.clipboardNoninteractiveOnLockscreen;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ENTERED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_UPDATED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_TOAST_SHOWN;
import static com.google.android.setupcompat.util.WizardManagerHelper.SETTINGS_SECURE_USER_SETUP_COMPLETE;
+import android.app.KeyguardManager;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
@@ -57,6 +59,7 @@
private final Provider<ClipboardOverlayController> mOverlayProvider;
private final ClipboardToast mClipboardToast;
private final ClipboardManager mClipboardManager;
+ private final KeyguardManager mKeyguardManager;
private final UiEventLogger mUiEventLogger;
private ClipboardOverlay mClipboardOverlay;
@@ -65,11 +68,13 @@
Provider<ClipboardOverlayController> clipboardOverlayControllerProvider,
ClipboardToast clipboardToast,
ClipboardManager clipboardManager,
+ KeyguardManager keyguardManager,
UiEventLogger uiEventLogger) {
mContext = context;
mOverlayProvider = clipboardOverlayControllerProvider;
mClipboardToast = clipboardToast;
mClipboardManager = clipboardManager;
+ mKeyguardManager = keyguardManager;
mUiEventLogger = uiEventLogger;
}
@@ -92,7 +97,9 @@
return;
}
- if (!isUserSetupComplete() // user should not access intents from this state
+ // user should not access intents before setup or while device is locked
+ if ((clipboardNoninteractiveOnLockscreen() && mKeyguardManager.isDeviceLocked())
+ || !isUserSetupComplete()
|| clipData == null // shouldn't happen, but just in case
|| clipData.getItemCount() == 0) {
if (shouldShowToast(clipData)) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
index 201be51..e2fed6d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
@@ -21,8 +21,8 @@
import com.android.systemui.log.dagger.CommunalTableLog
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.MediaData
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 636ea42..988393e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -75,7 +75,7 @@
mediaRepository: CommunalMediaRepository,
smartspaceRepository: SmartspaceRepository,
keyguardInteractor: KeyguardInteractor,
- private val communalSettingsInteractor: CommunalSettingsInteractor,
+ communalSettingsInteractor: CommunalSettingsInteractor,
private val appWidgetHost: CommunalAppWidgetHost,
private val editWidgetsActivityStarter: EditWidgetsActivityStarter,
@CommunalLog logBuffer: LogBuffer,
@@ -89,8 +89,7 @@
val editModeOpen: StateFlow<Boolean> = _editModeOpen.asStateFlow()
/** Whether communal features are enabled. */
- val isCommunalEnabled: Boolean
- get() = communalSettingsInteractor.isCommunalEnabled.value
+ val isCommunalEnabled: StateFlow<Boolean> = communalSettingsInteractor.isCommunalEnabled
/** Whether communal features are enabled and available. */
val isCommunalAvailable: Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt
index 2d9dd50..1a323a75 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt
@@ -47,7 +47,7 @@
private var communalTutorialIndicatorHandle: DisposableHandle? = null
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!communalInteractor.isCommunalEnabled) {
+ if (!communalInteractor.isCommunalEnabled.value) {
return
}
val padding =
@@ -79,7 +79,7 @@
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!communalInteractor.isCommunalEnabled) {
+ if (!communalInteractor.isCommunalEnabled.value) {
return
}
communalTutorialIndicatorHandle =
@@ -90,7 +90,7 @@
}
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!communalInteractor.isCommunalEnabled) {
+ if (!communalInteractor.isCommunalEnabled.value) {
return
}
val tutorialIndicatorId = R.id.communal_tutorial_indicator
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index dee7a0c..8a7b5eb 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -23,7 +23,7 @@
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.communal.widgets.WidgetConfigurator
-import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHost
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index efbb11e..bfe751a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -25,7 +25,7 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
-import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.dagger.MediaModule
import javax.inject.Inject
import javax.inject.Named
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index febfd4c..fc9a7df 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -24,9 +24,9 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.media.controls.ui.MediaHost
-import com.android.systemui.media.controls.ui.MediaHostState
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.dagger.MediaModule
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamMediaEntryComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamMediaEntryComplication.java
index 6a72785..bdefc4d 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/DreamMediaEntryComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/DreamMediaEntryComplication.java
@@ -28,7 +28,7 @@
import com.android.systemui.complication.dagger.DreamMediaEntryComplicationComponent;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.media.controls.ui.MediaCarouselController;
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController;
import com.android.systemui.media.dream.MediaDreamComplication;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/NotifInflation.kt
similarity index 63%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/dagger/qualifiers/NotifInflation.kt
index db2cdfa..231fb2d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/NotifInflation.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.dagger.qualifiers
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import javax.inject.Qualifier
-var Kosmos.mediaHierarchyManager by Fixture { mock<MediaHierarchyManager>() }
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class NotifInflation
diff --git a/packages/SystemUI/src/com/android/systemui/display/DisplayExtensions.kt b/packages/SystemUI/src/com/android/systemui/display/DisplayExtensions.kt
new file mode 100644
index 0000000..0482bd8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/DisplayExtensions.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.systemui.display
+
+import android.graphics.Rect
+import android.view.Display
+import android.view.DisplayInfo
+
+val Display.naturalBounds: Rect
+ get() {
+ val outDisplayInfo = DisplayInfo()
+ getDisplayInfo(outDisplayInfo)
+ return Rect(
+ /* left = */ 0,
+ /* top = */ 0,
+ /* right = */ outDisplayInfo.naturalWidth,
+ /* bottom = */ outDisplayInfo.naturalHeight
+ )
+ }
+
+val Display.naturalWidth: Int
+ get() {
+ val outDisplayInfo = DisplayInfo()
+ getDisplayInfo(outDisplayInfo)
+ return outDisplayInfo.naturalWidth
+ }
+
+val Display.naturalHeight: Int
+ get() {
+ val outDisplayInfo = DisplayInfo()
+ getDisplayInfo(outDisplayInfo)
+ return outDisplayInfo.naturalHeight
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt
index efbd59f..2fe1dd4 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt
@@ -19,6 +19,7 @@
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
+import android.app.NotificationManager.IMPORTANCE_DEFAULT
import android.content.Context
import android.util.Log
import com.android.systemui.CoreStartable
@@ -150,7 +151,7 @@
val title = "Invalid flag dependencies: ${unmet.size}"
val details = unmet.joinToString("\n") { it.shortUnmetString() }
Log.e("FlagDependencies", "$title:\n$details")
- val channel = NotificationChannel("FLAGS", "Flags", NotificationManager.IMPORTANCE_DEFAULT)
+ val channel = NotificationChannel(CHANNEL_ID, CHANNEL_NAME, IMPORTANCE_DEFAULT)
val notification =
Notification.Builder(context, channel.id)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
@@ -160,7 +161,18 @@
.setVisibility(Notification.VISIBILITY_PUBLIC)
.build()
notifManager.createNotificationChannel(channel)
- notifManager.notify("flags", 0, notification)
+ notifManager.notify(NOTIF_TAG, NOTIF_ID, notification)
+ }
+
+ override fun onCollected(all: List<FlagDependenciesBase.Dependency>) {
+ notifManager.cancel(NOTIF_TAG, NOTIF_ID)
+ }
+
+ companion object {
+ private const val CHANNEL_ID = "FLAGS"
+ private const val CHANNEL_NAME = "Flags"
+ private const val NOTIF_TAG = "FlagDependenciesNotifier"
+ private const val NOTIF_ID = 0
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 56162ae..aae73bc 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -324,9 +324,6 @@
// TODO(b/254512673): Tracking Bug
@JvmField val DREAM_MEDIA_TAP_TO_OPEN = unreleasedFlag("dream_media_tap_to_open")
- // TODO(b/263272731): Tracking Bug
- val MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE = releasedFlag("media_ttt_receiver_success_ripple")
-
// TODO(b/266157412): Tracking Bug
val MEDIA_RETAIN_SESSIONS = unreleasedFlag("media_retain_sessions")
@@ -477,11 +474,6 @@
val WARN_ON_BLOCKING_BINDER_TRANSACTIONS =
unreleasedFlag("warn_on_blocking_binder_transactions")
- // TODO(b/283071711): Tracking bug
- @JvmField
- val TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK =
- unreleasedFlag("trim_resources_with_background_trim_on_lock")
-
// TODO:(b/283203305): Tracking bug
@JvmField val TRIM_FONT_CACHES_AT_UNLOCK = unreleasedFlag("trim_font_caches_on_unlock")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
index c52ca68..e101b0a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
@@ -32,13 +32,13 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.utils.GlobalWindowManager
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
-import javax.inject.Inject
/**
* Releases cached resources on allocated by keyguard.
@@ -62,7 +62,7 @@
override fun start() {
Log.d(LOG_TAG, "Resource trimmer registered.")
- if (featureFlags.isEnabled(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)) {
+ if (com.android.systemui.Flags.trimResourcesWithBackgroundTrimAtLock()) {
applicationScope.launch(bgDispatcher) {
// We need to wait for the AoD transition (and animation) to complete.
// This means we're waiting for isDreaming (== implies isDoze) and dozeAmount == 1f
@@ -107,19 +107,16 @@
@WorkerThread
private fun onWakefulnessUpdated(
- isAsleep: Boolean,
- isDreaming: Boolean,
- isDozingFully: Boolean
+ isAsleep: Boolean,
+ isDreaming: Boolean,
+ isDozingFully: Boolean
) {
- if (!featureFlags.isEnabled(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)) {
+ if (!com.android.systemui.Flags.trimResourcesWithBackgroundTrimAtLock()) {
return
}
if (DEBUG) {
- Log.d(
- LOG_TAG,
- "isAsleep: $isAsleep Dreaming: $isDreaming DozeAmount: $isDozingFully"
- )
+ Log.d(LOG_TAG, "isAsleep: $isAsleep Dreaming: $isDreaming DozeAmount: $isDozingFully")
}
// There are three scenarios:
// * No dozing and no AoD at all - where we go directly to ASLEEP with isDreaming = false
@@ -129,8 +126,7 @@
// * AoD - where we go to ASLEEP with iDreaming = true and dozeAmount slowly increases
// to 1f
val dozeDisabledAndScreenOff = isAsleep && !isDreaming
- val dozeEnabledAndDozeAnimationCompleted =
- isAsleep && isDreaming && isDozingFully
+ val dozeEnabledAndDozeAnimationCompleted = isAsleep && isDreaming && isDozingFully
if (dozeDisabledAndScreenOff || dozeEnabledAndDozeAnimationCompleted) {
Trace.beginSection("ResourceTrimmer#trimMemory")
Log.d(LOG_TAG, "SysUI asleep, trimming memory.")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index ed48848..dc1f33d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -25,6 +25,7 @@
import android.view.HapticFeedbackConstants
import android.view.View
import android.view.View.OnLayoutChangeListener
+import android.view.View.VISIBLE
import android.view.ViewGroup
import android.view.ViewGroup.OnHierarchyChangeListener
import android.view.ViewPropertyAnimator
@@ -66,6 +67,7 @@
import com.android.systemui.util.ui.stopAnimating
import com.android.systemui.util.ui.value
import javax.inject.Provider
+import kotlin.math.min
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
@@ -345,7 +347,7 @@
}
}
- onLayoutChangeListener = OnLayoutChange(viewModel, burnInParams)
+ onLayoutChangeListener = OnLayoutChange(viewModel, childViews, burnInParams)
view.addOnLayoutChangeListener(onLayoutChangeListener)
// Views will be added or removed after the call to bind(). This is needed to avoid many
@@ -405,6 +407,7 @@
private class OnLayoutChange(
private val viewModel: KeyguardRootViewModel,
+ private val childViews: Map<Int, View>,
private val burnInParams: MutableStateFlow<BurnInParameters>,
) : OnLayoutChangeListener {
override fun onLayoutChange(
@@ -418,7 +421,7 @@
oldRight: Int,
oldBottom: Int
) {
- view.findViewById<View>(R.id.nssl_placeholder)?.let { notificationListPlaceholder ->
+ childViews[R.id.nssl_placeholder]?.let { notificationListPlaceholder ->
// After layout, ensure the notifications are positioned correctly
viewModel.onNotificationContainerBoundsChanged(
notificationListPlaceholder.top.toFloat(),
@@ -426,10 +429,34 @@
)
}
- view.findViewById<View>(R.id.keyguard_status_view)?.let { statusView ->
- burnInParams.update { current -> current.copy(statusViewTop = statusView.top) }
+ burnInParams.update { current ->
+ current.copy(
+ minViewY =
+ if (migrateClocksToBlueprint()) {
+ // To ensure burn-in doesn't enroach the top inset, get the min top Y
+ childViews.entries.fold(Int.MAX_VALUE) { currentMin, (viewId, view) ->
+ min(
+ currentMin,
+ if (!isUserVisible(view)) {
+ Int.MAX_VALUE
+ } else {
+ view.getTop()
+ }
+ )
+ }
+ } else {
+ childViews[R.id.keyguard_status_view]?.top ?: 0
+ }
+ )
}
}
+
+ private fun isUserVisible(view: View): Boolean {
+ return view.id != R.id.burn_in_layer &&
+ view.visibility == VISIBLE &&
+ view.width > 0 &&
+ view.height > 0
+ }
}
suspend fun bindAodNotifIconVisibility(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
index 390b39f..6e8605b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
@@ -34,7 +34,7 @@
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.keyguard.KeyguardViewConfigurator
import com.android.systemui.keyguard.shared.model.KeyguardSection
-import com.android.systemui.media.controls.ui.KeyguardMediaController
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.shade.NotificationPanelViewController
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
index b12a8a8..21e9455 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
@@ -30,7 +30,7 @@
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
-import com.android.systemui.media.controls.ui.KeyguardMediaController
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index 6fcbf48..8665aab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -158,9 +158,9 @@
val burnInY = MathUtils.lerp(0, burnIn.translationY, interpolated).toInt()
val translationY =
if (Flags.migrateClocksToBlueprint()) {
- burnInY
+ max(params.topInset - params.minViewY, burnInY)
} else {
- max(params.topInset, params.statusViewTop + burnInY) - params.statusViewTop
+ max(params.topInset, params.minViewY + burnInY) - params.minViewY
}
BurnInModel(
@@ -194,8 +194,8 @@
val clockControllerProvider: Provider<ClockController>? = null,
/** System insets that keyguard needs to stay out of */
val topInset: Int = 0,
- /** Status view top, without translation added in */
- val statusViewTop: Int = 0,
+ /** The min y-value of the visible elements on lockscreen */
+ val minViewY: Int = Int.MAX_VALUE,
/** The current y translation of the view */
val translationY: () -> Float? = { null }
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
index a0a77fb..c409028 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
@@ -25,10 +25,13 @@
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
/**
* Breaks down AOD->LOCKSCREEN transition into discrete steps for corresponding views to consume.
@@ -39,6 +42,7 @@
@Inject
constructor(
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+ shadeInteractor: ShadeInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
@@ -73,11 +77,22 @@
}
val notificationAlpha: Flow<Float> =
- transitionAnimation.sharedFlow(
- duration = 500.milliseconds,
- onStep = { it },
- onCancel = { 1f },
- )
+ combine(
+ shadeInteractor.shadeExpansion.map { it > 0f },
+ shadeInteractor.qsExpansion.map { it > 0f },
+ transitionAnimation.sharedFlow(
+ duration = 500.milliseconds,
+ onStep = { it },
+ onCancel = { 1f },
+ ),
+ ) { isShadeExpanded, isQsExpanded, alpha ->
+ if (isShadeExpanded || isQsExpanded) {
+ // One example of this happening is dragging a notification while pulsing on AOD
+ 1f
+ } else {
+ alpha
+ }
+ }
val shortcutsAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 3511933..921eb66 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.graphics.Point
+import android.util.MathUtils
import android.view.View.VISIBLE
import com.android.systemui.Flags.newAodTransition
import com.android.systemui.common.shared.model.NotificationContainerBounds
@@ -33,6 +34,7 @@
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.ui.StateToValue
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
@@ -43,6 +45,7 @@
import com.android.systemui.util.ui.toAnimatedValueFlow
import com.android.systemui.util.ui.zip
import javax.inject.Inject
+import kotlin.math.max
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -63,7 +66,7 @@
private val dozeParameters: DozeParameters,
private val keyguardInteractor: KeyguardInteractor,
private val communalInteractor: CommunalInteractor,
- keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
private val alternateBouncerToGoneTransitionViewModel:
AlternateBouncerToGoneTransitionViewModel,
@@ -87,6 +90,7 @@
private val screenOffAnimationController: ScreenOffAnimationController,
private val aodBurnInViewModel: AodBurnInViewModel,
private val aodAlphaViewModel: AodAlphaViewModel,
+ private val shadeInteractor: ShadeInteractor,
) {
val burnInLayerVisibility: Flow<Int> =
@@ -102,6 +106,16 @@
.onStart { emit(false) }
.distinctUntilChanged()
+ private val alphaOnShadeExpansion: Flow<Float> =
+ combine(
+ shadeInteractor.qsExpansion,
+ shadeInteractor.shadeExpansion,
+ ) { qsExpansion, shadeExpansion ->
+ // Fade out quickly as the shade expands
+ 1f - MathUtils.constrainedMap(0f, 1f, 0f, 0.2f, max(qsExpansion, shadeExpansion))
+ }
+ .distinctUntilChanged()
+
/** Last point that the root view was tapped */
val lastRootViewTapPosition: Flow<Point?> = keyguardInteractor.lastRootViewTapPosition
@@ -119,10 +133,12 @@
fun alpha(viewState: ViewStateAccessor): Flow<Float> {
return combine(
communalInteractor.isIdleOnCommunal,
+ keyguardTransitionInteractor.transitionValue(GONE).onStart { emit(0f) },
// The transitions are mutually exclusive, so they are safe to merge to get the last
// value emitted by any of them. Do not add flows that cannot make this guarantee.
merge(
aodAlphaViewModel.alpha,
+ alphaOnShadeExpansion,
keyguardInteractor.dismissAlpha.filterNotNull(),
alternateBouncerToGoneTransitionViewModel.lockscreenAlpha,
aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
@@ -140,11 +156,12 @@
primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha,
)
.onStart { emit(1f) }
- ) { isIdleOnCommunal, alpha ->
- if (isIdleOnCommunal) {
+ ) { isIdleOnCommunal, goneValue, alpha ->
+ if (isIdleOnCommunal || goneValue == 1f) {
// Keyguard should not show while the communal hub is fully visible. This check
// is added since at the moment, closing the notification shade will cause the
- // keyguard alpha to be set back to 1.
+ // keyguard alpha to be set back to 1. Also ensure keyguard is never visible
+ // when GONE.
0f
} else {
alpha
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index ac579d6..baf3e64 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -333,7 +333,7 @@
/**
* Provides a buffer for our connections and disconnections to MediaBrowserService.
*
- * See {@link com.android.systemui.media.controls.resume.ResumeMediaBrowser}.
+ * See {@link com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser}.
*/
@Provides
@SysUISingleton
@@ -345,7 +345,7 @@
/**
* Provides a buffer for updates to the media carousel.
*
- * See {@link com.android.systemui.media.controls.ui.MediaCarouselController}.
+ * See {@link com.android.systemui.media.controls.ui.controller.MediaCarouselController}.
*/
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java
index 1c00c93..901559b 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java
@@ -26,7 +26,8 @@
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for {@link com.android.systemui.media.controls.resume.ResumeMediaBrowser}
+ * A {@link LogBuffer} for
+ * {@link com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser}
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java
index 86a916e..abbfd4f 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java
@@ -26,7 +26,8 @@
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for {@link com.android.systemui.media.controls.ui.MediaCarouselController}
+ * A {@link LogBuffer} for
+ * {@link com.android.systemui.media.controls.ui.controller.MediaCarouselController}
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java
index 98e6556..0239caa 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java
@@ -26,7 +26,8 @@
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for {@link com.android.systemui.media.controls.pipeline.MediaTimeoutLogger}
+ * A {@link LogBuffer} for
+ * {@link com.android.systemui.media.controls.domain.pipeline.MediaTimeoutLogger}
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java
index dde0ee0..27a6a64 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java
@@ -26,7 +26,7 @@
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for {@link com.android.systemui.media.controls.ui.MediaViewLogger}
+ * A {@link LogBuffer} for {@link com.android.systemui.media.controls.ui.controller.MediaViewLogger}
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatest.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
index 789ef40..ad70db5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import javax.inject.Inject
/** Combines [MediaDataManager.Listener] events with [MediaDeviceManager.Listener] events. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilter.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilter.kt
index 185a783..bc539ef 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilter.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.content.Context
import android.content.pm.UserInfo
@@ -24,9 +24,9 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.EXTRA_KEY_TRIGGER_RESUME
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_RESUME
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.settings.UserTracker
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt
index 47df3b7..84a9690 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.annotation.SuppressLint
import android.app.BroadcastOptions
@@ -66,17 +66,17 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.models.player.MediaAction
-import com.android.systemui.media.controls.models.player.MediaButton
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
-import com.android.systemui.media.controls.models.player.MediaViewHolder
-import com.android.systemui.media.controls.models.recommendation.EXTRA_KEY_TRIGGER_SOURCE
-import com.android.systemui.media.controls.models.recommendation.EXTRA_VALUE_TRIGGER_PERIODIC
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaDataProvider
-import com.android.systemui.media.controls.resume.MediaResumeListener
-import com.android.systemui.media.controls.resume.ResumeMediaBrowser
+import com.android.systemui.media.controls.domain.resume.MediaResumeListener
+import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
+import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_SOURCE
+import com.android.systemui.media.controls.shared.model.EXTRA_VALUE_TRIGGER_PERIODIC
+import com.android.systemui.media.controls.shared.model.MediaAction
+import com.android.systemui.media.controls.shared.model.MediaButton
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaDataProvider
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaDataUtils
import com.android.systemui.media.controls.util.MediaFlags
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index dcbf670..42d68ba 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.bluetooth.BluetoothLeBroadcast
import android.bluetooth.BluetoothLeBroadcastMetadata
@@ -37,8 +37,9 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.util.LocalMediaManagerFactory
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaDataUtils
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilter.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt
index 6a8ffb7..b2a8f2e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.content.ComponentName
import android.content.Context
@@ -25,8 +25,8 @@
import android.util.Log
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -199,9 +199,7 @@
packageControllers.put(controller.packageName, tokens)
}
}
- controllers?.map { TokenId(it.sessionToken) }?.let {
- tokensWithNotifications.retainAll(it)
- }
+ controllers?.map { TokenId(it.sessionToken) }?.let { tokensWithNotifications.retainAll(it) }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
index ed4eef9..29f3967 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.media.session.MediaController
import android.media.session.MediaSession
@@ -23,8 +23,8 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutLogger.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutLogger.kt
index 534241e..c50c46a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.media.session.PlaybackState
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaBrowserFactory.java b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaBrowserFactory.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaBrowserFactory.java
rename to packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaBrowserFactory.java
index 00620b5..2c45ddb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaBrowserFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaBrowserFactory.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume;
+package com.android.systemui.media.controls.domain.resume;
import android.content.ComponentName;
import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt
index 23ee00d..e4047e5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume
+package com.android.systemui.media.controls.domain.resume
import android.content.BroadcastReceiver
import android.content.ComponentName
@@ -34,9 +34,9 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
-import com.android.systemui.media.controls.pipeline.RESUME_MEDIA_TIMEOUT
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.RESUME_MEDIA_TIMEOUT
+import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.settings.UserTracker
import com.android.systemui.tuner.TunerService
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowser.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowser.java
rename to packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowser.java
index ceaccaf..b2960cd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowser.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowser.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume;
+package com.android.systemui.media.controls.domain.resume;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserFactory.java b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserFactory.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserFactory.java
rename to packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserFactory.java
index e374191..50eb776 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserFactory.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume;
+package com.android.systemui.media.controls.domain.resume;
import android.annotation.UserIdInt;
import android.content.ComponentName;
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserLogger.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserLogger.kt
index 888b9c7..ce2a080 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume
+package com.android.systemui.media.controls.domain.resume
import android.content.ComponentName
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt
index 5caa27f..4fa7cb5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt
@@ -11,10 +11,10 @@
* 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
+ * limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.shared.model
import android.app.PendingIntent
import android.graphics.drawable.Drawable
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaData.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaData.kt
index ae03f27..52c605f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaData.kt
@@ -11,10 +11,10 @@
* 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
+ * limitations under the License.
*/
-package com.android.systemui.media.controls.models.recommendation
+package com.android.systemui.media.controls.shared.model
import android.app.smartspace.SmartspaceAction
import android.content.Context
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaDataProvider.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaDataProvider.kt
index cacb3e2..8726d81 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaDataProvider.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.recommendation
+package com.android.systemui.media.controls.shared.model
import android.app.smartspace.SmartspaceTarget
import android.util.Log
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/AnimationBindHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandler.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/AnimationBindHandler.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandler.kt
index f5cc043..8258059 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/AnimationBindHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandler.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.graphics.drawable.Animatable2
import android.graphics.drawable.Drawable
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
index 952f9b8..21407f3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.animation.ArgbEvaluator
import android.animation.ValueAnimator
@@ -27,7 +27,7 @@
import com.android.internal.R
import com.android.internal.annotations.VisibleForTesting
import com.android.settingslib.Utils
-import com.android.systemui.media.controls.models.player.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.monet.ColorScheme
import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect
import com.android.systemui.surfaceeffects.ripple.MultiRippleController
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaColorSchemes.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MediaColorSchemes.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaColorSchemes.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MediaColorSchemes.kt
index 2a8362b..3c57c83 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaColorSchemes.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MediaColorSchemes.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import com.android.systemui.monet.ColorScheme
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MetadataAnimationHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandler.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/MetadataAnimationHandler.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandler.kt
index 1cdcf5e..98202c5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MetadataAnimationHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandler.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
index 8d918e7..34f7c4d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.binder
import android.animation.Animator
import android.animation.ObjectAnimator
@@ -24,7 +24,9 @@
import com.android.app.animation.Interpolators
import com.android.app.tracing.TraceStateLogger
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.media.controls.ui.SquigglyProgress
+import com.android.systemui.media.controls.ui.drawable.SquigglyProgress
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.res.R
private const val TAG = "SeekBarObserver"
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
index c629337..9206af2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.content.Context
import android.content.res.Configuration
@@ -31,6 +31,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.dagger.MediaModule.KEYGUARD
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerLogger.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerLogger.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerLogger.kt
index 0dd4b58..c0d9dc2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.view.ViewGroup
import com.android.systemui.log.LogBuffer
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 992eeca..b721236 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.app.PendingIntent
import android.content.Context
@@ -46,12 +46,15 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaViewHolder
-import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
-import com.android.systemui.media.controls.ui.MediaControlPanel.SMARTSPACE_CARD_DISMISS_EVENT
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.ui.controller.MediaControlPanel.SMARTSPACE_CARD_DISMISS_EVENT
+import com.android.systemui.media.controls.ui.view.MediaCarouselScrollHandler
+import com.android.systemui.media.controls.ui.view.MediaHostState
+import com.android.systemui.media.controls.ui.view.MediaScrollView
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.media.controls.util.SmallHash
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
index 3dc0000..ebf1c6a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index 7cf18ba..e8ad4d3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui;
+package com.android.systemui.media.controls.ui.controller;
import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS;
import static com.android.systemui.Flags.legacyLeAudioSharing;
-import static com.android.systemui.media.controls.models.recommendation.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS;
+import static com.android.systemui.media.controls.shared.model.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS;
import android.animation.Animator;
import android.animation.AnimatorInflater;
@@ -90,17 +90,21 @@
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.media.controls.models.GutsViewHolder;
-import com.android.systemui.media.controls.models.player.MediaAction;
-import com.android.systemui.media.controls.models.player.MediaButton;
-import com.android.systemui.media.controls.models.player.MediaData;
-import com.android.systemui.media.controls.models.player.MediaDeviceData;
-import com.android.systemui.media.controls.models.player.MediaViewHolder;
-import com.android.systemui.media.controls.models.player.SeekBarObserver;
-import com.android.systemui.media.controls.models.player.SeekBarViewModel;
-import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder;
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.shared.model.MediaAction;
+import com.android.systemui.media.controls.shared.model.MediaButton;
+import com.android.systemui.media.controls.shared.model.MediaData;
+import com.android.systemui.media.controls.shared.model.MediaDeviceData;
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData;
+import com.android.systemui.media.controls.ui.animation.AnimationBindHandler;
+import com.android.systemui.media.controls.ui.animation.ColorSchemeTransition;
+import com.android.systemui.media.controls.ui.animation.MediaColorSchemesKt;
+import com.android.systemui.media.controls.ui.animation.MetadataAnimationHandler;
+import com.android.systemui.media.controls.ui.binder.SeekBarObserver;
+import com.android.systemui.media.controls.ui.view.GutsViewHolder;
+import com.android.systemui.media.controls.ui.view.MediaViewHolder;
+import com.android.systemui.media.controls.ui.view.RecommendationViewHolder;
+import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel;
import com.android.systemui.media.controls.util.MediaDataUtils;
import com.android.systemui.media.controls.util.MediaFlags;
import com.android.systemui.media.controls.util.MediaUiEventLogger;
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index 35e0271..3b989d9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
@@ -42,7 +42,8 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHostStatesManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManager.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHostStatesManager.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManager.kt
index 1f711cf..8660d12 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHostStatesManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManager.kt
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import com.android.app.tracing.traceSection
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.util.animation.MeasurementOutput
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index 962764c..ad7990b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -11,10 +11,10 @@
* 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
+ * limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.content.Context
import android.content.res.Configuration
@@ -22,10 +22,11 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
import com.android.app.tracing.traceSection
-import com.android.systemui.media.controls.models.GutsViewHolder
-import com.android.systemui.media.controls.models.player.MediaViewHolder
-import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
-import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.calculateAlpha
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController.Companion.calculateAlpha
+import com.android.systemui.media.controls.ui.view.GutsViewHolder
+import com.android.systemui.media.controls.ui.view.MediaHostState
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt
index 3ff23159..1514db3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/IlluminationDrawable.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/IlluminationDrawable.kt
index 5aa6824..260d300 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/IlluminationDrawable.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.drawable
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/LightSourceDrawable.kt
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/LightSourceDrawable.kt
index 6ee072d..e4ce135 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/LightSourceDrawable.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.drawable
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgress.kt
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgress.kt
index 47df021..c417fe6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgress.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.drawable
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/GutsViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/GutsViewHolder.kt
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/media/controls/models/GutsViewHolder.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/view/GutsViewHolder.kt
index f5f5d38..a667c58 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/GutsViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/GutsViewHolder.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models
+package com.android.systemui.media.controls.ui.view
import android.content.res.ColorStateList
import android.util.Log
@@ -22,11 +22,11 @@
import android.view.ViewGroup
import android.widget.ImageButton
import android.widget.TextView
-import com.android.systemui.res.R
-import com.android.systemui.media.controls.ui.accentPrimaryFromScheme
-import com.android.systemui.media.controls.ui.surfaceFromScheme
-import com.android.systemui.media.controls.ui.textPrimaryFromScheme
+import com.android.systemui.media.controls.ui.animation.accentPrimaryFromScheme
+import com.android.systemui.media.controls.ui.animation.surfaceFromScheme
+import com.android.systemui.media.controls.ui.animation.textPrimaryFromScheme
import com.android.systemui.monet.ColorScheme
+import com.android.systemui.res.R
/**
* A view holder for the guts menu of a media player. The guts are shown when the user long-presses
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt
index 038582c..c033e46 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.view
import android.graphics.Outline
import android.util.MathUtils
@@ -31,6 +31,7 @@
import com.android.settingslib.Utils
import com.android.systemui.Gefingerpoken
import com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS
+import com.android.systemui.media.controls.ui.controller.MediaControlPanel
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PageIndicator
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
index 437218f..d92168b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
@@ -14,15 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.view
import android.graphics.Rect
import android.util.ArraySet
import android.view.View
import android.view.View.OnAttachStateChangeListener
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaHostStatesManager
+import com.android.systemui.media.controls.ui.controller.MediaLocation
import com.android.systemui.util.animation.DisappearParameters
import com.android.systemui.util.animation.MeasurementInput
import com.android.systemui.util.animation.MeasurementOutput
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaScrollView.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaScrollView.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaScrollView.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaScrollView.kt
index 10512f1..b625908 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaScrollView.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.view
import android.content.Context
import android.os.SystemClock
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaViewHolder.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaViewHolder.kt
index 898eacf..35309ea 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaViewHolder.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.view
import android.view.LayoutInflater
import android.view.View
@@ -25,7 +25,6 @@
import android.widget.TextView
import androidx.constraintlayout.widget.Barrier
import com.android.internal.widget.CachingIconView
-import com.android.systemui.media.controls.models.GutsViewHolder
import com.android.systemui.res.R
import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView
import com.android.systemui.surfaceeffects.ripple.MultiRippleView
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/RecommendationViewHolder.kt
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/view/RecommendationViewHolder.kt
index 8ac8a2d..2d028d0213 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/RecommendationViewHolder.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.recommendation
+package com.android.systemui.media.controls.ui.view
import android.view.LayoutInflater
import android.view.View
@@ -23,9 +23,8 @@
import android.widget.SeekBar
import android.widget.TextView
import com.android.internal.widget.CachingIconView
+import com.android.systemui.media.controls.ui.drawable.IlluminationDrawable
import com.android.systemui.res.R
-import com.android.systemui.media.controls.models.GutsViewHolder
-import com.android.systemui.media.controls.ui.IlluminationDrawable
import com.android.systemui.util.animation.TransitionLayout
private const val TAG = "RecommendationViewHolder"
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
index 13d743f..cef1e69 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.viewmodel
import android.media.MediaMetadata
import android.media.session.MediaController
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/LocalMediaManagerFactory.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/LocalMediaManagerFactory.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/LocalMediaManagerFactory.kt
rename to packages/SystemUI/src/com/android/systemui/media/controls/util/LocalMediaManagerFactory.kt
index 1d3cfd2..5d113a9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/LocalMediaManagerFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/LocalMediaManagerFactory.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.util
import android.content.Context
import com.android.settingslib.bluetooth.LocalBluetoothManager
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
index 16a703a..f8c816c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
@@ -21,9 +21,9 @@
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.media.controls.ui.MediaLocation
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaLocation
import com.android.systemui.res.R
import java.lang.IllegalArgumentException
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 8f752e5..d84e5dd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -19,10 +19,10 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.LogBufferFactory;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
-import com.android.systemui.media.controls.ui.MediaHost;
-import com.android.systemui.media.controls.ui.MediaHostStatesManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
+import com.android.systemui.media.controls.ui.controller.MediaHostStatesManager;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.media.dream.dagger.MediaComplicationComponent;
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
import com.android.systemui.media.taptotransfer.MediaTttFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java
index b4153d7..7f6398b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java
@@ -21,9 +21,9 @@
import android.widget.FrameLayout;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
-import com.android.systemui.media.controls.ui.MediaHost;
-import com.android.systemui.media.controls.ui.MediaHostState;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
+import com.android.systemui.media.controls.ui.view.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHostState;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
index 08c626c..88a5f78 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
@@ -27,9 +27,9 @@
import com.android.systemui.complication.DreamMediaEntryComplication;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.media.controls.models.player.MediaData;
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.shared.model.MediaData;
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
index 8a565fa..03bc935 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
@@ -26,8 +26,4 @@
class MediaTttFlags @Inject constructor(private val featureFlags: FeatureFlags) {
/** */
fun isMediaTttEnabled(): Boolean = featureFlags.isEnabled(Flags.MEDIA_TAP_TO_TRANSFER)
-
- /** Check whether the flag for the receiver success state is enabled. */
- fun isMediaTttReceiverSuccessRippleEnabled(): Boolean =
- featureFlags.isEnabled(Flags.MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 6e9485e..416eae1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -266,8 +266,7 @@
// translation animation.
bounceAnimator.removeAllUpdateListeners()
bounceAnimator.cancel()
- if (removalReason == ChipStateReceiver.TRANSFER_TO_RECEIVER_SUCCEEDED.name &&
- mediaTttFlags.isMediaTttReceiverSuccessRippleEnabled()) {
+ if (removalReason == ChipStateReceiver.TRANSFER_TO_RECEIVER_SUCCEEDED.name) {
rippleController.expandToSuccessState(rippleView, onAnimationEnd)
animateViewTranslationAndFade(
iconContainerView,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index 8ff0e36..7413362 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -51,7 +51,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -207,6 +207,9 @@
mFooterActionsViewBinder = footerActionsViewBinder;
mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner();
mSceneContainerFlags = sceneContainerFlags;
+ if (mSceneContainerFlags.isEnabled()) {
+ mStatusBarState = StatusBarState.SHADE;
+ }
}
/**
@@ -506,10 +509,20 @@
}
}
- private boolean isKeyguardState() {
- // We want the freshest state here since otherwise we'll have some weirdness if earlier
- // listeners trigger updates
- return mStatusBarStateController.getCurrentOrUpcomingState() == KEYGUARD;
+ @VisibleForTesting
+ boolean isKeyguardState() {
+ if (mSceneContainerFlags.isEnabled()) {
+ return false;
+ } else {
+ // We want the freshest state here since otherwise we'll have some weirdness if earlier
+ // listeners trigger updates
+ return mStatusBarStateController.getCurrentOrUpcomingState() == KEYGUARD;
+ }
+ }
+
+ @VisibleForTesting
+ int getStatusBarState() {
+ return mStatusBarState;
}
private void updateShowCollapsedOnKeyguard() {
@@ -562,15 +575,17 @@
}
private void setKeyguardShowing(boolean keyguardShowing) {
- if (DEBUG) Log.d(TAG, "setKeyguardShowing " + keyguardShowing);
- mLastQSExpansion = -1;
+ if (!mSceneContainerFlags.isEnabled()) {
+ if (DEBUG) Log.d(TAG, "setKeyguardShowing " + keyguardShowing);
+ mLastQSExpansion = -1;
- if (mQSAnimator != null) {
- mQSAnimator.setOnKeyguard(keyguardShowing);
+ if (mQSAnimator != null) {
+ mQSAnimator.setOnKeyguard(keyguardShowing);
+ }
+
+ mFooter.setKeyguardShowing(keyguardShowing);
+ updateQsState();
}
-
- mFooter.setKeyguardShowing(keyguardShowing);
- updateQsState();
}
@Override
@@ -971,7 +986,7 @@
@Override
public void onStateChanged(int newState) {
- if (newState == mStatusBarState) {
+ if (mSceneContainerFlags.isEnabled() || newState == mStatusBarState) {
return;
}
mStatusBarState = newState;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index c3f5086..2440651 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -27,9 +27,9 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
-import com.android.systemui.media.controls.ui.MediaHost;
-import com.android.systemui.media.controls.ui.MediaHostState;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
+import com.android.systemui.media.controls.ui.view.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHostState;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 1c37510f..975c871 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -32,7 +32,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizerController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index f278dce..a8e88da 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -25,8 +25,8 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
index 4bad45f..16aa99e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
@@ -25,6 +25,8 @@
import com.android.systemui.qs.pipeline.data.repository.DefaultTilesRepository
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepositoryImpl
+import com.android.systemui.qs.pipeline.data.repository.MinimumTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.MinimumTilesResourceRepository
import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredBroadcastRepository
import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
@@ -81,6 +83,11 @@
impl: QSSettingsRestoredBroadcastRepository
): QSSettingsRestoredRepository
+ @Binds
+ abstract fun provideMinimumTilesRepository(
+ impl: MinimumTilesResourceRepository
+ ): MinimumTilesRepository
+
companion object {
/**
* Provides a logging buffer for all logs related to the new Quick Settings pipeline to log
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesRepository.kt
new file mode 100644
index 0000000..3a005c0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesRepository.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.systemui.qs.pipeline.data.repository
+
+import android.content.res.Resources
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/**
+ * Provides the minimum number of tiles required in QS. The default number of tiles should be at
+ * least this many.
+ */
+interface MinimumTilesRepository {
+ val minNumberOfTiles: Int
+}
+
+/**
+ * Minimum number of tiles using the corresponding resource. The value will be read once upon
+ * creation, as it's not expected to change.
+ */
+@SysUISingleton
+class MinimumTilesResourceRepository @Inject constructor(@Main resources: Resources) :
+ MinimumTilesRepository {
+ override val minNumberOfTiles: Int =
+ resources.getInteger(R.integer.quick_settings_min_num_tiles)
+}
+
+/** Provides a fixed minimum number of tiles. */
+class MinimumTilesFixedRepository(override val minNumberOfTiles: Int = 0) : MinimumTilesRepository
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
index 00ea0b5..214e9f0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
@@ -19,12 +19,12 @@
import android.annotation.UserIdInt
import android.content.res.Resources
import android.util.SparseArray
-import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.pipeline.data.model.RestoreData
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.res.R
import com.android.systemui.retail.data.repository.RetailModeRepository
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -68,6 +68,9 @@
suspend fun reconcileRestore(restoreData: RestoreData, currentAutoAdded: Set<TileSpec>)
+ /** Prepend the default list of tiles to the current set of tiles */
+ suspend fun prependDefault(@UserIdInt userId: Int)
+
companion object {
/** Position to indicate the end of the list */
const val POSITION_AT_END = -1
@@ -152,6 +155,12 @@
?.reconcileRestore(restoreData, currentAutoAdded)
}
+ override suspend fun prependDefault(
+ userId: Int,
+ ) {
+ userTileRepositories.get(userId)?.prependDefault()
+ }
+
companion object {
private const val DELIMITER = TilesSettingConverter.DELIMITER
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
index 152fd0f..8ad5cb2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
@@ -50,9 +50,8 @@
private val defaultTiles: List<TileSpec>
get() = defaultTilesRepository.defaultTiles
- private val changeEvents = MutableSharedFlow<ChangeAction>(
- extraBufferCapacity = CHANGES_BUFFER_SIZE
- )
+ private val changeEvents =
+ MutableSharedFlow<ChangeAction>(extraBufferCapacity = CHANGES_BUFFER_SIZE)
private lateinit var _tiles: StateFlow<List<TileSpec>>
@@ -163,6 +162,10 @@
changeEvents.emit(RestoreTiles(restoreData, currentAutoAdded))
}
+ suspend fun prependDefault() {
+ changeEvents.emit(PrependDefault(defaultTiles))
+ }
+
sealed interface ChangeAction {
fun apply(currentTiles: List<TileSpec>): List<TileSpec>
}
@@ -199,6 +202,12 @@
}
}
+ private data class PrependDefault(val defaultTiles: List<TileSpec>) : ChangeAction {
+ override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
+ return defaultTiles + currentTiles
+ }
+ }
+
private data class RestoreTiles(
val restoreData: RestoreData,
val currentAutoAdded: Set<TileSpec>,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 957cb1e..61896f0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -35,6 +35,7 @@
import com.android.systemui.qs.external.TileServiceKey
import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository
+import com.android.systemui.qs.pipeline.data.repository.MinimumTilesRepository
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
import com.android.systemui.qs.pipeline.domain.model.TileModel
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
@@ -131,6 +132,7 @@
private val tileSpecRepository: TileSpecRepository,
private val installedTilesComponentRepository: InstalledTilesComponentRepository,
private val userRepository: UserRepository,
+ private val minimumTilesRepository: MinimumTilesRepository,
private val customTileStatePersister: CustomTileStatePersister,
private val newQSTileFactory: Lazy<NewQSTileFactory>,
private val tileFactory: QSFactory,
@@ -255,17 +257,23 @@
val resolvedSpecs = newTileMap.keys.toList()
specsToTiles.clear()
specsToTiles.putAll(newTileMap)
- _currentSpecsAndTiles.value =
+ val newResolvedTiles =
newTileMap
.filter { it.value is TileOrNotInstalled.Tile }
.map {
TileModel(it.key, (it.value as TileOrNotInstalled.Tile).tile)
}
+
+ _currentSpecsAndTiles.value = newResolvedTiles
logger.logTilesNotInstalled(
newTileMap.filter { it.value is TileOrNotInstalled.NotInstalled }.keys,
newUser
)
- if (resolvedSpecs != newTileList) {
+ if (newResolvedTiles.size < minimumTilesRepository.minNumberOfTiles) {
+ // We ended up with not enough tiles (some may be not installed).
+ // Prepend the default set of tiles
+ launch { tileSpecRepository.prependDefault(currentUser.value) }
+ } else if (resolvedSpecs != newTileList) {
// There were some tiles that couldn't be created. Change the value in
// the
// repository
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
index 6e4f72d..72a5c46 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
@@ -84,18 +84,36 @@
*/
val qsHeight: Int
- sealed class State(
- val isVisible: Boolean,
- val expansion: Float,
- ) {
- data object CLOSED : State(false, 0f)
- data class Expanding(val progress: Float) : State(true, progress)
+ sealed interface State {
+
+ val isVisible: Boolean
+ val expansion: Float
+ val squishiness: Float
+
+ data object CLOSED : State {
+ override val isVisible = false
+ override val expansion = 0f
+ override val squishiness = 1f
+ }
+
+ /** State for expanding between QQS and QS */
+ data class Expanding(override val expansion: Float) : State {
+ override val isVisible = true
+ override val squishiness = 1f
+ }
+
+ /** State for appearing QQS from Lockscreen or Gone */
+ data class Unsquishing(override val squishiness: Float) : State {
+ override val isVisible = true
+ override val expansion = 0f
+ }
companion object {
// These are special cases of the expansion.
val QQS = Expanding(0f)
val QS = Expanding(1f)
+ /** Collapsing from QS to QQS. [progress] is 0f in QS and 1f in QQS. */
fun Collapsing(progress: Float) = Expanding(1f - progress)
}
}
@@ -232,7 +250,7 @@
setQsVisible(state.isVisible)
setExpanded(state.isVisible)
setListening(state.isVisible)
- setQsExpansion(state.expansion, 1f, 0f, 1f)
- setTransitionToFullShadeProgress(false, 1f, 1f)
+ setQsExpansion(state.expansion, 1f, 0f, state.squishiness)
+ setTransitionToFullShadeProgress(false, 1f, state.squishiness)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
index 4ccb18f..8408c51 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
@@ -29,6 +29,7 @@
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.Flags.SCENE_CONTAINER_ENABLED
import com.android.systemui.flags.RefactorFlagUtils
+import com.android.systemui.keyguard.shared.ComposeLockscreen
import com.android.systemui.media.controls.util.MediaInSceneContainerFlag
import dagger.Module
import dagger.Provides
@@ -45,6 +46,7 @@
sceneContainer() && // mainAconfigFlag
keyguardBottomAreaRefactor() &&
migrateClocksToBlueprint() &&
+ ComposeLockscreen.isEnabled &&
MediaInSceneContainerFlag.isEnabled &&
// NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer
ComposeFacade.isComposeAvailable()
@@ -65,6 +67,7 @@
sequenceOf(
FlagToken(FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor()),
FlagToken(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT, migrateClocksToBlueprint()),
+ ComposeLockscreen.token,
MediaInSceneContainerFlag.token,
// NOTE: Changes should also be made in isEnabled and @EnableSceneContainer
)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
index 7cff7ff..4c2c979 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
@@ -25,8 +25,8 @@
import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.core.view.updateMargins
-import com.android.systemui.res.R
import com.android.systemui.compose.ComposeFacade
+import com.android.systemui.res.R
/** A view that can serve as the root of the main SysUI window. */
open class WindowRootView(
@@ -71,6 +71,7 @@
override fun onApplyWindowInsets(windowInsets: WindowInsets): WindowInsets? {
val insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars())
+ val displayCutout = rootWindowInsets.displayCutout
if (fitsSystemWindows) {
val paddingChanged = insets.top != paddingTop || insets.bottom != paddingBottom
@@ -78,22 +79,23 @@
if (paddingChanged) {
setPadding(0, 0, 0, 0)
}
+
+ val pairInsets: Pair<Int, Int> =
+ layoutInsetsController.getinsets(windowInsets, displayCutout)
+ leftInset = pairInsets.first
+ rightInset = pairInsets.second
+ applyMargins()
} else {
val changed =
paddingLeft != 0 || paddingRight != 0 || paddingTop != 0 || paddingBottom != 0
if (changed) {
setPadding(0, 0, 0, 0)
}
- }
- leftInset = 0
- rightInset = 0
- val displayCutout = rootWindowInsets.displayCutout
- val pairInsets: Pair<Int, Int> =
- layoutInsetsController.getinsets(windowInsets, displayCutout)
- leftInset = pairInsets.first
- rightInset = pairInsets.second
- applyMargins()
+ leftInset = 0
+ rightInset = 0
+ }
+
return windowInsets
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 95d9bc4..7f06778 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -149,9 +149,9 @@
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.media.controls.ui.KeyguardMediaController;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index f7fed53..8f9cef3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -60,6 +60,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.scene.ui.view.WindowRootViewComponent;
import com.android.systemui.settings.UserTracker;
@@ -506,7 +507,7 @@
private void applyFitsSystemWindows(NotificationShadeWindowState state) {
boolean fitsSystemWindows = !state.isKeyguardShowingAndNotOccluded();
- if (mWindowRootView != null
+ if (!SceneContainerFlag.isEnabled() && mWindowRootView != null
&& mWindowRootView.getFitsSystemWindows() != fitsSystemWindows) {
mWindowRootView.setFitsSystemWindows(fitsSystemWindows);
mWindowRootView.requestApplyInsets();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index f7b9e4e..e7f9700 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -71,8 +71,8 @@
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.res.R;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
index d393f0d..4054a86 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
@@ -17,6 +17,8 @@
package com.android.systemui.shade
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shade.data.repository.PrivacyChipRepository
+import com.android.systemui.shade.data.repository.PrivacyChipRepositoryImpl
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.shade.data.repository.ShadeRepositoryImpl
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor
@@ -61,4 +63,8 @@
abstract fun bindsShadeAnimationInteractor(
sai: ShadeAnimationInteractorEmptyImpl
): ShadeAnimationInteractor
+
+ @Binds
+ @SysUISingleton
+ abstract fun bindsPrivacyChipRepository(impl: PrivacyChipRepositoryImpl): PrivacyChipRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index 86fdcee..5632766 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -18,6 +18,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.shade.data.repository.PrivacyChipRepository
+import com.android.systemui.shade.data.repository.PrivacyChipRepositoryImpl
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.shade.data.repository.ShadeRepositoryImpl
import com.android.systemui.shade.domain.interactor.BaseShadeInteractor
@@ -124,4 +126,8 @@
abstract fun bindsShadeViewController(
notificationPanelViewController: NotificationPanelViewController
): ShadeViewController
+
+ @Binds
+ @SysUISingleton
+ abstract fun bindsPrivacyChipRepository(impl: PrivacyChipRepositoryImpl): PrivacyChipRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/PrivacyChipRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/PrivacyChipRepository.kt
new file mode 100644
index 0000000..91c92cc8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/PrivacyChipRepository.kt
@@ -0,0 +1,133 @@
+/*
+ * 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.systemui.shade.data.repository
+
+import android.content.IntentFilter
+import android.os.UserHandle
+import android.safetycenter.SafetyCenterManager
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.privacy.PrivacyConfig
+import com.android.systemui.privacy.PrivacyItem
+import com.android.systemui.privacy.PrivacyItemController
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
+
+interface PrivacyChipRepository {
+ /** Whether or not the Safety Center is enabled. */
+ val isSafetyCenterEnabled: StateFlow<Boolean>
+
+ /** The list of PrivacyItems to be displayed by the privacy chip. */
+ val privacyItems: StateFlow<List<PrivacyItem>>
+
+ /** Whether or not mic & camera indicators are enabled in the device privacy config. */
+ val isMicCameraIndicationEnabled: StateFlow<Boolean>
+
+ /** Whether or not location indicators are enabled in the device privacy config. */
+ val isLocationIndicationEnabled: StateFlow<Boolean>
+}
+
+@SysUISingleton
+class PrivacyChipRepositoryImpl
+@Inject
+constructor(
+ @Application applicationScope: CoroutineScope,
+ private val privacyConfig: PrivacyConfig,
+ private val privacyItemController: PrivacyItemController,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ broadcastDispatcher: BroadcastDispatcher,
+ private val safetyCenterManager: SafetyCenterManager,
+) : PrivacyChipRepository {
+ override val isSafetyCenterEnabled: StateFlow<Boolean> =
+ broadcastDispatcher
+ .broadcastFlow(
+ filter =
+ IntentFilter().apply {
+ addAction(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED)
+ },
+ user = UserHandle.SYSTEM,
+ map = { _, _ -> safetyCenterManager.isSafetyCenterEnabled }
+ )
+ .onStart { emit(safetyCenterManager.isSafetyCenterEnabled) }
+ .flowOn(backgroundDispatcher)
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ override val privacyItems: StateFlow<List<PrivacyItem>> =
+ conflatedCallbackFlow {
+ val callback =
+ object : PrivacyItemController.Callback {
+ override fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) {
+ trySend(privacyItems)
+ }
+ }
+ privacyItemController.addCallback(callback)
+ awaitClose { privacyItemController.removeCallback(callback) }
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = emptyList(),
+ )
+
+ override val isMicCameraIndicationEnabled: StateFlow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : PrivacyConfig.Callback {
+ override fun onFlagMicCameraChanged(flag: Boolean) {
+ trySend(flag)
+ }
+ }
+ privacyConfig.addCallback(callback)
+ awaitClose { privacyConfig.removeCallback(callback) }
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = privacyItemController.micCameraAvailable,
+ )
+
+ override val isLocationIndicationEnabled: StateFlow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : PrivacyConfig.Callback {
+ override fun onFlagLocationChanged(flag: Boolean) {
+ trySend(flag)
+ }
+ }
+ privacyConfig.addCallback(callback)
+ awaitClose { privacyConfig.removeCallback(callback) }
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = privacyItemController.locationAvailable,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractor.kt
new file mode 100644
index 0000000..4c6c318
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractor.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.systemui.shade.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.privacy.PrivacyDialogController
+import com.android.systemui.privacy.PrivacyDialogControllerV2
+import com.android.systemui.privacy.PrivacyItem
+import com.android.systemui.shade.data.repository.PrivacyChipRepository
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+@SysUISingleton
+class PrivacyChipInteractor
+@Inject
+constructor(
+ @Application applicationScope: CoroutineScope,
+ private val repository: PrivacyChipRepository,
+ private val privacyDialogController: PrivacyDialogController,
+ private val privacyDialogControllerV2: PrivacyDialogControllerV2,
+ private val deviceProvisionedController: DeviceProvisionedController,
+) {
+ /** The list of PrivacyItems to be displayed by the privacy chip. */
+ val privacyItems: StateFlow<List<PrivacyItem>> = repository.privacyItems
+
+ /** Whether or not mic & camera indicators are enabled in the device privacy config. */
+ val isMicCameraIndicationEnabled: StateFlow<Boolean> = repository.isMicCameraIndicationEnabled
+
+ /** Whether or not location indicators are enabled in the device privacy config. */
+ val isLocationIndicationEnabled: StateFlow<Boolean> = repository.isLocationIndicationEnabled
+
+ /** Whether or not the privacy chip should be visible. */
+ val isChipVisible: StateFlow<Boolean> =
+ privacyItems
+ .map { it.isNotEmpty() }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ /** Whether or not the privacy chip is enabled in the device privacy config. */
+ val isChipEnabled: StateFlow<Boolean> =
+ combine(
+ isMicCameraIndicationEnabled,
+ isLocationIndicationEnabled,
+ ) { micCamera, location ->
+ micCamera || location
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ /** Notifies that the privacy chip was clicked. */
+ fun onPrivacyChipClicked(privacyChip: OngoingPrivacyChip) {
+ if (!deviceProvisionedController.isDeviceProvisioned) return
+
+ if (repository.isSafetyCenterEnabled.value) {
+ privacyDialogControllerV2.showDialog(privacyChip.context, privacyChip)
+ } else {
+ privacyDialogController.showDialog(privacyChip.context)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
index 314637e..700825d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
@@ -25,8 +25,10 @@
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.privacy.PrivacyItem
import com.android.systemui.res.R
-import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.shade.domain.interactor.PrivacyChipInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
import java.util.Date
@@ -50,9 +52,9 @@
constructor(
@Application private val applicationScope: CoroutineScope,
context: Context,
- sceneInteractor: SceneInteractor,
mobileIconsInteractor: MobileIconsInteractor,
val mobileIconsViewModel: MobileIconsViewModel,
+ private val privacyChipInteractor: PrivacyChipInteractor,
broadcastDispatcher: BroadcastDispatcher,
) {
/** True if there is exactly one mobile connection. */
@@ -64,6 +66,23 @@
.map { list -> list.map { it.subscriptionId } }
.stateIn(applicationScope, SharingStarted.WhileSubscribed(), emptyList())
+ /** The list of PrivacyItems to be displayed by the privacy chip. */
+ val privacyItems: StateFlow<List<PrivacyItem>> = privacyChipInteractor.privacyItems
+
+ /** Whether or not mic & camera indicators are enabled in the device privacy config. */
+ val isMicCameraIndicationEnabled: StateFlow<Boolean> =
+ privacyChipInteractor.isMicCameraIndicationEnabled
+
+ /** Whether or not location indicators are enabled in the device privacy config. */
+ val isLocationIndicationEnabled: StateFlow<Boolean> =
+ privacyChipInteractor.isLocationIndicationEnabled
+
+ /** Whether or not the privacy chip should be visible. */
+ val isPrivacyChipVisible: StateFlow<Boolean> = privacyChipInteractor.isChipVisible
+
+ /** Whether or not the privacy chip is enabled in the device privacy config. */
+ val isPrivacyChipEnabled: StateFlow<Boolean> = privacyChipInteractor.isChipEnabled
+
private val longerPattern = context.getString(R.string.abbrev_wday_month_day_no_year_alarm)
private val shorterPattern = context.getString(R.string.abbrev_month_day_no_year)
private val longerDateFormat = MutableStateFlow(getFormatFromPattern(longerPattern))
@@ -97,6 +116,11 @@
applicationScope.launch { updateDateTexts(false) }
}
+ /** Notifies that the privacy chip was clicked. */
+ fun onPrivacyChipClicked(privacyChip: OngoingPrivacyChip) {
+ privacyChipInteractor.onPrivacyChipClicked(privacyChip)
+ }
+
private fun updateDateTexts(invalidateFormats: Boolean) {
if (invalidateFormats) {
longerDateFormat.value = getFormatFromPattern(longerPattern)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
index 9af2d58..abdfa53 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
@@ -19,7 +19,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt b/packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt
new file mode 100644
index 0000000..384acc4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.systemui.slice
+
+import android.net.Uri
+import androidx.slice.Slice
+import androidx.slice.SliceViewManager
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+
+/**
+ * Returns updating [Slice] for a [sliceUri]. It's null when there is no slice available for the
+ * provided Uri. This can change overtime because of external changes (like device being
+ * connected/disconnected).
+ */
+fun SliceViewManager.sliceForUri(sliceUri: Uri): Flow<Slice?> =
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val callback = SliceViewManager.SliceCallback { launch { send(it) } }
+
+ val slice = bindSlice(sliceUri)
+ send(slice)
+ registerSliceCallback(sliceUri, callback)
+ awaitClose { unregisterSliceCallback(sliceUri, callback) }
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
index 0b470c1..9f098e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
@@ -4,7 +4,7 @@
import android.util.IndentingPrintWriter
import android.util.MathUtils
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeLockscreenInteractor
import com.android.systemui.statusbar.policy.ConfigurationController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 1dbd87e..a59d753 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -24,7 +24,7 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
@@ -68,7 +68,7 @@
private val mediaHierarchyManager: MediaHierarchyManager,
private val scrimTransitionController: LockscreenShadeScrimTransitionController,
private val keyguardTransitionControllerFactory:
- LockscreenShadeKeyguardTransitionController.Factory,
+ LockscreenShadeKeyguardTransitionController.Factory,
private val depthController: NotificationShadeDepthController,
private val context: Context,
private val splitShadeOverScrollerFactory: SplitShadeLockScreenOverScroller.Factory,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 9c4625e..d465973 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -30,9 +30,9 @@
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.models.player.MediaData;
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.shared.model.MediaData;
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData;
import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index f6d99bd..f960fca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -33,7 +33,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpHandler;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shade.NotificationPanelViewController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
index 16af9d9..072f56d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
-import static com.android.systemui.media.controls.pipeline.MediaDataManagerKt.isMediaNotification;
+import static com.android.systemui.media.controls.domain.pipeline.MediaDataManagerKt.isMediaNotification;
import android.os.RemoteException;
import android.service.notification.StatusBarNotification;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 913d5f6..e288e85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -43,7 +43,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ImageMessageConsumer;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.NotifInflation;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.InflationTask;
@@ -82,7 +82,7 @@
private final NotificationRemoteInputManager mRemoteInputManager;
private final NotifRemoteViewCache mRemoteViewCache;
private final ConversationNotificationProcessor mConversationProcessor;
- private final Executor mBgExecutor;
+ private final Executor mInflationExecutor;
private final SmartReplyStateInflater mSmartReplyStateInflater;
private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
private final NotificationContentInflaterLogger mLogger;
@@ -93,7 +93,7 @@
NotificationRemoteInputManager remoteInputManager,
ConversationNotificationProcessor conversationProcessor,
MediaFeatureFlag mediaFeatureFlag,
- @Background Executor bgExecutor,
+ @NotifInflation Executor inflationExecutor,
SmartReplyStateInflater smartRepliesInflater,
NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider,
NotificationContentInflaterLogger logger) {
@@ -101,7 +101,7 @@
mRemoteInputManager = remoteInputManager;
mConversationProcessor = conversationProcessor;
mIsMediaInQS = mediaFeatureFlag.getEnabled();
- mBgExecutor = bgExecutor;
+ mInflationExecutor = inflationExecutor;
mSmartReplyStateInflater = smartRepliesInflater;
mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider;
mLogger = logger;
@@ -138,7 +138,7 @@
cancelContentViewFrees(row, contentToBind);
AsyncInflationTask task = new AsyncInflationTask(
- mBgExecutor,
+ mInflationExecutor,
mInflateSynchronously,
/* reInflateFlags = */ contentToBind,
mRemoteViewCache,
@@ -157,7 +157,7 @@
if (mInflateSynchronously) {
task.onPostExecute(task.doInBackground());
} else {
- task.executeOnExecutor(mBgExecutor);
+ task.executeOnExecutor(mInflationExecutor);
}
}
@@ -208,7 +208,7 @@
}
apply(
- mBgExecutor,
+ mInflationExecutor,
inflateSynchronously,
result,
reInflateFlags,
@@ -416,7 +416,7 @@
}
private static CancellationSignal apply(
- Executor bgExecutor,
+ Executor inflationExecutor,
boolean inflateSynchronously,
InflationProgress result,
@InflationFlag int reInflateFlags,
@@ -447,7 +447,7 @@
}
};
logger.logAsyncTaskProgress(entry, "applying contracted view");
- applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
+ applyRemoteView(inflationExecutor, inflateSynchronously, result, reInflateFlags, flag,
remoteViewCache, entry, row, isNewView, remoteViewClickHandler, callback,
privateLayout, privateLayout.getContractedChild(),
privateLayout.getVisibleWrapper(
@@ -474,11 +474,10 @@
}
};
logger.logAsyncTaskProgress(entry, "applying expanded view");
- applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
- remoteViewCache, entry, row, isNewView, remoteViewClickHandler,
+ applyRemoteView(inflationExecutor, inflateSynchronously, result, reInflateFlags,
+ flag, remoteViewCache, entry, row, isNewView, remoteViewClickHandler,
callback, privateLayout, privateLayout.getExpandedChild(),
- privateLayout.getVisibleWrapper(
- NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,
+ privateLayout.getVisibleWrapper(VISIBLE_TYPE_EXPANDED), runningInflations,
applyCallback, logger);
}
}
@@ -502,11 +501,10 @@
}
};
logger.logAsyncTaskProgress(entry, "applying heads up view");
- applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
- remoteViewCache, entry, row, isNewView, remoteViewClickHandler,
+ applyRemoteView(inflationExecutor, inflateSynchronously, result, reInflateFlags,
+ flag, remoteViewCache, entry, row, isNewView, remoteViewClickHandler,
callback, privateLayout, privateLayout.getHeadsUpChild(),
- privateLayout.getVisibleWrapper(
- VISIBLE_TYPE_HEADSUP), runningInflations,
+ privateLayout.getVisibleWrapper(VISIBLE_TYPE_HEADSUP), runningInflations,
applyCallback, logger);
}
}
@@ -529,7 +527,7 @@
}
};
logger.logAsyncTaskProgress(entry, "applying public view");
- applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
+ applyRemoteView(inflationExecutor, inflateSynchronously, result, reInflateFlags, flag,
remoteViewCache, entry, row, isNewView, remoteViewClickHandler, callback,
publicLayout, publicLayout.getContractedChild(),
publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),
@@ -551,7 +549,7 @@
@VisibleForTesting
static void applyRemoteView(
- Executor bgExecutor,
+ Executor inflationExecutor,
boolean inflateSynchronously,
final InflationProgress result,
final @InflationFlag int reInflateFlags,
@@ -655,14 +653,14 @@
cancellationSignal = newContentView.applyAsync(
result.packageContext,
parentLayout,
- bgExecutor,
+ inflationExecutor,
listener,
remoteViewClickHandler);
} else {
cancellationSignal = newContentView.reapplyAsync(
result.packageContext,
existingView,
- bgExecutor,
+ inflationExecutor,
listener,
remoteViewClickHandler);
}
@@ -918,7 +916,7 @@
private final boolean mUsesIncreasedHeadsUpHeight;
private final @InflationFlag int mReInflateFlags;
private final NotifRemoteViewCache mRemoteViewCache;
- private final Executor mBgExecutor;
+ private final Executor mInflationExecutor;
private ExpandableNotificationRow mRow;
private Exception mError;
private RemoteViews.InteractionHandler mRemoteViewClickHandler;
@@ -930,7 +928,7 @@
private final NotificationContentInflaterLogger mLogger;
private AsyncInflationTask(
- Executor bgExecutor,
+ Executor inflationExecutor,
boolean inflateSynchronously,
@InflationFlag int reInflateFlags,
NotifRemoteViewCache cache,
@@ -948,7 +946,7 @@
NotificationContentInflaterLogger logger) {
mEntry = entry;
mRow = row;
- mBgExecutor = bgExecutor;
+ mInflationExecutor = inflationExecutor;
mInflateSynchronously = inflateSynchronously;
mReInflateFlags = reInflateFlags;
mRemoteViewCache = cache;
@@ -1067,7 +1065,7 @@
if (mError == null) {
// Logged in detail in apply.
mCancellationSignal = apply(
- mBgExecutor,
+ mInflationExecutor,
mInflateSynchronously,
result,
mReInflateFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index cfc433a..d269eda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -19,7 +19,7 @@
import android.util.Log
import android.view.View
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.media.controls.ui.KeyguardMediaController
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.SourceType
import com.android.systemui.statusbar.notification.collection.render.MediaContainerController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 715505d..8dfac86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -74,7 +74,7 @@
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
-import com.android.systemui.media.controls.ui.KeyguardMediaController;
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index 30708b7..2d9c63e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -23,7 +23,7 @@
import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.statusbar.SysuiStatusBarStateController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index 77e146b..5191053 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -91,10 +91,10 @@
if (!sceneContainerFlags.flexiNotifsEnabled()) {
launch {
// Only temporarily needed, until flexi notifs go live
- viewModel.shadeCollpaseFadeIn.collect { fadeIn ->
+ viewModel.shadeCollapseFadeIn.collect { fadeIn ->
if (fadeIn) {
android.animation.ValueAnimator.ofFloat(0f, 1f).apply {
- duration = 350
+ duration = 250
addUpdateListener { animation ->
controller.setMaxAlphaForExpansion(
animation.getAnimatedFraction()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 6b949a3..052e35c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -133,6 +133,21 @@
.distinctUntilChanged()
.onStart { emit(false) }
+ /**
+ * Shade locked is a legacy concept, but necessary to mimic current functionality. Listen for
+ * both SHADE_LOCKED and shade/qs expansion in order to determine lock state, as one can arrive
+ * before the other.
+ */
+ private val isShadeLocked: Flow<Boolean> =
+ combine(
+ keyguardInteractor.statusBarState.map { it == SHADE_LOCKED },
+ shadeInteractor.qsExpansion.map { it > 0f },
+ shadeInteractor.shadeExpansion.map { it > 0f },
+ ) { isShadeLocked, isQsExpanded, isShadeExpanded ->
+ isShadeLocked && (isQsExpanded || isShadeExpanded)
+ }
+ .distinctUntilChanged()
+
val shadeCollapseFadeInComplete = MutableStateFlow(false)
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
@@ -190,7 +205,7 @@
)
/** Are we purely on the glanceable hub without the shade/qs? */
- internal val isOnGlanceableHubWithoutShade: Flow<Boolean> =
+ val isOnGlanceableHubWithoutShade: Flow<Boolean> =
combine(
communalInteractor.isIdleOnCommunal,
// Shade with notifications
@@ -208,12 +223,12 @@
)
/** Fade in only for use after the shade collapses */
- val shadeCollpaseFadeIn: Flow<Boolean> =
+ val shadeCollapseFadeIn: Flow<Boolean> =
flow {
while (currentCoroutineContext().isActive) {
emit(false)
// Wait for shade to be fully expanded
- keyguardInteractor.statusBarState.first { it == SHADE_LOCKED }
+ isShadeLocked.first { it }
// ... and then for it to be collapsed
isOnLockscreenWithoutShade.first { it }
emit(true)
@@ -330,16 +345,16 @@
// shade expansion or swipe to dismiss
combineTransform(
isOnLockscreenWithoutShade,
- shadeCollpaseFadeIn,
+ shadeCollapseFadeIn,
alphaForShadeAndQsExpansion,
keyguardInteractor.dismissAlpha,
) {
isOnLockscreenWithoutShade,
- shadeCollpaseFadeIn,
+ shadeCollapseFadeIn,
alphaForShadeAndQsExpansion,
dismissAlpha ->
if (isOnLockscreenWithoutShade) {
- if (!shadeCollpaseFadeIn && dismissAlpha != null) {
+ if (!shadeCollapseFadeIn && dismissAlpha != null) {
emit(dismissAlpha)
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index 323ab80..613efaa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -185,7 +185,7 @@
*/
fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Insets =
traceSection(tag = "StatusBarContentInsetsProvider.getStatusBarContentInsetsForRotation") {
- val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplay()
+ val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplayAndRotation()
val displayCutout = sysUICutout?.cutout
val key = getCacheKey(rotation, displayCutout)
@@ -227,7 +227,7 @@
*/
@JvmOverloads
fun getStatusBarContentAreaForRotation(@Rotation rotation: Int): Rect {
- val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplay()
+ val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplayAndRotation()
val displayCutout = sysUICutout?.cutout
val key = getCacheKey(rotation, displayCutout)
return insetsCache[key]
@@ -528,7 +528,7 @@
var leftMargin = minLeft
var rightMargin = minRight
for (cutoutRect in cutoutRects) {
- val protectionRect = sysUICutout.cameraProtection?.cutoutBounds
+ val protectionRect = sysUICutout.cameraProtection?.bounds
val actualCutoutRect =
if (protectionRect?.intersects(cutoutRect) == true) {
rectUnion(cutoutRect, protectionRect)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index 1c33d3f..bef6b0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -18,15 +18,22 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.dagger.OemSatelliteInputLog
import com.android.systemui.statusbar.pipeline.satellite.domain.interactor.DeviceBasedSatelliteInteractor
import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel
import javax.inject.Inject
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
@@ -42,25 +49,44 @@
interactor: DeviceBasedSatelliteInteractor,
@Application scope: CoroutineScope,
airplaneModeRepository: AirplaneModeRepository,
+ @OemSatelliteInputLog logBuffer: LogBuffer,
) {
- private val shouldShowIcon: StateFlow<Boolean> =
- interactor.areAllConnectionsOutOfService
- .flatMapLatest { allOos ->
- if (!allOos) {
- flowOf(false)
+ private val shouldShowIcon: Flow<Boolean> =
+ interactor.areAllConnectionsOutOfService.flatMapLatest { allOos ->
+ if (!allOos) {
+ flowOf(false)
+ } else {
+ combine(interactor.isSatelliteAllowed, airplaneModeRepository.isAirplaneMode) {
+ isSatelliteAllowed,
+ isAirplaneMode ->
+ isSatelliteAllowed && !isAirplaneMode
+ }
+ }
+ }
+
+ // This adds a 10 seconds delay before showing the icon
+ private val shouldActuallyShowIcon: StateFlow<Boolean> =
+ shouldShowIcon
+ .distinctUntilChanged()
+ .flatMapLatest { shouldShow ->
+ if (shouldShow) {
+ logBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ { long1 = DELAY_DURATION.inWholeSeconds },
+ { "Waiting $long1 seconds before showing the satellite icon" }
+ )
+ delay(DELAY_DURATION)
+ flowOf(true)
} else {
- combine(interactor.isSatelliteAllowed, airplaneModeRepository.isAirplaneMode) {
- isSatelliteAllowed,
- isAirplaneMode ->
- isSatelliteAllowed && !isAirplaneMode
- }
+ flowOf(false)
}
}
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
val icon: StateFlow<Icon?> =
combine(
- shouldShowIcon,
+ shouldActuallyShowIcon,
interactor.connectionState,
interactor.signalStrength,
) { shouldShow, state, signalStrength ->
@@ -71,4 +97,9 @@
}
}
.stateIn(scope, SharingStarted.WhileSubscribed(), null)
+
+ companion object {
+ private const val TAG = "DeviceBasedSatelliteViewModel"
+ private val DELAY_DURATION = 10.seconds
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
index 7b652c1..6124f63 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
@@ -23,11 +23,13 @@
import android.os.Looper;
import android.os.Process;
+import com.android.systemui.Flags;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.BroadcastRunning;
import com.android.systemui.dagger.qualifiers.LongRunning;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.NotifInflation;
import dagger.Module;
import dagger.Provides;
@@ -50,6 +52,8 @@
private static final Long LONG_SLOW_DELIVERY_THRESHOLD = 2500L;
private static final Long BROADCAST_SLOW_DISPATCH_THRESHOLD = 1000L;
private static final Long BROADCAST_SLOW_DELIVERY_THRESHOLD = 1000L;
+ private static final Long NOTIFICATION_INFLATION_SLOW_DISPATCH_THRESHOLD = 1000L;
+ private static final Long NOTIFICATION_INFLATION_SLOW_DELIVERY_THRESHOLD = 1000L;
/** Background Looper */
@Provides
@@ -90,6 +94,24 @@
return thread.getLooper();
}
+ /** Notification inflation Looper */
+ @Provides
+ @SysUISingleton
+ @NotifInflation
+ public static Looper provideNotifInflationLooper(@Background Looper bgLooper) {
+ if (!Flags.dedicatedNotifInflationThread()) {
+ return bgLooper;
+ }
+
+ final HandlerThread thread = new HandlerThread("NotifInflation",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ final Looper looper = thread.getLooper();
+ looper.setSlowLogThresholdMs(NOTIFICATION_INFLATION_SLOW_DISPATCH_THRESHOLD,
+ NOTIFICATION_INFLATION_SLOW_DELIVERY_THRESHOLD);
+ return looper;
+ }
+
/**
* Background Handler.
*
@@ -225,4 +247,12 @@
thread.start();
return new Handler(thread.getLooper());
}
+
+ /** */
+ @Provides
+ @SysUISingleton
+ @NotifInflation
+ public static Executor provideNotifInflationExecutor(@NotifInflation Looper looper) {
+ return new ExecutorImpl(looper);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AncModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AncModule.kt
new file mode 100644
index 0000000..66df45c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AncModule.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.systemui.volume.dagger
+
+import android.content.Context
+import androidx.slice.SliceViewManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.volume.panel.component.anc.data.repository.AncSliceRepository
+import com.android.systemui.volume.panel.component.anc.data.repository.AncSliceRepositoryImpl
+import dagger.Module
+import dagger.Provides
+
+/** Dagger module that provides ANC controlling backend. */
+@Module
+interface AncModule {
+
+ companion object {
+ @Provides
+ @SysUISingleton
+ fun provideAncSliceRepository(
+ @Application context: Context,
+ implFactory: AncSliceRepositoryImpl.Factory
+ ): AncSliceRepository = implFactory.create(SliceViewManager.getInstance(context))
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index 3285637..c6aee42 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -57,6 +57,7 @@
@Module(
includes = {
AudioModule.class,
+ AncModule.class,
CaptioningModule.class,
MediaDevicesModule.class
},
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
new file mode 100644
index 0000000..8f18aa8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.systemui.volume.panel.component.anc.data.repository
+
+import android.bluetooth.BluetoothDevice
+import android.net.Uri
+import androidx.slice.Slice
+import androidx.slice.SliceViewManager
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.media.BluetoothMediaDevice
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.slice.sliceForUri
+import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+/** Provides ANC slice data */
+interface AncSliceRepository {
+
+ /**
+ * ANC slice with a given width. Emits null when there is no ANC slice available. This can mean
+ * that:
+ * - there is no supported device connected;
+ * - there is no slice provider for the uri;
+ */
+ fun ancSlice(width: Int): Flow<Slice?>
+}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class AncSliceRepositoryImpl
+@AssistedInject
+constructor(
+ mediaRepositoryFactory: LocalMediaRepositoryFactory,
+ @Background private val backgroundCoroutineContext: CoroutineContext,
+ @Assisted private val sliceViewManager: SliceViewManager,
+) : AncSliceRepository {
+
+ private val localMediaRepository = mediaRepositoryFactory.create(null)
+
+ override fun ancSlice(width: Int): Flow<Slice?> {
+ return localMediaRepository.currentConnectedDevice
+ .map { (it as? BluetoothMediaDevice)?.cachedDevice?.device?.getExtraControlUri(width) }
+ .distinctUntilChanged()
+ .flatMapLatest { sliceUri ->
+ sliceUri ?: return@flatMapLatest flowOf(null)
+ sliceViewManager.sliceForUri(sliceUri)
+ }
+ .flowOn(backgroundCoroutineContext)
+ }
+
+ private fun BluetoothDevice.getExtraControlUri(width: Int): Uri? {
+ val uri: String? = BluetoothUtils.getControlUriMetaData(this)
+ uri ?: return null
+
+ return if (uri.isEmpty()) {
+ null
+ } else {
+ Uri.parse(
+ "$uri$width" +
+ "&version=${SliceParameters.VERSION}" +
+ "&is_collapsed=${SliceParameters.IS_COLLAPSED}"
+ )
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(sliceViewManager: SliceViewManager): AncSliceRepositoryImpl
+ }
+
+ private object SliceParameters {
+ /**
+ * Slice version
+ * 1) legacy slice
+ * 2) new slice
+ */
+ const val VERSION = 2
+
+ /**
+ * Collapsed slice shows a single button, and expanded shows a row buttons. Supported since
+ * [VERSION]==2.
+ */
+ const val IS_COLLAPSED = false
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt
new file mode 100644
index 0000000..89b9274
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.systemui.volume.panel.component.anc.domain
+
+import com.android.systemui.volume.panel.component.anc.domain.interactor.AncSliceInteractor
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** Determines if ANC component is available for the Volume Panel. */
+@VolumePanelScope
+class AncAvailabilityCriteria
+@Inject
+constructor(
+ private val ancSliceInteractor: AncSliceInteractor,
+) : ComponentAvailabilityCriteria {
+
+ override fun isAvailable(): Flow<Boolean> = ancSliceInteractor.ancSlice.map { it != null }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
new file mode 100644
index 0000000..91af622
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.systemui.volume.panel.component.anc.domain.interactor
+
+import android.app.slice.Slice.HINT_ERROR
+import android.app.slice.SliceItem.FORMAT_SLICE
+import androidx.slice.Slice
+import com.android.systemui.volume.panel.component.anc.data.repository.AncSliceRepository
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.shareIn
+
+/** Provides a valid slice from [AncSliceRepository]. */
+@OptIn(ExperimentalCoroutinesApi::class)
+@VolumePanelScope
+class AncSliceInteractor
+@Inject
+constructor(
+ private val ancSliceRepository: AncSliceRepository,
+ scope: CoroutineScope,
+) {
+
+ // Start with a positive width to check is the Slice is available.
+ private val width = MutableStateFlow(1)
+
+ /** Provides a valid ANC slice. */
+ val ancSlice: SharedFlow<Slice?> =
+ width
+ .flatMapLatest { width -> ancSliceRepository.ancSlice(width) }
+ .map { slice ->
+ if (slice?.isValidSlice() == true) {
+ slice
+ } else {
+ null
+ }
+ }
+ .shareIn(scope, SharingStarted.Eagerly, replay = 1)
+
+ /** Updates the width of the [ancSlice] */
+ fun changeWidth(newWidth: Int) {
+ width.value = newWidth
+ }
+
+ private fun Slice.isValidSlice(): Boolean {
+ if (hints.contains(HINT_ERROR)) {
+ return false
+ }
+ for (item in items) {
+ if (item.format == FORMAT_SLICE) {
+ return true
+ }
+ }
+ return false
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
index 1f52260..d8cd128 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
@@ -21,7 +21,7 @@
import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.media.controls.pipeline.LocalMediaManagerFactory
+import com.android.systemui.media.controls.util.LocalMediaManagerFactory
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
index 438f0f4..cc36cfa 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
@@ -18,8 +18,6 @@
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
-import static com.android.systemui.flags.Flags.ENABLE_CLOCK_KEYGUARD_PRESENTATION;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
@@ -37,9 +35,7 @@
import androidx.test.filters.SmallTest;
-import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -60,13 +56,9 @@
@Mock
private NavigationBarController mNavigationBarController;
@Mock
- private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- @Mock
private ConnectedDisplayKeyguardPresentation.Factory
mConnectedDisplayKeyguardPresentationFactory;
@Mock
- private KeyguardDisplayManager.KeyguardPresentation mKeyguardPresentation;
- @Mock
private ConnectedDisplayKeyguardPresentation mConnectedDisplayKeyguardPresentation;
@Mock
private KeyguardDisplayManager.DeviceStateHelper mDeviceStateHelper;
@@ -77,7 +69,6 @@
private Executor mBackgroundExecutor = Runnable::run;
private KeyguardDisplayManager mManager;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
- private FakeFeatureFlags mFakeFeatureFlags = new FakeFeatureFlags();
// The default and secondary displays are both in the default group
private Display mDefaultDisplay;
private Display mSecondaryDisplay;
@@ -88,15 +79,13 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mFakeFeatureFlags.set(ENABLE_CLOCK_KEYGUARD_PRESENTATION, false);
mManager = spy(new KeyguardDisplayManager(mContext, () -> mNavigationBarController,
- mKeyguardStatusViewComponentFactory, mDisplayTracker, mMainExecutor,
- mBackgroundExecutor, mDeviceStateHelper, mKeyguardStateController,
- mConnectedDisplayKeyguardPresentationFactory, mFakeFeatureFlags));
- doReturn(mKeyguardPresentation).when(mManager).createPresentation(any());
+ mDisplayTracker, mMainExecutor, mBackgroundExecutor, mDeviceStateHelper,
+ mKeyguardStateController, mConnectedDisplayKeyguardPresentationFactory));
doReturn(mConnectedDisplayKeyguardPresentation).when(
mConnectedDisplayKeyguardPresentationFactory).create(any());
-
+ doReturn(mConnectedDisplayKeyguardPresentation).when(mManager)
+ .createPresentation(any());
mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY,
new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
mSecondaryDisplay = new Display(DisplayManagerGlobal.getInstance(),
@@ -152,9 +141,8 @@
}
@Test
- public void testShow_withClockPresentationFlagEnabled_presentationCreated() {
+ public void testShow_presentationCreated() {
when(mManager.createPresentation(any())).thenCallRealMethod();
- mFakeFeatureFlags.set(ENABLE_CLOCK_KEYGUARD_PRESENTATION, true);
mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay});
mManager.show();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
deleted file mode 100644
index 5102957..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
+++ /dev/null
@@ -1,116 +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.keyguard;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.util.AttributeSet;
-import android.view.Display;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.keyguard.KeyguardDisplayManager.KeyguardPresentation;
-import com.android.keyguard.dagger.KeyguardStatusViewComponent;
-import com.android.systemui.res.R;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class KeyguardPresentationTest extends SysuiTestCase {
-
- @Mock
- KeyguardClockSwitch mMockKeyguardClockSwitch;
- @Mock
- KeyguardSliceView mMockKeyguardSliceView;
- @Mock
- KeyguardStatusView mMockKeyguardStatusView;
- @Mock
- private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- @Mock
- private KeyguardStatusViewComponent mKeyguardStatusViewComponent;
- @Mock
- private KeyguardClockSwitchController mKeyguardClockSwitchController;
-
- LayoutInflater mLayoutInflater;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mMockKeyguardClockSwitch.getContext()).thenReturn(mContext);
- when(mMockKeyguardSliceView.getContext()).thenReturn(mContext);
- when(mMockKeyguardStatusView.getContext()).thenReturn(mContext);
- when(mMockKeyguardStatusView.findViewById(R.id.clock)).thenReturn(mMockKeyguardStatusView);
- when(mKeyguardStatusViewComponentFactory.build(any(KeyguardStatusView.class),
- any(Display.class)))
- .thenReturn(mKeyguardStatusViewComponent);
- when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
- .thenReturn(mKeyguardClockSwitchController);
-
- allowTestableLooperAsMainThread();
-
- mLayoutInflater = LayoutInflater.from(mContext);
- mLayoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
-
- @Override
- public View onCreateView(View parent, String name, Context context,
- AttributeSet attrs) {
- return onCreateView(name, context, attrs);
- }
-
- @Override
- public View onCreateView(String name, Context context, AttributeSet attrs) {
- if ("com.android.keyguard.KeyguardStatusView".equals(name)) {
- return mMockKeyguardStatusView;
- } else if ("com.android.keyguard.KeyguardClockSwitch".equals(name)) {
- return mMockKeyguardClockSwitch;
- } else if ("com.android.keyguard.KeyguardSliceView".equals(name)) {
- return mMockKeyguardStatusView;
- }
- return null;
- }
- });
- }
-
- @After
- public void tearDown() {
- disallowTestableLooperAsMainThread();
- }
-
- @Test
- public void testInflation_doesntCrash() {
- final Display display = mContext.getSystemService(DisplayManager.class).getDisplay(
- Display.DEFAULT_DISPLAY);
- KeyguardPresentation keyguardPresentation = new KeyguardPresentation(mContext, display,
- mKeyguardStatusViewComponentFactory);
- keyguardPresentation.onCreate(null /*savedInstanceState */);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderImplTest.kt
index a19a0c7..d2a17c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderImplTest.kt
@@ -89,7 +89,7 @@
loader.loadCameraProtectionInfoList().map { it.toTestableVersion() }
private fun CameraProtectionInfo.toTestableVersion() =
- TestableProtectionInfo(logicalCameraId, physicalCameraId, cutoutBounds, displayUniqueId)
+ TestableProtectionInfo(logicalCameraId, physicalCameraId, bounds, displayUniqueId)
/**
* "Testable" version, because the original version contains a Path property, which doesn't
diff --git a/packages/SystemUI/tests/src/com/android/systemui/FakeCameraProtectionLoader.kt b/packages/SystemUI/tests/src/com/android/systemui/FakeCameraProtectionLoader.kt
index f769b4e..6cb77cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/FakeCameraProtectionLoader.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/FakeCameraProtectionLoader.kt
@@ -16,6 +16,7 @@
package com.android.systemui
+import android.graphics.Rect
import com.android.systemui.res.R
class FakeCameraProtectionLoader(private val context: SysuiTestableContext) :
@@ -36,7 +37,10 @@
addInnerCameraProtection()
}
- fun addOuterCameraProtection(displayUniqueId: String = "111") {
+ fun addOuterCameraProtection(
+ displayUniqueId: String = "111",
+ bounds: Rect = Rect(/* left = */ 0, /* top = */ 0, /* right = */ 10, /* bottom = */ 10)
+ ) {
context.orCreateTestableResources.addOverride(R.string.config_protectedCameraId, "1")
context.orCreateTestableResources.addOverride(
R.string.config_protectedPhysicalCameraId,
@@ -44,7 +48,7 @@
)
context.orCreateTestableResources.addOverride(
R.string.config_frontBuiltInDisplayCutoutProtection,
- "M 0,0 H 10,10 V 10,10 H 0,10 Z"
+ bounds.asPath(),
)
context.orCreateTestableResources.addOverride(
R.string.config_protectedScreenUniqueId,
@@ -52,7 +56,10 @@
)
}
- fun addInnerCameraProtection(displayUniqueId: String = "222") {
+ fun addInnerCameraProtection(
+ displayUniqueId: String = "222",
+ bounds: Rect = Rect(/* left = */ 0, /* top = */ 0, /* right = */ 20, /* bottom = */ 20)
+ ) {
context.orCreateTestableResources.addOverride(R.string.config_protectedInnerCameraId, "2")
context.orCreateTestableResources.addOverride(
R.string.config_protectedInnerPhysicalCameraId,
@@ -60,11 +67,13 @@
)
context.orCreateTestableResources.addOverride(
R.string.config_innerBuiltInDisplayCutoutProtection,
- "M 0,0 H 20,20 V 20,20 H 0,20 Z"
+ bounds.asPath(),
)
context.orCreateTestableResources.addOverride(
R.string.config_protectedInnerScreenUniqueId,
displayUniqueId
)
}
+
+ private fun Rect.asPath() = "M $left, $top H $right V $bottom H $left Z"
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysUICutoutProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/SysUICutoutProviderTest.kt
index f37c4ae..61c7e1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysUICutoutProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysUICutoutProviderTest.kt
@@ -16,11 +16,16 @@
package com.android.systemui
+import android.graphics.Rect
import android.view.Display
import android.view.DisplayAdjustments
import android.view.DisplayCutout
+import android.view.DisplayInfo
+import android.view.Surface
+import android.view.Surface.Rotation
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -39,7 +44,7 @@
val noCutoutDisplayContext = context.createDisplayContext(noCutoutDisplay)
val provider = SysUICutoutProvider(noCutoutDisplayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()
assertThat(sysUICutout).isNull()
}
@@ -50,7 +55,7 @@
val cutoutDisplayContext = context.createDisplayContext(cutoutDisplay)
val provider = SysUICutoutProvider(cutoutDisplayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cutout).isEqualTo(cutoutDisplay.cutout)
}
@@ -61,7 +66,7 @@
val cutoutDisplayContext = context.createDisplayContext(cutoutDisplay)
val provider = SysUICutoutProvider(cutoutDisplayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cameraProtection).isNull()
}
@@ -72,7 +77,7 @@
val outerDisplayContext = context.createDisplayContext(OUTER_DISPLAY)
val provider = SysUICutoutProvider(outerDisplayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cameraProtection).isNotNull()
}
@@ -83,7 +88,7 @@
val outerDisplayContext = context.createDisplayContext(OUTER_DISPLAY)
val provider = SysUICutoutProvider(outerDisplayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cameraProtection).isNull()
}
@@ -94,7 +99,7 @@
val displayContext = context.createDisplayContext(createDisplay(uniqueId = null))
val provider = SysUICutoutProvider(displayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cameraProtection).isNull()
}
@@ -105,20 +110,170 @@
val displayContext = context.createDisplayContext(createDisplay(uniqueId = ""))
val provider = SysUICutoutProvider(displayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cameraProtection).isNull()
}
+ @Test
+ fun cutoutInfo_rotation0_returnsOriginalProtectionBounds() {
+ val provider =
+ setUpProviderWithCameraProtection(
+ displayWidth = 500,
+ displayHeight = 1000,
+ rotation = Surface.ROTATION_0,
+ protectionBounds =
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ )
+
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
+
+ assertThat(sysUICutout.cameraProtection!!.bounds)
+ .isEqualTo(
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ )
+ }
+
+ @Test
+ fun cutoutInfo_rotation90_returnsRotatedProtectionBounds() {
+ val provider =
+ setUpProviderWithCameraProtection(
+ displayWidth = 500,
+ displayHeight = 1000,
+ rotation = Surface.ROTATION_90,
+ protectionBounds =
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ )
+
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
+
+ assertThat(sysUICutout.cameraProtection!!.bounds)
+ .isEqualTo(Rect(/* left = */ 10, /* top = */ 10, /* right = */ 110, /* bottom = */ 60))
+ }
+
+ @Test
+ fun cutoutInfo_withRotation_doesNotMutateOriginalBounds() {
+ val displayNaturalWidth = 500
+ val displayNaturalHeight = 1000
+ val originalProtectionBounds =
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ // Safe copy as we don't know at which layer the mutation could happen
+ val originalProtectionBoundsCopy = Rect(originalProtectionBounds)
+ val display =
+ createDisplay(
+ uniqueId = OUTER_DISPLAY_UNIQUE_ID,
+ rotation = Surface.ROTATION_180,
+ width = displayNaturalWidth,
+ height = displayNaturalHeight,
+ )
+ fakeProtectionLoader.addOuterCameraProtection(
+ displayUniqueId = OUTER_DISPLAY_UNIQUE_ID,
+ bounds = originalProtectionBounds
+ )
+ val provider =
+ SysUICutoutProvider(context.createDisplayContext(display), fakeProtectionLoader)
+
+ // Here we get the rotated bounds once
+ provider.cutoutInfoForCurrentDisplayAndRotation()
+
+ // Rotate display back to original rotation
+ whenever(display.rotation).thenReturn(Surface.ROTATION_0)
+
+ // Now the bounds should match the original ones. We are checking for mutation since Rect
+ // is mutable and has many methods that mutate the instance, and it is easy to do it by
+ // mistake.
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
+ assertThat(sysUICutout.cameraProtection!!.bounds).isEqualTo(originalProtectionBoundsCopy)
+ }
+
+ @Test
+ fun cutoutInfo_rotation180_returnsRotatedProtectionBounds() {
+ val provider =
+ setUpProviderWithCameraProtection(
+ displayWidth = 500,
+ displayHeight = 1000,
+ rotation = Surface.ROTATION_180,
+ protectionBounds =
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ )
+
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
+
+ assertThat(sysUICutout.cameraProtection!!.bounds)
+ .isEqualTo(Rect(/* left = */ 10, /* top = */ 890, /* right = */ 60, /* bottom = */ 990))
+ }
+
+ @Test
+ fun cutoutInfo_rotation270_returnsRotatedProtectionBounds() {
+ val provider =
+ setUpProviderWithCameraProtection(
+ displayWidth = 500,
+ displayHeight = 1000,
+ rotation = Surface.ROTATION_270,
+ protectionBounds =
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ )
+
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
+
+ assertThat(sysUICutout.cameraProtection!!.bounds)
+ .isEqualTo(
+ Rect(/* left = */ 890, /* top = */ 440, /* right = */ 990, /* bottom = */ 490)
+ )
+ }
+
+ private fun setUpProviderWithCameraProtection(
+ displayWidth: Int,
+ displayHeight: Int,
+ rotation: Int = Surface.ROTATION_0,
+ protectionBounds: Rect,
+ ): SysUICutoutProvider {
+ val display =
+ createDisplay(
+ uniqueId = OUTER_DISPLAY_UNIQUE_ID,
+ rotation = rotation,
+ width =
+ if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
+ displayWidth
+ } else {
+ displayHeight
+ },
+ height =
+ if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180)
+ displayHeight
+ else displayWidth,
+ )
+ fakeProtectionLoader.addOuterCameraProtection(
+ displayUniqueId = OUTER_DISPLAY_UNIQUE_ID,
+ bounds = protectionBounds
+ )
+ return SysUICutoutProvider(context.createDisplayContext(display), fakeProtectionLoader)
+ }
+
companion object {
private const val OUTER_DISPLAY_UNIQUE_ID = "outer"
private val OUTER_DISPLAY = createDisplay(uniqueId = OUTER_DISPLAY_UNIQUE_ID)
private fun createDisplay(
+ width: Int = 500,
+ height: Int = 1000,
+ @Rotation rotation: Int = Surface.ROTATION_0,
uniqueId: String? = "uniqueId",
cutout: DisplayCutout? = mock<DisplayCutout>()
) =
mock<Display> {
+ whenever(this.getDisplayInfo(any())).thenAnswer {
+ val displayInfo = it.arguments[0] as DisplayInfo
+ displayInfo.rotation = rotation
+ displayInfo.logicalWidth = width
+ displayInfo.logicalHeight = height
+ return@thenAnswer true
+ }
+ // Setting width and height to smaller values to simulate real behavior of this API
+ // not always returning the real display size
+ whenever(this.width).thenReturn(width - 5)
+ whenever(this.height).thenReturn(height - 10)
+ whenever(this.rotation).thenReturn(rotation)
whenever(this.displayAdjustments).thenReturn(DisplayAdjustments())
whenever(this.cutout).thenReturn(cutout)
whenever(this.uniqueId).thenReturn(uniqueId)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
index f770a38..6e00b70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
@@ -21,6 +21,8 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyDouble;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
@@ -40,6 +42,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -71,6 +75,10 @@
private KeyguardStateController mKeyguardStateController;
@Mock
private AccessibilityManager mAccessibilityManager;
+ @Captor
+ private ArgumentCaptor<FalsingDataProvider.SessionListener> mSessionListenerArgumentCaptor;
+ @Captor
+ private ArgumentCaptor<HistoryTracker.BeliefListener> mBeliefListenerArgumentCaptor;
private final FalsingClassifier.Result mPassedResult = FalsingClassifier.Result.passed(1);
private final FalsingClassifier.Result mFalsedResult =
@@ -194,4 +202,28 @@
when(mFalsingDataProvider.isFromTrackpad()).thenReturn(true);
assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
}
+
+ @Test
+ public void testAddAndRemoveFalsingBeliefListener() {
+ verify(mHistoryTracker, never()).addBeliefListener(any());
+
+ // Session started
+ final FalsingDataProvider.SessionListener sessionListener = captureSessionListener();
+ sessionListener.onSessionStarted();
+
+ // Verify belief listener added when session started
+ verify(mHistoryTracker).addBeliefListener(mBeliefListenerArgumentCaptor.capture());
+ verify(mHistoryTracker, never()).removeBeliefListener(any());
+
+ // Session ended
+ sessionListener.onSessionEnded();
+
+ // Verify belief listener removed when session ended
+ verify(mHistoryTracker).removeBeliefListener(mBeliefListenerArgumentCaptor.getValue());
+ }
+
+ private FalsingDataProvider.SessionListener captureSessionListener() {
+ verify(mFalsingDataProvider).addSessionListener(mSessionListenerArgumentCaptor.capture());
+ return mSessionListenerArgumentCaptor.getValue();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index fcb18f5..3f13033 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -33,6 +33,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -85,6 +86,8 @@
private BatteryController mBatteryController;
@Mock
private SelectedUserInteractor mSelectedUserInteractor;
+ @Mock
+ private CommunalInteractor mCommunalInteractor;
private final DockManagerFake mDockManager = new DockManagerFake();
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
@@ -102,7 +105,8 @@
mStatusBarStateController, mKeyguardStateController,
() -> mShadeInteractor, mBatteryController,
mDockManager, mFakeExecutor,
- mJavaAdapter, mFakeSystemClock, () -> mSelectedUserInteractor
+ mJavaAdapter, mFakeSystemClock, () -> mSelectedUserInteractor,
+ () -> mCommunalInteractor
);
mFalsingCollector.init();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
index 1851582..c65a117 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
@@ -27,16 +27,20 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.app.KeyguardManager;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.os.PersistableBundle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
@@ -59,6 +63,8 @@
@Mock
private ClipboardManager mClipboardManager;
@Mock
+ private KeyguardManager mKeyguardManager;
+ @Mock
private ClipboardOverlayController mOverlayController;
@Mock
private ClipboardToast mClipboardToast;
@@ -96,7 +102,7 @@
when(mClipboardManager.getPrimaryClipSource()).thenReturn(mSampleSource);
mClipboardListener = new ClipboardListener(getContext(), mOverlayControllerProvider,
- mClipboardToast, mClipboardManager, mUiEventLogger);
+ mClipboardToast, mClipboardManager, mKeyguardManager, mUiEventLogger);
}
@@ -191,6 +197,34 @@
}
@Test
+ @EnableFlags(Flags.FLAG_CLIPBOARD_NONINTERACTIVE_ON_LOCKSCREEN)
+ public void test_deviceLocked_showsToast() {
+ when(mKeyguardManager.isDeviceLocked()).thenReturn(true);
+
+ mClipboardListener.start();
+ mClipboardListener.onPrimaryClipChanged();
+
+ verify(mUiEventLogger, times(1)).log(
+ ClipboardOverlayEvent.CLIPBOARD_TOAST_SHOWN, 0, mSampleSource);
+ verify(mClipboardToast, times(1)).showCopiedToast();
+ verifyZeroInteractions(mOverlayControllerProvider);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_CLIPBOARD_NONINTERACTIVE_ON_LOCKSCREEN)
+ public void test_deviceLocked_legacyBehavior_showsInteractiveUI() {
+ when(mKeyguardManager.isDeviceLocked()).thenReturn(true);
+
+ mClipboardListener.start();
+ mClipboardListener.onPrimaryClipChanged();
+
+ verify(mUiEventLogger, times(1)).log(
+ ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ENTERED, 0, mSampleSource);
+ verify(mOverlayController).setClipData(mSampleClipData, mSampleSource);
+ verifyZeroInteractions(mClipboardToast);
+ }
+
+ @Test
public void test_nullClipData_showsNothing() {
when(mClipboardManager.getPrimaryClip()).thenReturn(null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
index 2bf9ab2..05b4a41 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
@@ -37,7 +37,7 @@
import com.android.systemui.complication.dagger.DreamMediaEntryComplicationComponent;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.media.controls.ui.MediaCarouselController;
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController;
import com.android.systemui.media.dream.MediaDreamComplication;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
index cf8fe79..2b51863 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
@@ -2,6 +2,9 @@
import android.content.ComponentCallbacks2
import android.graphics.HardwareRenderer
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -23,6 +26,7 @@
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
@@ -48,10 +52,11 @@
@Mock private lateinit var globalWindowManager: GlobalWindowManager
private lateinit var resourceTrimmer: ResourceTrimmer
+ @Rule @JvmField public val setFlagsRule = SetFlagsRule()
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- featureFlags.set(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK, true)
featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, true)
keyguardRepository.setDozeAmount(0f)
keyguardRepository.setKeyguardGoingAway(false)
@@ -76,6 +81,7 @@
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun noChange_noOutputChanges() =
testScope.runTest {
testScope.runCurrent()
@@ -83,6 +89,7 @@
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun dozeAodDisabled_sleep_trimsMemory() =
testScope.runTest {
powerInteractor.setAsleepForTest()
@@ -93,6 +100,27 @@
}
@Test
+ @DisableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
+ fun dozeAodDisabled_flagDisabled_sleep_doesntTrimMemory() =
+ testScope.runTest {
+ powerInteractor.setAsleepForTest()
+ testScope.runCurrent()
+ verifyZeroInteractions(globalWindowManager)
+ }
+
+ @Test
+ @DisableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
+ fun dozeEnabled_flagDisabled_sleepWithFullDozeAmount_doesntTrimMemory() =
+ testScope.runTest {
+ keyguardRepository.setDreaming(true)
+ keyguardRepository.setDozeAmount(1f)
+ powerInteractor.setAsleepForTest()
+ testScope.runCurrent()
+ verifyZeroInteractions(globalWindowManager)
+ }
+
+ @Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun dozeEnabled_sleepWithFullDozeAmount_trimsMemory() =
testScope.runTest {
keyguardRepository.setDreaming(true)
@@ -105,6 +133,7 @@
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun dozeEnabled_sleepWithoutFullDozeAmount_doesntTrimMemory() =
testScope.runTest {
keyguardRepository.setDreaming(true)
@@ -115,6 +144,7 @@
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun aodEnabled_sleepWithFullDozeAmount_trimsMemoryOnce() {
testScope.runTest {
keyguardRepository.setDreaming(true)
@@ -141,6 +171,7 @@
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun aodEnabled_deviceWakesHalfWayThrough_doesNotTrimMemory() {
testScope.runTest {
keyguardRepository.setDreaming(true)
@@ -172,6 +203,7 @@
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun keyguardTransitionsToGone_trimsFontCache() =
testScope.runTest {
keyguardTransitionRepository.sendTransitionSteps(
@@ -186,6 +218,7 @@
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun keyguardTransitionsToGone_flagDisabled_doesNotTrimFontCache() =
testScope.runTest {
featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/MediaTestUtils.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/MediaTestUtils.kt
index 3437365..4e976d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/MediaTestUtils.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/MediaTestUtils.kt
@@ -17,7 +17,7 @@
package com.android.systemui.media.controls
import com.android.internal.logging.InstanceId
-import com.android.systemui.media.controls.models.player.MediaData
+import com.android.systemui.media.controls.shared.model.MediaData
class MediaTestUtils {
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
index fb101dd..bb5b572 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline;
+package com.android.systemui.media.controls.domain.pipeline;
import static com.google.common.truth.Truth.assertThat;
@@ -33,8 +33,8 @@
import com.android.internal.logging.InstanceId;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.media.controls.models.player.MediaData;
-import com.android.systemui.media.controls.models.player.MediaDeviceData;
+import com.android.systemui.media.controls.shared.model.MediaData;
+import com.android.systemui.media.controls.shared.model.MediaDeviceData;
import org.junit.Before;
import org.junit.Rule;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterTest.kt
index 94b9fa4..59eb7bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.app.smartspace.SmartspaceAction
import android.os.Bundle
@@ -25,10 +25,10 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.EXTRA_KEY_TRIGGER_RESUME
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.ui.MediaPlayerData
+import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_RESUME
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.ui.controller.MediaPlayerData
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.settings.UserTracker
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManagerTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManagerTest.kt
index 59d8104..7d5305b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManagerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.app.IUriGrantsManager
import android.app.Notification
@@ -51,13 +51,13 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.EXTRA_KEY_TRIGGER_SOURCE
-import com.android.systemui.media.controls.models.recommendation.EXTRA_VALUE_TRIGGER_PERIODIC
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaDataProvider
-import com.android.systemui.media.controls.resume.MediaResumeListener
-import com.android.systemui.media.controls.resume.ResumeMediaBrowser
+import com.android.systemui.media.controls.domain.resume.MediaResumeListener
+import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
+import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_SOURCE
+import com.android.systemui.media.controls.shared.model.EXTRA_VALUE_TRIGGER_PERIODIC
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaDataProvider
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index e3c4c28..14fe182 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.bluetooth.BluetoothLeBroadcast
import android.bluetooth.BluetoothLeBroadcastMetadata
@@ -39,8 +39,9 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.util.LocalMediaManagerFactory
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManagerFactory
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilterTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
index 3099609..5a3c220 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.media.session.MediaController
import android.media.session.MediaController.PlaybackInfo
@@ -25,7 +25,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
+import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
index 8baa06a..3cc65c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.media.MediaMetadata
import android.media.session.MediaController
@@ -24,8 +24,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
index 530b86e..55ff231 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume
+package com.android.systemui.media.controls.domain.resume
import android.app.PendingIntent
import android.content.ComponentName
@@ -34,10 +34,10 @@
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
-import com.android.systemui.media.controls.pipeline.RESUME_MEDIA_TIMEOUT
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.RESUME_MEDIA_TIMEOUT
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.settings.UserTracker
import com.android.systemui.tuner.TunerService
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt
index b45e66b..8dfa5b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume
+package com.android.systemui.media.controls.domain.resume
import android.content.ComponentName
import android.content.Context
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/shared/SmartspaceMediaDataTest.kt
similarity index 94%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/shared/SmartspaceMediaDataTest.kt
index f7c20ac..473dc47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/shared/SmartspaceMediaDataTest.kt
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.recommendation
+package com.android.systemui.media.controls.shared
import android.app.smartspace.SmartspaceAction
import android.graphics.drawable.Icon
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.media.controls.shared.model.NUM_REQUIRED_RECOMMENDATIONS
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
import org.junit.Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt
index 32b822d..b509e77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt
@@ -20,7 +20,9 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.ui.controller.MediaControlPanel
+import com.android.systemui.media.controls.ui.controller.MediaPlayerData
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/AnimationBindHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/AnimationBindHandlerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt
index 99f56b1..eb885fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/AnimationBindHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.graphics.drawable.Animatable2
import android.graphics.drawable.Drawable
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
index a943746..aa297b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.animation.ValueAnimator
import android.graphics.Color
@@ -22,8 +22,8 @@
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.media.controls.models.GutsViewHolder
-import com.android.systemui.media.controls.models.player.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.GutsViewHolder
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.monet.ColorScheme
import com.android.systemui.surfaceeffects.ripple.MultiRippleController
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MetadataAnimationHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MetadataAnimationHandlerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt
index 323b781..711669e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MetadataAnimationHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.animation.Animator
import android.test.suitebuilder.annotation.SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarObserverTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
index 4ec29ce..8a6b272 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.binder
import android.animation.Animator
import android.animation.ObjectAnimator
@@ -25,7 +25,9 @@
import android.widget.TextView
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.media.controls.ui.SquigglyProgress
+import com.android.systemui.media.controls.ui.drawable.SquigglyProgress
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt
index 50f0eb4..9f5260c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.provider.Settings
import android.test.suitebuilder.annotation.SmallTest
@@ -25,6 +25,8 @@
import android.widget.FrameLayout
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index f3b9102..f755199 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.app.PendingIntent
import android.content.res.ColorStateList
@@ -38,11 +38,13 @@
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
-import com.android.systemui.media.controls.pipeline.MediaDataManager
-import com.android.systemui.media.controls.ui.MediaHierarchyManager.Companion.LOCATION_QS
+import com.android.systemui.media.controls.domain.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
+import com.android.systemui.media.controls.ui.view.MediaHostState
+import com.android.systemui.media.controls.ui.view.MediaScrollView
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.ActivityStarter
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index 93d6121..2e7829d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.animation.Animator
import android.animation.AnimatorSet
@@ -69,19 +69,19 @@
import com.android.systemui.bluetooth.BroadcastDialogController
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.GutsViewHolder
-import com.android.systemui.media.controls.models.player.MediaAction
-import com.android.systemui.media.controls.models.player.MediaButton
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
-import com.android.systemui.media.controls.models.player.MediaViewHolder
-import com.android.systemui.media.controls.models.player.SeekBarObserver
-import com.android.systemui.media.controls.models.player.SeekBarViewModel
-import com.android.systemui.media.controls.models.recommendation.KEY_SMARTSPACE_APP_NAME
-import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.KEY_SMARTSPACE_APP_NAME
+import com.android.systemui.media.controls.shared.model.MediaAction
+import com.android.systemui.media.controls.shared.model.MediaButton
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.ui.binder.SeekBarObserver
+import com.android.systemui.media.controls.ui.view.GutsViewHolder
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
+import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.media.dialog.MediaOutputDialogFactory
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
index 87d093f..85291b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.graphics.Rect
import android.provider.Settings
@@ -31,7 +31,10 @@
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.ui.view.MediaCarouselScrollHandler
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
index b701d7f..a73bb2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.content.res.Configuration
import android.content.res.Configuration.ORIENTATION_LANDSCAPE
@@ -24,8 +24,9 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.media.controls.models.player.MediaViewHolder
-import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.res.R
import com.android.systemui.util.animation.MeasurementInput
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/SquigglyProgressTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/SquigglyProgressTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt
index d6cff81..0319aaa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/SquigglyProgressTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.drawable
import android.graphics.Canvas
import android.graphics.Color
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
index 74b3fce..1208369 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.view
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/MediaViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaViewHolderTest.kt
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/MediaViewHolderTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaViewHolderTest.kt
index c829d4c..d3c703c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/MediaViewHolderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaViewHolderTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.view
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarViewModelTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
index e3c8b05..e1c2d3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.viewmodel
import android.media.MediaMetadata
import android.media.session.MediaController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index f7873aa..ca403e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -47,6 +48,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.media.LocalMediaManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastSender;
@@ -126,6 +128,13 @@
mNotifCollection, mDialogTransitionAnimator,
mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
+
+ // Using a fake package will cause routing operations to fail, so we intercept
+ // scanning-related operations.
+ mMediaOutputController.mLocalMediaManager = mock(LocalMediaManager.class);
+ doNothing().when(mMediaOutputController.mLocalMediaManager).startScan();
+ doNothing().when(mMediaOutputController.mLocalMediaManager).stopScan();
+
mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext, mBroadcastSender,
mMediaOutputController);
mMediaOutputBaseDialogImpl.onCreate(new Bundle());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java
index ce885c0..a828843 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java
@@ -25,7 +25,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.util.animation.UniqueObjectHostView;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
index 8a31664..ff7c970 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
@@ -33,8 +33,8 @@
import com.android.systemui.complication.DreamMediaEntryComplication;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.media.controls.models.player.MediaData;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.shared.model.MediaData;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index e2be4cb..27f59d29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -102,7 +102,6 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(mediaTttFlags.isMediaTttEnabled()).thenReturn(true)
- whenever(mediaTttFlags.isMediaTttReceiverSuccessRippleEnabled()).thenReturn(true)
fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(fakeAppIconDrawable)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
index 563a3fe..e4a4836 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
@@ -20,6 +20,7 @@
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
+import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
import static com.google.common.truth.Truth.assertThat;
@@ -57,7 +58,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlagsClassic;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSComponent;
import com.android.systemui.qs.external.TileServiceRequestController;
@@ -511,6 +512,28 @@
);
}
+ @Test
+ public void testSceneContainerFlagsEnabled_statusBarStateIsShade() {
+ when(mSceneContainerFlags.isEnabled()).thenReturn(true);
+
+ mUnderTest.onStateChanged(KEYGUARD);
+ assertThat(mUnderTest.getStatusBarState()).isEqualTo(SHADE);
+
+ mUnderTest.onStateChanged(SHADE_LOCKED);
+ assertThat(mUnderTest.getStatusBarState()).isEqualTo(SHADE);
+ }
+
+ @Test
+ public void testSceneContainerFlagsEnabled_isKeyguardState_alwaysFalse() {
+ when(mSceneContainerFlags.isEnabled()).thenReturn(true);
+
+ mUnderTest.onStateChanged(KEYGUARD);
+ assertThat(mUnderTest.isKeyguardState()).isFalse();
+
+ when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD);
+ assertThat(mUnderTest.isKeyguardState()).isFalse();
+ }
+
private QSImpl instantiate() {
setupQsComponent();
setUpViews();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index da8d29c..65ede89 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -45,7 +45,7 @@
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.logging.QSLogger;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 1f7a029..85d7d98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -10,8 +10,8 @@
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.ui.MediaHost
-import com.android.systemui.media.controls.ui.MediaHostState
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.customize.QSCustomizerController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 2db79c2..2c14308 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -25,8 +25,8 @@
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.ui.MediaHost
-import com.android.systemui.media.controls.ui.MediaHostState
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.customize.QSCustomizerController
import com.android.systemui.qs.logging.QSLogger
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index db455cb..23c3334 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -115,9 +115,9 @@
import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.media.controls.ui.KeyguardMediaController;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationModeController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index 061f88e..42342d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -54,8 +54,8 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.power.domain.interactor.PowerInteractor;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 0933425..91701b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -15,7 +15,7 @@
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.res.R
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 4b145d8..5c45b2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -30,7 +30,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.media.controls.ui.KeyguardMediaController;
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index f2ef4e1..a4f88fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -63,7 +63,7 @@
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
-import com.android.systemui.media.controls.ui.KeyguardMediaController;
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index 2b3f9d0..6fec9ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -23,7 +23,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index f53fc46..cd0652e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -20,6 +20,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.log.core.FakeLogBuffer
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
@@ -28,7 +29,10 @@
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.mockito.MockitoAnnotations
@@ -61,6 +65,7 @@
interactor,
testScope.backgroundScope,
airplaneModeRepository,
+ FakeLogBuffer.Factory.create(),
)
}
@@ -121,8 +126,9 @@
assertThat(latest).isNull()
}
+ @OptIn(ExperimentalCoroutinesApi::class)
@Test
- fun icon_satelliteIsOff() =
+ fun icon_satelliteIsOn() =
testScope.runTest {
val latest by collectLastValue(underTest.icon)
@@ -133,7 +139,45 @@
val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
i1.isInService.value = false
- // THEN icon is null because we have service
+ // GIVEN apm is disabled
+ airplaneModeRepository.setIsAirplaneMode(false)
+
+ // Wait for delay to be completed
+ advanceTimeBy(10.seconds)
+
+ // THEN icon is set because we don't have service
assertThat(latest).isInstanceOf(Icon::class.java)
}
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun icon_hysteresisWhenEnablingIcon() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.icon)
+
+ // GIVEN satellite is allowed
+ repo.isSatelliteAllowedForCurrentLocation.value = true
+
+ // GIVEN all icons are OOS
+ val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
+ i1.isInService.value = false
+
+ // GIVEN apm is disabled
+ airplaneModeRepository.setIsAirplaneMode(false)
+
+ // THEN icon is null because of the hysteresis
+ assertThat(latest).isNull()
+
+ // Wait for delay to be completed
+ advanceTimeBy(10.seconds)
+
+ // THEN icon is set after the delay
+ assertThat(latest).isInstanceOf(Icon::class.java)
+
+ // GIVEN apm is enabled
+ airplaneModeRepository.setIsAirplaneMode(true)
+
+ // THEN icon is null immediately
+ assertThat(latest).isNull()
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
index b8c880b..62a1aa9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
@@ -37,7 +37,7 @@
import com.android.systemui.log.dagger.BiometricLog
import com.android.systemui.log.dagger.BroadcastDispatcherLog
import com.android.systemui.log.dagger.SceneFrameworkLog
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.model.SysUiState
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.DarkIconDispatcher
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
index 43897c9..cceb3ff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
@@ -17,9 +17,10 @@
package com.android.systemui.flags
import android.platform.test.annotations.EnableFlags
+import com.android.systemui.Flags.FLAG_COMPOSE_LOCKSCREEN
import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
-import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.Flags.FLAG_MEDIA_IN_SCENE_CONTAINER
+import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
/**
@@ -30,6 +31,7 @@
FLAG_SCENE_CONTAINER,
FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
+ FLAG_COMPOSE_LOCKSCREEN,
FLAG_MEDIA_IN_SCENE_CONTAINER,
)
@Retention(AnnotationRetention.RUNTIME)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
index 733340c..460913f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
@@ -22,11 +22,13 @@
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.aodToLockscreenTransitionViewModel by Fixture {
AodToLockscreenTransitionViewModel(
deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
+ shadeInteractor = shadeInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index 4939237b..ecf66a2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -24,6 +24,7 @@
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.dozeParameters
import com.android.systemui.statusbar.phone.screenOffAnimationController
@@ -56,5 +57,6 @@
screenOffAnimationController = screenOffAnimationController,
aodBurnInViewModel = aodBurnInViewModel,
aodAlphaViewModel = aodAlphaViewModel,
+ shadeInteractor = shadeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt
similarity index 93%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt
index db2cdfa..7c24b4c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerKosmos.kt
similarity index 73%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerKosmos.kt
index db2cdfa..960a069 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.privacy
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.util.mockito.mock
-var Kosmos.mediaHierarchyManager by Fixture { mock<MediaHierarchyManager>() }
+var Kosmos.privacyDialogController: PrivacyDialogController by
+ Kosmos.Fixture { mock<PrivacyDialogController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerV2Kosmos.kt
similarity index 73%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerV2Kosmos.kt
index db2cdfa..7628c0e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerV2Kosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.privacy
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.util.mockito.mock
-var Kosmos.mediaHierarchyManager by Fixture { mock<MediaHierarchyManager>() }
+var Kosmos.privacyDialogControllerV2: PrivacyDialogControllerV2 by
+ Kosmos.Fixture { mock<PrivacyDialogControllerV2>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeDefaultTilesRepository.kt
similarity index 63%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeDefaultTilesRepository.kt
index db2cdfa..ced29cc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeDefaultTilesRepository.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.qs.pipeline.data.repository
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.qs.pipeline.shared.TileSpec
-var Kosmos.mediaHierarchyManager by Fixture { mock<MediaHierarchyManager>() }
+class FakeDefaultTilesRepository(override val defaultTiles: List<TileSpec> = emptyList()) :
+ DefaultTilesRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
index ae4cf3a..a9cce69 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
@@ -23,7 +23,9 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-class FakeTileSpecRepository : TileSpecRepository {
+class FakeTileSpecRepository(
+ private val defaultTilesRepository: DefaultTilesRepository = FakeDefaultTilesRepository()
+) : TileSpecRepository {
private val tilesPerUser = mutableMapOf<Int, MutableStateFlow<List<TileSpec>>>()
@@ -67,4 +69,8 @@
value = UserTileSpecRepository.reconcileTiles(value, currentAutoAdded, restoreData)
}
}
+
+ override suspend fun prependDefault(userId: Int) {
+ with(getFlow(userId)) { value = defaultTilesRepository.defaultTiles + value }
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt
index 0091482..604c16f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt
@@ -18,7 +18,17 @@
import com.android.systemui.kosmos.Kosmos
-val Kosmos.fakeTileSpecRepository by Kosmos.Fixture { FakeTileSpecRepository() }
+/** This fake uses 0 as the minimum number of tiles. That means that no tiles is a valid state. */
+var Kosmos.fakeMinimumTilesRepository by Kosmos.Fixture { MinimumTilesFixedRepository(0) }
+val Kosmos.minimumTilesRepository: MinimumTilesRepository by
+ Kosmos.Fixture { fakeMinimumTilesRepository }
+
+var Kosmos.fakeDefaultTilesRepository by Kosmos.Fixture { FakeDefaultTilesRepository() }
+val Kosmos.defaultTilesRepository: DefaultTilesRepository by
+ Kosmos.Fixture { fakeDefaultTilesRepository }
+
+val Kosmos.fakeTileSpecRepository by
+ Kosmos.Fixture { FakeTileSpecRepository(defaultTilesRepository) }
var Kosmos.tileSpecRepository: TileSpecRepository by Kosmos.Fixture { fakeTileSpecRepository }
val Kosmos.fakeAutoAddRepository by Kosmos.Fixture { FakeAutoAddRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
index 67df563..9ef44c4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
@@ -24,6 +24,7 @@
import com.android.systemui.qs.newQSTileFactory
import com.android.systemui.qs.pipeline.data.repository.customTileAddedRepository
import com.android.systemui.qs.pipeline.data.repository.installedTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.minimumTilesRepository
import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository
import com.android.systemui.qs.pipeline.shared.logging.qsLogger
import com.android.systemui.qs.pipeline.shared.pipelineFlagsRepository
@@ -37,6 +38,7 @@
tileSpecRepository,
installedTilesRepository,
userRepository,
+ minimumTilesRepository,
customTileStatePersister,
{ newQSTileFactory },
qsTileFactory,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakePrivacyChipRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakePrivacyChipRepository.kt
new file mode 100644
index 0000000..5bc61e2
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakePrivacyChipRepository.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.shade.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.privacy.PrivacyItem
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/** Fake implementation of [PrivacyChipRepository] */
+@SysUISingleton
+class FakePrivacyChipRepository @Inject constructor() : PrivacyChipRepository {
+ private val _isSafetyCenterEnabled = MutableStateFlow(false)
+ override val isSafetyCenterEnabled = _isSafetyCenterEnabled
+
+ private val _privacyItems: MutableStateFlow<List<PrivacyItem>> = MutableStateFlow(emptyList())
+ override val privacyItems = _privacyItems
+
+ private val _isMicCameraIndicationEnabled = MutableStateFlow(false)
+ override val isMicCameraIndicationEnabled = _isMicCameraIndicationEnabled
+
+ private val _isLocationIndicationEnabled = MutableStateFlow(false)
+ override val isLocationIndicationEnabled = _isLocationIndicationEnabled
+
+ fun setIsSafetyCenterEnabled(value: Boolean) {
+ _isSafetyCenterEnabled.value = value
+ }
+
+ fun setPrivacyItems(value: List<PrivacyItem>) {
+ _privacyItems.value = value
+ }
+
+ fun setIsMicCameraIndicationEnabled(value: Boolean) {
+ _isMicCameraIndicationEnabled.value = value
+ }
+
+ fun setIsLocationIndicationEnabled(value: Boolean) {
+ _isLocationIndicationEnabled.value = value
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryKosmos.kt
similarity index 70%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryKosmos.kt
index db2cdfa..2428c61 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryKosmos.kt
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.shade.data.repository
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
-var Kosmos.mediaHierarchyManager by Fixture { mock<MediaHierarchyManager>() }
+var Kosmos.privacyChipRepository: PrivacyChipRepository by
+ Kosmos.Fixture { fakePrivacyChipRepository }
+val Kosmos.fakePrivacyChipRepository: FakePrivacyChipRepository by
+ Kosmos.Fixture { FakePrivacyChipRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorKosmos.kt
new file mode 100644
index 0000000..7334286
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 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.systemui.shade.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.privacy.privacyDialogController
+import com.android.systemui.privacy.privacyDialogControllerV2
+import com.android.systemui.shade.data.repository.fakePrivacyChipRepository
+import com.android.systemui.statusbar.policy.deviceProvisionedController
+
+var Kosmos.privacyChipInteractor: PrivacyChipInteractor by
+ Kosmos.Fixture {
+ PrivacyChipInteractor(
+ applicationScope = applicationCoroutineScope,
+ repository = fakePrivacyChipRepository,
+ privacyDialogController = privacyDialogController,
+ privacyDialogControllerV2 = privacyDialogControllerV2,
+ deviceProvisionedController = deviceProvisionedController,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
index 81888c484..e5072f1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
@@ -24,7 +24,7 @@
import com.android.systemui.keyguard.wakefulnessLifecycle
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.media.controls.ui.mediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.mediaHierarchyManager
import com.android.systemui.plugins.activityStarter
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/FakeSliceFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/FakeSliceFactory.kt
new file mode 100644
index 0000000..fc406ea
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/FakeSliceFactory.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.systemui.volume.panel.component.anc
+
+import androidx.slice.Slice
+import androidx.slice.SliceItem
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+
+object FakeSliceFactory {
+
+ fun createSlice(hasError: Boolean, hasSliceItem: Boolean): Slice {
+ return mock {
+ val sliceItem: SliceItem = mock {
+ whenever(format).thenReturn(android.app.slice.SliceItem.FORMAT_SLICE)
+ }
+
+ whenever(items)
+ .thenReturn(
+ buildList {
+ if (hasSliceItem) {
+ add(sliceItem)
+ }
+ }
+ )
+
+ whenever(hints)
+ .thenReturn(
+ buildList {
+ if (hasError) {
+ add(android.app.slice.Slice.HINT_ERROR)
+ }
+ }
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/VolumePanelAncKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/VolumePanelAncKosmos.kt
new file mode 100644
index 0000000..f9b7e69
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/VolumePanelAncKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.systemui.volume.panel.component.anc
+
+import androidx.slice.SliceViewManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.volume.panel.component.anc.data.repository.FakeAncSliceRepository
+import com.android.systemui.volume.panel.component.anc.domain.interactor.AncSliceInteractor
+
+var Kosmos.sliceViewManager: SliceViewManager by Kosmos.Fixture { mock {} }
+val Kosmos.ancSliceRepository by Kosmos.Fixture { FakeAncSliceRepository() }
+val Kosmos.ancSliceInteractor by
+ Kosmos.Fixture { AncSliceInteractor(ancSliceRepository, testScope.backgroundScope) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt
new file mode 100644
index 0000000..b66d7f9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.systemui.volume.panel.component.anc.data.repository
+
+import androidx.slice.Slice
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeAncSliceRepository : AncSliceRepository {
+
+ private val sliceByWidth = mutableMapOf<Int, MutableStateFlow<Slice?>>()
+
+ override fun ancSlice(width: Int): Flow<Slice?> =
+ sliceByWidth.getOrPut(width) { MutableStateFlow(null) }
+
+ fun putSlice(width: Int, slice: Slice?) {
+ sliceByWidth.getOrPut(width) { MutableStateFlow(null) }.value = slice
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3442a6a..46db624 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -64,6 +64,7 @@
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.app.admin.DevicePolicyManager;
+import android.app.ecm.EnhancedConfirmationManager;
import android.appwidget.AppWidgetManagerInternal;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
@@ -112,6 +113,7 @@
import android.text.TextUtils.SimpleStringSplitter;
import android.util.ArraySet;
import android.util.IntArray;
+import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -4394,13 +4396,29 @@
// permittedServices null means all accessibility services are allowed.
boolean allowed = permittedServices == null || permittedServices.contains(packageName);
if (allowed) {
- final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
- final int mode = appOps.noteOpNoThrow(
- AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
- uid, packageName, /* attributionTag= */ null, /* message= */ null);
- final boolean ecmEnabled = mContext.getResources().getBoolean(
- R.bool.config_enhancedConfirmationModeEnabled);
- return !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ try {
+ return !mContext.getSystemService(EnhancedConfirmationManager.class)
+ .isRestricted(packageName,
+ AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Exception when retrieving package:" + packageName, e);
+ return false;
+ }
+ } else {
+ try {
+ final int mode = mContext.getSystemService(AppOpsManager.class)
+ .noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
+ uid, packageName);
+ final boolean ecmEnabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
+ return !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
+ } catch (Exception e) {
+ // Fallback in case if app ops is not available in testing.
+ return false;
+ }
+ }
}
return false;
} finally {
@@ -4423,8 +4441,21 @@
return true;
}
- RestrictedLockUtils.sendShowRestrictedSettingDialogIntent(mContext,
- packageName, uid);
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ try {
+ Intent settingDialogIntent = mContext
+ .getSystemService(EnhancedConfirmationManager.class)
+ .createRestrictedSettingDialogIntent(packageName,
+ AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
+ mContext.startActivity(settingDialogIntent);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Exception when retrieving package:" + packageName, e);
+ }
+ } else {
+ RestrictedLockUtils.sendShowRestrictedSettingDialogIntent(mContext,
+ packageName, uid);
+ }
return true;
}
diff --git a/services/core/java/com/android/server/am/LmkdStatsReporter.java b/services/core/java/com/android/server/am/LmkdStatsReporter.java
index 507fd9e..595d16d 100644
--- a/services/core/java/com/android/server/am/LmkdStatsReporter.java
+++ b/services/core/java/com/android/server/am/LmkdStatsReporter.java
@@ -34,7 +34,6 @@
static final String TAG = TAG_WITH_CLASS_NAME ? "LmkdStatsReporter" : TAG_AM;
public static final int KILL_OCCURRED_MSG_SIZE = 80;
- public static final int STATE_CHANGED_MSG_SIZE = 8;
private static final int PRESSURE_AFTER_KILL = 0;
private static final int NOT_RESPONDING = 1;
@@ -79,16 +78,6 @@
}
}
- /**
- * Processes the LMK_STATE_CHANGED packet
- * Logs the change in LMKD state which is used as start/stop boundaries for logging
- * LMK_KILL_OCCURRED event.
- * Code: LMK_STATE_CHANGED = 54
- */
- public static void logStateChanged(int state) {
- FrameworkStatsLog.write(FrameworkStatsLog.LMK_STATE_CHANGED, state);
- }
-
private static int mapKillReason(int reason) {
switch (reason) {
case PRESSURE_AFTER_KILL:
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 3adea7a..89c8994 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -348,7 +348,7 @@
// LMK_PROCKILL
// LMK_UPDATE_PROPS
// LMK_KILL_OCCURRED
- // LMK_STATE_CHANGED
+ // LMK_START_MONITORING
static final byte LMK_TARGET = 0;
static final byte LMK_PROCPRIO = 1;
static final byte LMK_PROCREMOVE = 2;
@@ -358,7 +358,6 @@
static final byte LMK_PROCKILL = 6; // Note: this is an unsolicited command
static final byte LMK_UPDATE_PROPS = 7;
static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event
- static final byte LMK_STATE_CHANGED = 9; // Msg to subscribed clients on state changed
static final byte LMK_START_MONITORING = 9; // Start monitoring if delayed earlier
// Low Memory Killer Daemon command codes.
@@ -965,14 +964,6 @@
foregroundServices.first,
foregroundServices.second);
return true;
- case LMK_STATE_CHANGED:
- if (receivedLen
- != LmkdStatsReporter.STATE_CHANGED_MSG_SIZE) {
- return false;
- }
- final int state = inputData.readInt();
- LmkdStatsReporter.logStateChanged(state);
- return true;
default:
return false;
}
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index df5a824..767f54d 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -161,6 +161,7 @@
"media_solutions",
"nfc",
"pdf_viewer",
+ "perfetto",
"pixel_audio_android",
"pixel_biometrics_face",
"pixel_bluetooth",
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 102a960..e0c2425 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -167,8 +167,7 @@
ads = findBtDeviceStateForAddress(peerAddress, deviceType);
}
if (ads != null) {
- if (ads.getAudioDeviceCategory() != category
- && category != AUDIO_DEVICE_CATEGORY_UNKNOWN) {
+ if (ads.getAudioDeviceCategory() != category) {
ads.setAudioDeviceCategory(category);
mDeviceBroker.postUpdatedAdiDeviceState(ads);
mDeviceBroker.postPersistAudioDeviceSettings();
@@ -892,7 +891,7 @@
if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"A2dp config change ignored (scheduled connection change)")
- .printLog(TAG));
+ .printSlog(EventLogger.Event.ALOGI, TAG));
mmi.set(MediaMetrics.Property.EARLY_RETURN, "A2dp config change ignored")
.record();
return;
@@ -929,7 +928,7 @@
"APM handleDeviceConfigChange failed for A2DP device addr="
+ address + " codec="
+ AudioSystem.audioFormatToString(codec))
- .printLog(TAG));
+ .printSlog(EventLogger.Event.ALOGE, TAG));
// force A2DP device disconnection in case of error so that AudioService
// state is consistent with audio policy manager state
@@ -940,7 +939,7 @@
"APM handleDeviceConfigChange success for A2DP device addr="
+ address
+ " codec=" + AudioSystem.audioFormatToString(codec))
- .printLog(TAG));
+ .printSlog(EventLogger.Event.ALOGI, TAG));
}
}
}
@@ -1707,10 +1706,13 @@
if (res != AudioSystem.AUDIO_STATUS_OK) {
final String reason = "not connecting device 0x" + Integer.toHexString(device)
+ " due to command error " + res;
- Slog.e(TAG, reason);
mmi.set(MediaMetrics.Property.EARLY_RETURN, reason)
.set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED)
.record();
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "APM failed to make available device 0x" + Integer.toHexString(device)
+ + "addr=" + address + " error=" + res)
+ .printSlog(EventLogger.Event.ALOGE, TAG));
return false;
}
mConnectedDevices.put(deviceKey, new DeviceInfo(device, deviceName, address));
@@ -1736,7 +1738,8 @@
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"SCO " + (AudioSystem.isInputDevice(device) ? "source" : "sink")
+ " device addr=" + address
- + (connect ? " now available" : " made unavailable")).printLog(TAG));
+ + (connect ? " now available" : " made unavailable"))
+ .printSlog(EventLogger.Event.ALOGI, TAG));
}
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
} else {
@@ -1992,14 +1995,15 @@
// double connection is made.
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "APM failed to make available A2DP device addr=" + address
- + " error=" + res).printLog(TAG));
+ "APM failed to make available A2DP device addr="
+ + Utils.anonymizeBluetoothAddress(address)
+ + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
// TODO: connection failed, stop here
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"A2DP sink device addr=" + Utils.anonymizeBluetoothAddress(address)
- + " now available").printLog(TAG));
+ + " now available").printSlog(EventLogger.Event.ALOGI, TAG));
}
// Reset A2DP suspend state each time a new sink is connected
@@ -2240,7 +2244,8 @@
// removing A2DP device not currently used by AudioPolicy, log but don't act on it
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
"A2DP device " + Utils.anonymizeBluetoothAddress(address)
- + " made unavailable, was not used")).printLog(TAG));
+ + " made unavailable, was not used"))
+ .printSlog(EventLogger.Event.ALOGI, TAG));
mmi.set(MediaMetrics.Property.EARLY_RETURN,
"A2DP device made unavailable, was not used")
.record();
@@ -2258,13 +2263,13 @@
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"APM failed to make unavailable A2DP device addr="
+ Utils.anonymizeBluetoothAddress(address)
- + " error=" + res).printLog(TAG));
+ + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
// TODO: failed to disconnect, stop here
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
"A2DP device addr=" + Utils.anonymizeBluetoothAddress(address)
- + " made unavailable")).printLog(TAG));
+ + " made unavailable")).printSlog(EventLogger.Event.ALOGI, TAG));
}
mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
@@ -2297,10 +2302,22 @@
@GuardedBy("mDevicesLock")
private void makeA2dpSrcAvailable(String address) {
- mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+ final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address),
AudioSystem.DEVICE_STATE_AVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
+ if (res != AudioSystem.AUDIO_STATUS_OK) {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "APM failed to make available A2DP source device addr="
+ + Utils.anonymizeBluetoothAddress(address)
+ + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
+ // TODO: connection failed, stop here
+ // TODO: return
+ } else {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "A2DP source device addr=" + Utils.anonymizeBluetoothAddress(address)
+ + " now available").printSlog(EventLogger.Event.ALOGI, TAG));
+ }
mConnectedDevices.put(
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address),
new DeviceInfo(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, "", address));
@@ -2453,14 +2470,14 @@
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"APM failed to make available LE Audio device addr=" + address
- + " error=" + res).printLog(TAG));
+ + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
// TODO: connection failed, stop here
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"LE Audio " + (AudioSystem.isInputDevice(device) ? "source" : "sink")
+ " device addr=" + Utils.anonymizeBluetoothAddress(address)
- + " now available").printLog(TAG));
+ + " now available").printSlog(EventLogger.Event.ALOGI, TAG));
}
// Reset LEA suspend state each time a new sink is connected
mDeviceBroker.clearLeAudioSuspended(true /* internalOnly */);
@@ -2502,13 +2519,13 @@
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"APM failed to make unavailable LE Audio device addr=" + address
- + " error=" + res).printLog(TAG));
+ + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
// TODO: failed to disconnect, stop here
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"LE Audio device addr=" + Utils.anonymizeBluetoothAddress(address)
- + " made unavailable").printLog(TAG));
+ + " made unavailable").printSlog(EventLogger.Event.ALOGI, TAG));
}
mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 3a63330..88c24e0 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -842,7 +842,8 @@
// We must tell sidekick/displayoffload to stop controlling the display
// before we can change its power mode, so do that first.
if (isDisplayOffloadEnabled) {
- if (displayOffloadSession != null) {
+ if (displayOffloadSession != null
+ && !DisplayOffloadSession.isSupportedOffloadState(state)) {
displayOffloadSession.stopOffload();
}
} else {
@@ -874,8 +875,8 @@
// have a sidekick/displayoffload available, tell it now that it can take
// control.
if (isDisplayOffloadEnabled) {
- if (DisplayOffloadSession.isSupportedOffloadState(state)
- && displayOffloadSession != null) {
+ if (displayOffloadSession != null
+ && DisplayOffloadSession.isSupportedOffloadState(state)) {
displayOffloadSession.startOffload();
}
} else {
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index 34bb219..b3df784 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -224,10 +224,6 @@
mIsConnected = true;
service = mService;
}
-
- // TODO (b/318745416): Add support for FGS in MediaSession2. Passing a
- // null playback state means the owning process will not be allowed to
- // run in the foreground.
service.onSessionActiveStateChanged(MediaSession2Record.this,
/* playbackState= */ null);
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index e7e8096..757b26c 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -312,8 +312,7 @@
}
user.mPriorityStack.onSessionActiveStateChanged(record);
}
- boolean isUserEngaged =
- record.isActive() && (playbackState == null || playbackState.isActive());
+ boolean isUserEngaged = isUserEngaged(record, playbackState);
Log.d(TAG, "onSessionActiveStateChanged: "
+ "record=" + record
@@ -325,6 +324,15 @@
}
}
+ private boolean isUserEngaged(MediaSessionRecordImpl record,
+ @Nullable PlaybackState playbackState) {
+ if (playbackState == null) {
+ // MediaSession2 case
+ return record.checkPlaybackActiveState(/* expected= */ true);
+ }
+ return playbackState.isActive() && record.isActive();
+ }
+
// Currently only media1 can become global priority session.
void setGlobalPrioritySession(MediaSessionRecord record) {
synchronized (mLock) {
@@ -417,16 +425,13 @@
return;
}
user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority);
- if (playbackState != null) {
- boolean isUserEngaged = playbackState.isActive() && record.isActive();
- Log.d(TAG, "onSessionPlaybackStateChanged: "
- + "record=" + record
- + "playbackState=" + playbackState
- + "allowRunningInForeground=" + isUserEngaged);
- setForegroundServiceAllowance(
- record, /* allowRunningInForeground= */ isUserEngaged);
- reportMediaInteractionEvent(record, isUserEngaged);
- }
+ boolean isUserEngaged = isUserEngaged(record, playbackState);
+ Log.d(TAG, "onSessionPlaybackStateChanged: "
+ + "record=" + record
+ + "playbackState=" + playbackState
+ + "allowRunningInForeground=" + isUserEngaged);
+ setForegroundServiceAllowance(record, /* allowRunningInForeground= */ isUserEngaged);
+ reportMediaInteractionEvent(record, isUserEngaged);
}
}
diff --git a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
index 59d3d17..5ad5507 100644
--- a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
@@ -656,8 +656,10 @@
@VisibleForTesting
boolean isFrpActive() {
- waitForInitDoneSignal();
synchronized (mLock) {
+ // mFrpActive is initialized and automatic deactivation done (if possible) before the
+ // service is published, so there's no chance that callers could ask for the state
+ // before it has settled.
return mFrpActive;
}
}
@@ -1253,6 +1255,7 @@
private void enforceFactoryResetProtectionInactive() {
if (mFrpEnforced && isFrpActive()) {
+ Slog.w(TAG, "Attempt to update PDB was blocked because FRP is active.");
throw new SecurityException("FRP is active");
}
}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index af4da81..9b347d5 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -13854,7 +13854,9 @@
mNumAllUidCpuTimeReads += 2;
}
- updateSystemServerThreadStats();
+ if (!Flags.disableSystemServicePowerAttr()) {
+ updateSystemServerThreadStats();
+ }
if (powerAccumulator != null) {
updateCpuEnergyConsumerStatsLocked(cpuClusterChargeUC, powerAccumulator);
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index c9805c7..09a177a 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -149,7 +149,7 @@
&& mAdaptiveHapticsScales.size() > 0
&& mAdaptiveHapticsScales.contains(usageHint)) {
float adaptiveScale = mAdaptiveHapticsScales.get(usageHint);
- segment = segment.scale(adaptiveScale);
+ segment = segment.scaleLinearly(adaptiveScale);
}
segments.set(i, segment);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 764170a..c3efcb1 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2228,8 +2228,9 @@
if (wallpaper == null || !wallpaper.mSupportsMultiCrop) return null;
SparseArray<Rect> relativeSuggestedCrops =
mWallpaperCropper.getRelativeCropHints(wallpaper);
- Point croppedBitmapSize =
- new Point(wallpaper.cropHint.width(), wallpaper.cropHint.height());
+ Point croppedBitmapSize = new Point(
+ (int) (0.5f + wallpaper.cropHint.width() / wallpaper.mSampleSize),
+ (int) (0.5f + wallpaper.cropHint.height() / wallpaper.mSampleSize));
SparseArray<Rect> relativeDefaultCrops =
mWallpaperCropper.getDefaultCrops(relativeSuggestedCrops, croppedBitmapSize);
SparseArray<Rect> adjustedRelativeSuggestedCrops = new SparseArray<>();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index ec3c122..eb618ef 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -102,6 +102,9 @@
import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_LAST_ORDERED_ID;
import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_FIRST_ORDERED_ID;
import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_LAST_ORDERED_ID;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
import static com.android.server.wm.ActivityRecord.State.PAUSING;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
@@ -4173,7 +4176,8 @@
task = mRootWindowContainer.getDefaultTaskDisplayArea().getRootTask(
t -> t.isActivityTypeStandard());
}
- if (task != null && task.getTopMostActivity() != null) {
+ if (task != null && task.getTopMostActivity() != null
+ && !task.getTopMostActivity().isState(FINISHING, DESTROYING, DESTROYED)) {
mWindowManager.mAtmService.mActivityClientController.onPictureInPictureUiStateChanged(
task.getTopMostActivity(), pipState);
}
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index a3e2869..b68e67e 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -25,6 +25,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
+import android.app.ActivityManager;
import android.content.ClipData;
import android.content.Context;
import android.hardware.input.InputManagerGlobal;
@@ -43,8 +44,8 @@
import android.view.SurfaceControl;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
+import android.window.IGlobalDragListener;
import android.window.IUnhandledDragCallback;
-import android.window.IUnhandledDragListener;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
@@ -81,9 +82,9 @@
private WindowManagerService mService;
private final Handler mHandler;
- // The unhandled drag listener for handling cross-window drags that end with no target window
- private IUnhandledDragListener mUnhandledDragListener;
- private final IBinder.DeathRecipient mUnhandledDragListenerDeathRecipient =
+ // The global drag listener for handling cross-window drags
+ private IGlobalDragListener mGlobalDragListener;
+ private final IBinder.DeathRecipient mGlobalDragListenerDeathRecipient =
new IBinder.DeathRecipient() {
@Override
public void binderDied() {
@@ -91,7 +92,7 @@
if (hasPendingUnhandledDropCallback()) {
onUnhandledDropCallback(false /* consumedByListeners */);
}
- setUnhandledDragListener(null);
+ setGlobalDragListener(null);
}
}
};
@@ -129,29 +130,22 @@
/**
* Sets the listener for unhandled cross-window drags.
*/
- public void setUnhandledDragListener(IUnhandledDragListener listener) {
- if (mUnhandledDragListener != null && mUnhandledDragListener.asBinder() != null) {
- mUnhandledDragListener.asBinder().unlinkToDeath(
- mUnhandledDragListenerDeathRecipient, 0);
+ public void setGlobalDragListener(IGlobalDragListener listener) {
+ if (mGlobalDragListener != null && mGlobalDragListener.asBinder() != null) {
+ mGlobalDragListener.asBinder().unlinkToDeath(
+ mGlobalDragListenerDeathRecipient, 0);
}
- mUnhandledDragListener = listener;
+ mGlobalDragListener = listener;
if (listener != null && listener.asBinder() != null) {
try {
- mUnhandledDragListener.asBinder().linkToDeath(
- mUnhandledDragListenerDeathRecipient, 0);
+ mGlobalDragListener.asBinder().linkToDeath(
+ mGlobalDragListenerDeathRecipient, 0);
} catch (RemoteException e) {
- mUnhandledDragListener = null;
+ mGlobalDragListener = null;
}
}
}
- /**
- * Returns whether there is an unhandled drag listener set.
- */
- boolean hasUnhandledDragListener() {
- return mUnhandledDragListener != null;
- }
-
void sendDragStartedIfNeededLocked(WindowState window) {
mDragState.sendDragStartedIfNeededLocked(window);
}
@@ -351,7 +345,20 @@
final boolean relinquishDragSurfaceToDropTarget =
consumed && mDragState.targetInterceptsGlobalDrag(callingWin);
+ final boolean isCrossWindowDrag = !mDragState.mLocalWin.equals(token);
mDragState.endDragLocked(consumed, relinquishDragSurfaceToDropTarget);
+
+ final Task droppedWindowTask = callingWin.getTask();
+ if (com.android.window.flags.Flags.delegateUnhandledDrags()
+ && mGlobalDragListener != null && droppedWindowTask != null && consumed
+ && isCrossWindowDrag) {
+ try {
+ mGlobalDragListener.onCrossWindowDrop(droppedWindowTask.getTaskInfo());
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Failed to call global drag listener for cross-window "
+ + "drop", e);
+ }
+ }
}
} finally {
mCallback.get().postReportDropResult();
@@ -367,19 +374,19 @@
final boolean isLocalDrag =
(mDragState.mFlags & (DRAG_FLAG_GLOBAL_SAME_APPLICATION | DRAG_FLAG_GLOBAL)) == 0;
if (!com.android.window.flags.Flags.delegateUnhandledDrags()
- || mUnhandledDragListener == null
+ || mGlobalDragListener == null
|| isLocalDrag) {
// Skip if the flag is disabled, there is no unhandled-drag listener, or if this is a
// purely local drag
if (DEBUG_DRAG) Slog.d(TAG_WM, "Skipping unhandled listener "
- + "(listener=" + mUnhandledDragListener + ", flags=" + mDragState.mFlags + ")");
+ + "(listener=" + mGlobalDragListener + ", flags=" + mDragState.mFlags + ")");
return false;
}
if (DEBUG_DRAG) Slog.d(TAG_WM, "Sending DROP to unhandled listener (" + reason + ")");
try {
// Schedule timeout for the unhandled drag listener to call back
sendTimeoutMessage(MSG_UNHANDLED_DROP_LISTENER_TIMEOUT, null, DRAG_TIMEOUT_MS);
- mUnhandledDragListener.onUnhandledDrop(dropEvent, new IUnhandledDragCallback.Stub() {
+ mGlobalDragListener.onUnhandledDrop(dropEvent, new IUnhandledDragCallback.Stub() {
@Override
public void notifyUnhandledDropComplete(boolean consumedByListener) {
if (DEBUG_DRAG) Slog.d(TAG_WM, "Unhandled listener finished handling DROP");
@@ -390,7 +397,7 @@
});
return true;
} catch (RemoteException e) {
- Slog.e(TAG_WM, "Failed to call unhandled drag listener", e);
+ Slog.e(TAG_WM, "Failed to call global drag listener for unhandled drop", e);
return false;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8ebe826..9b7bc43 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -306,11 +306,11 @@
import android.view.inputmethod.ImeTracker;
import android.window.AddToSurfaceSyncGroupResult;
import android.window.ClientWindowFrames;
+import android.window.IGlobalDragListener;
import android.window.IScreenRecordingCallback;
import android.window.ISurfaceSyncGroupCompletedListener;
import android.window.ITaskFpsCallback;
import android.window.ITrustedPresentationListener;
-import android.window.IUnhandledDragListener;
import android.window.InputTransferToken;
import android.window.ScreenCapture;
import android.window.SystemPerformanceHinter;
@@ -10020,14 +10020,13 @@
}
/**
- * Sets the listener to be called back when a cross-window drag and drop operation is unhandled
- * (ie. not handled by any window which can handle the drag).
+ * Sets the listener to be called back when a cross-window drag and drop operation happens.
*/
@Override
- public void setUnhandledDragListener(IUnhandledDragListener listener) throws RemoteException {
+ public void setGlobalDragListener(IGlobalDragListener listener) throws RemoteException {
mAtmService.enforceTaskPermission("setUnhandledDragListener");
synchronized (mGlobalLock) {
- mDragDropController.setUnhandledDragListener(listener);
+ mDragDropController.setGlobalDragListener(listener);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index a8de919..d6fc01a 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -2254,6 +2254,11 @@
ownerTask.addChild(taskFragment, position);
taskFragment.setWindowingMode(creationParams.getWindowingMode());
if (!creationParams.getInitialRelativeBounds().isEmpty()) {
+ // The surface operations for the task fragment should sync with the transition.
+ // This avoid using pending transaction before collectExistenceChange is called.
+ if (transition != null) {
+ addToSyncSet(transition.getSyncId(), taskFragment);
+ }
// Set relative bounds instead of using setBounds. This will avoid unnecessary update in
// case the parent has resized since the last time parent info is sent to the organizer.
taskFragment.setRelativeEmbeddedBounds(creationParams.getInitialRelativeBounds());
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index d821625..13cc534 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1510,8 +1510,8 @@
switch (inputEvent.getType()) {
case InputEventType::KEY:
inputEventObj.reset(
- android_view_KeyEvent_fromNative(env,
- static_cast<const KeyEvent&>(inputEvent)));
+ android_view_KeyEvent_obtainAsCopy(env,
+ static_cast<const KeyEvent&>(inputEvent)));
break;
case InputEventType::MOTION:
inputEventObj.reset(
@@ -1559,7 +1559,7 @@
const nsecs_t when = keyEvent.getEventTime();
JNIEnv* env = jniEnv();
- ScopedLocalRef<jobject> keyEventObj(env, android_view_KeyEvent_fromNative(env, keyEvent));
+ ScopedLocalRef<jobject> keyEventObj(env, android_view_KeyEvent_obtainAsCopy(env, keyEvent));
if (!keyEventObj.get()) {
ALOGE("Failed to obtain key event object for interceptKeyBeforeQueueing.");
return;
@@ -1639,7 +1639,7 @@
// Token may be null
ScopedLocalRef<jobject> tokenObj(env, javaObjectForIBinder(env, token));
- ScopedLocalRef<jobject> keyEventObj(env, android_view_KeyEvent_fromNative(env, keyEvent));
+ ScopedLocalRef<jobject> keyEventObj(env, android_view_KeyEvent_obtainAsCopy(env, keyEvent));
if (!keyEventObj.get()) {
ALOGE("Failed to obtain key event object for interceptKeyBeforeDispatching.");
return 0;
@@ -1670,7 +1670,7 @@
// Note: tokenObj may be null.
ScopedLocalRef<jobject> tokenObj(env, javaObjectForIBinder(env, token));
- ScopedLocalRef<jobject> keyEventObj(env, android_view_KeyEvent_fromNative(env, keyEvent));
+ ScopedLocalRef<jobject> keyEventObj(env, android_view_KeyEvent_obtainAsCopy(env, keyEvent));
if (!keyEventObj.get()) {
ALOGE("Failed to obtain key event object for dispatchUnhandledKey.");
return {};
@@ -1689,7 +1689,8 @@
return {};
}
- const KeyEvent fallbackEvent = android_view_KeyEvent_toNative(env, fallbackKeyEventObj.get());
+ const KeyEvent fallbackEvent =
+ android_view_KeyEvent_obtainAsCopy(env, fallbackKeyEventObj.get());
android_view_KeyEvent_recycle(env, fallbackKeyEventObj.get());
return fallbackEvent;
}
@@ -2070,7 +2071,7 @@
InputEventInjectionSync mode = static_cast<InputEventInjectionSync>(syncMode);
if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) {
- const KeyEvent keyEvent = android_view_KeyEvent_toNative(env, inputEventObj);
+ const KeyEvent keyEvent = android_view_KeyEvent_obtainAsCopy(env, inputEventObj);
const InputEventInjectionResult result =
im->getInputManager()->getDispatcher().injectInputEvent(&keyEvent, targetUid, mode,
std::chrono::milliseconds(
@@ -2101,7 +2102,7 @@
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) {
- const KeyEvent keyEvent = android_view_KeyEvent_toNative(env, inputEventObj);
+ const KeyEvent keyEvent = android_view_KeyEvent_obtainAsCopy(env, inputEventObj);
std::unique_ptr<VerifiedInputEvent> verifiedEvent =
im->getInputManager()->getDispatcher().verifyInputEvent(keyEvent);
if (verifiedEvent == nullptr) {
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index 23aa374..96ef2ed 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -531,7 +531,7 @@
int index = 0;
for (CandidateBrowsingPhaseMetric metric : browsingPhaseMetrics) {
browsedClickedEntries[index] = metric.getEntryEnum();
- browsedProviderUid[index] = metric.getProviderUid();
+ browsedProviderUid[index] = DEFAULT_INT_32;
index++;
}
FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_FINALNOUID_REPORTED,
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING b/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING
new file mode 100644
index 0000000..e42bdad
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "postsubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.rollback"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index dfda1fc..3bc089f 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -662,7 +662,7 @@
android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
public void getEnhancedConfirmationTrustedInstallers_returnsTrustedInstallers()
throws IOException {
- String pkgName = "com.example.app";
+ String packageName = "com.example.app";
String certificateDigestStr = "E9:7A:BC:2C:D1:CA:8D:58:6A:57:0B:8C:F8:60:AA:D2:"
+ "8D:13:30:2A:FB:C9:00:2C:5D:53:B2:6C:09:A4:85:A0";
@@ -670,7 +670,7 @@
.toByteArray();
String contents = "<config>"
+ "<" + "enhanced-confirmation-trusted-installer" + " "
- + "package=\"" + pkgName + "\""
+ + "package=\"" + packageName + "\""
+ " sha256-cert-digest=\"" + certificateDigestStr + "\""
+ "/>"
+ "</config>";
@@ -684,10 +684,10 @@
assertThat(actualTrustedInstallers.size()).isEqualTo(1);
SignedPackage actual = actualTrustedInstallers.stream().findFirst().orElseThrow();
- SignedPackage expected = new SignedPackage(pkgName, certificateDigest);
+ SignedPackage expected = new SignedPackage(packageName, certificateDigest);
assertThat(actual.getCertificateDigest()).isEqualTo(expected.getCertificateDigest());
- assertThat(actual.getPkgName()).isEqualTo(expected.getPkgName());
+ assertThat(actual.getPackageName()).isEqualTo(expected.getPackageName());
assertThat(actual).isEqualTo(expected);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 266a0d5c..4c32a58 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -67,7 +67,7 @@
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
-import android.window.IUnhandledDragListener;
+import android.window.IGlobalDragListener;
import androidx.test.filters.SmallTest;
@@ -544,9 +544,9 @@
public void testUnhandledDragListenerNotCalledForNormalDrags() throws RemoteException {
assumeTrue(com.android.window.flags.Flags.delegateUnhandledDrags());
- final IUnhandledDragListener listener = mock(IUnhandledDragListener.class);
+ final IGlobalDragListener listener = mock(IGlobalDragListener.class);
doReturn(mock(Binder.class)).when(listener).asBinder();
- mTarget.setUnhandledDragListener(listener);
+ mTarget.setGlobalDragListener(listener);
doDragAndDrop(0, ClipData.newPlainText("label", "Test"), 0, 0);
verify(listener, times(0)).onUnhandledDrop(any(), any());
}
@@ -555,9 +555,9 @@
public void testUnhandledDragListenerReceivesUnhandledDropOverWindow() {
assumeTrue(com.android.window.flags.Flags.delegateUnhandledDrags());
- final IUnhandledDragListener listener = mock(IUnhandledDragListener.class);
+ final IGlobalDragListener listener = mock(IGlobalDragListener.class);
doReturn(mock(Binder.class)).when(listener).asBinder();
- mTarget.setUnhandledDragListener(listener);
+ mTarget.setGlobalDragListener(listener);
final int invalidXY = 100_000;
startDrag(View.DRAG_FLAG_GLOBAL, ClipData.newPlainText("label", "Test"), () -> {
// Notify the unhandled drag listener
@@ -578,9 +578,9 @@
public void testUnhandledDragListenerReceivesUnhandledDropOverNoValidWindow() {
assumeTrue(com.android.window.flags.Flags.delegateUnhandledDrags());
- final IUnhandledDragListener listener = mock(IUnhandledDragListener.class);
+ final IGlobalDragListener listener = mock(IGlobalDragListener.class);
doReturn(mock(Binder.class)).when(listener).asBinder();
- mTarget.setUnhandledDragListener(listener);
+ mTarget.setGlobalDragListener(listener);
final int invalidXY = 100_000;
startDrag(View.DRAG_FLAG_GLOBAL, ClipData.newPlainText("label", "Test"), () -> {
// Notify the unhandled drag listener
@@ -600,9 +600,9 @@
public void testUnhandledDragListenerCallbackTimeout() {
assumeTrue(com.android.window.flags.Flags.delegateUnhandledDrags());
- final IUnhandledDragListener listener = mock(IUnhandledDragListener.class);
+ final IGlobalDragListener listener = mock(IGlobalDragListener.class);
doReturn(mock(Binder.class)).when(listener).asBinder();
- mTarget.setUnhandledDragListener(listener);
+ mTarget.setGlobalDragListener(listener);
final int invalidXY = 100_000;
startDrag(View.DRAG_FLAG_GLOBAL, ClipData.newPlainText("label", "Test"), () -> {
// Notify the unhandled drag listener
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index f628af1..452c98c 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -296,6 +296,10 @@
clickObject(MEDIA_SESSION_START_RADIO_BUTTON_ID)
}
+ fun setSourceRectHint() {
+ clickObject(SOURCE_RECT_HINT)
+ }
+
fun checkWithCustomActionsCheckbox() =
uiDevice
.findObject(By.res(packageName, WITH_CUSTOM_ACTIONS_BUTTON_ID))
@@ -444,6 +448,7 @@
private const val MEDIA_SESSION_START_RADIO_BUTTON_ID = "media_session_start"
private const val ENTER_PIP_ON_USER_LEAVE_HINT = "enter_pip_on_leave_manual"
private const val ENTER_PIP_AUTOENTER = "enter_pip_on_leave_autoenter"
+ private const val SOURCE_RECT_HINT = "set_source_rect_hint"
// minimum number of steps to take, when animating gestures, needs to be 2
// so that there is at least a single intermediate layer that flicker tests can check
private const val MIN_STEPS_TO_ANIMATE = 2
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
index f7ba45b..36cbf1a 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
@@ -27,6 +27,15 @@
where things are arranged differently and to circle back up to the top once we reach the
bottom. -->
+ <!-- View used for testing sourceRectHint. -->
+ <View
+ android:id="@+id/source_rect"
+ android:layout_width="320dp"
+ android:layout_height="180dp"
+ android:visibility="gone"
+ android:background="@android:color/holo_green_light"
+ />
+
<Button
android:id="@+id/enter_pip"
android:layout_width="wrap_content"
@@ -113,6 +122,13 @@
android:onClick="onRatioSelected"/>
</RadioGroup>
+ <Button
+ android:id="@+id/set_source_rect_hint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Set SourceRectHint"
+ android:onClick="setSourceRectHint"/>
+
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
index 12eaad1..1ab8ddb 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
@@ -37,6 +37,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.media.MediaMetadata;
import android.media.session.MediaSession;
@@ -45,6 +46,7 @@
import android.util.Log;
import android.util.Rational;
import android.view.View;
+import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
import android.widget.CheckBox;
@@ -248,6 +250,29 @@
}
}
+ /**
+ * Adds a temporary view used for testing sourceRectHint.
+ *
+ */
+ public void setSourceRectHint(View v) {
+ View rectView = findViewById(R.id.source_rect);
+ if (rectView != null) {
+ rectView.setVisibility(View.VISIBLE);
+ rectView.getViewTreeObserver().addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ Rect boundingRect = new Rect();
+ rectView.getGlobalVisibleRect(boundingRect);
+ mPipParamsBuilder.setSourceRectHint(boundingRect);
+ setPictureInPictureParams(mPipParamsBuilder.build());
+ rectView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ }
+ });
+ rectView.invalidate(); // changing the visibility, invalidating to redraw the view
+ }
+ }
+
public void onRatioSelected(View v) {
switch (v.getId()) {
case R.id.ratio_default:
diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp
index 228520e..bc1df75 100644
--- a/tests/vcn/Android.bp
+++ b/tests/vcn/Android.bp
@@ -3,6 +3,7 @@
//########################################################################
package {
+ default_team: "trendy_team_enigma",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
diff --git a/wifi/TEST_MAPPING b/wifi/TEST_MAPPING
index 757ecaa..3ae91be 100644
--- a/wifi/TEST_MAPPING
+++ b/wifi/TEST_MAPPING
@@ -2,9 +2,7 @@
"presubmit": [
{
"name": "FrameworksWifiNonUpdatableApiTests"
- }
- ],
- "postsubmit": [
+ },
{
"name": "CtsWifiNonUpdatableTestCases"
}