Merge "[Media TTT] Fix a11y focus on receiver icon" into tm-qpr-dev
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 93c0c4d..8c00c6a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2411,6 +2411,7 @@
public abstract class DreamOverlayService extends android.app.Service {
ctor public DreamOverlayService();
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public void onEndDream();
method public abstract void onStartDream(@NonNull android.view.WindowManager.LayoutParams);
method public final void requestExit();
method public final boolean shouldShowComplications();
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index baf8836..b6cd06e 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -168,7 +168,7 @@
* The <code>android:label</code> attribute specifies a human-readable descriptive
* label to describe the keyboard layout in the user interface, such as "English (US)".
* The <code>android:keyboardLayout</code> attribute refers to a
- * <a href="http://source.android.com/tech/input/key-character-map-files.html">
+ * <a href="https://source.android.com/docs/core/interaction/input/key-character-map-files">
* key character map</a> resource that defines the keyboard layout.
* </p>
*/
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 6330661..1929a4d 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -536,8 +536,8 @@
mWarnOnBlocking = false;
warnOnBlocking = false;
- if (Build.IS_USERDEBUG) {
- // Log this as a WTF on userdebug builds.
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ // Log this as a WTF on userdebug and eng builds.
Log.wtf(Binder.TAG,
"Outgoing transactions from this process must be FLAG_ONEWAY",
new Throwable());
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 497bfa6..d9604b3f 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -2555,6 +2555,8 @@
* This API doesn't require any special permissions, though typical implementations
* will require being called from an SELinux domain that allows setting file attributes
* related to quota (eg the GID or project ID).
+ * If the calling user has MANAGE_EXTERNAL_STORAGE permissions, quota for shared profile's
+ * volumes is also updated.
*
* The default platform user of this API is the MediaProvider process, which is
* responsible for managing all of external storage.
@@ -2575,7 +2577,14 @@
@QuotaType int quotaType) throws IOException {
long projectId;
final String filePath = path.getCanonicalPath();
- final StorageVolume volume = getStorageVolume(path);
+ int volFlags = FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE;
+ // If caller has MANAGE_EXTERNAL_STORAGE permission, results from User Profile(s) are also
+ // returned by enabling FLAG_INCLUDE_SHARED_PROFILE.
+ if (mContext.checkSelfPermission(MANAGE_EXTERNAL_STORAGE) == PERMISSION_GRANTED) {
+ volFlags |= FLAG_INCLUDE_SHARED_PROFILE;
+ }
+ final StorageVolume[] availableVolumes = getVolumeList(mContext.getUserId(), volFlags);
+ final StorageVolume volume = getStorageVolume(availableVolumes, path);
if (volume == null) {
Log.w(TAG, "Failed to update quota type for " + filePath);
return;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d8d0bb2..ce4a735 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9534,7 +9534,7 @@
/**
* Indicates whether "seen" notifications should be suppressed from the lockscreen.
* <p>
- * Type: int (0 for false, 1 for true)
+ * Type: int (0 for unset, 1 for true, 2 for false)
*
* @hide
*/
diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java
index 6e8198b..bf5b970 100644
--- a/core/java/android/service/dreams/DreamOverlayService.java
+++ b/core/java/android/service/dreams/DreamOverlayService.java
@@ -51,6 +51,11 @@
}
@Override
+ public void endDream() {
+ onEndDream();
+ }
+
+ @Override
public void wakeUp() {
onWakeUp(() -> {
try {
@@ -83,13 +88,22 @@
/**
* This method is overridden by implementations to handle when the dream has been requested
- * to wakeup. This allows any overlay animations to run.
+ * to wakeup. This allows any overlay animations to run. By default, the method will invoke
+ * the callback immediately.
*
* @param onCompleteCallback The callback to trigger to notify the dream service that the
* overlay has completed waking up.
* @hide
*/
public void onWakeUp(@NonNull Runnable onCompleteCallback) {
+ onCompleteCallback.run();
+ }
+
+ /**
+ * This method is overridden by implementations to handle when the dream has ended. There may
+ * be earlier signals leading up to this step, such as @{@link #onWakeUp(Runnable)}.
+ */
+ public void onEndDream() {
}
/**
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 8b9852a..d378886 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -297,14 +297,20 @@
}
public void addConsumer(Consumer<IDreamOverlay> consumer) {
- mConsumers.add(consumer);
- if (mOverlay != null) {
- consumer.accept(mOverlay);
- }
+ execute(() -> {
+ mConsumers.add(consumer);
+ if (mOverlay != null) {
+ consumer.accept(mOverlay);
+ }
+ });
}
public void removeConsumer(Consumer<IDreamOverlay> consumer) {
- mConsumers.remove(consumer);
+ execute(() -> mConsumers.remove(consumer));
+ }
+
+ public void clearConsumers() {
+ execute(() -> mConsumers.clear());
}
}
@@ -1383,6 +1389,17 @@
@Override
public void onViewDetachedFromWindow(View v) {
+ if (mOverlayConnection != null) {
+ mOverlayConnection.addConsumer(overlay -> {
+ try {
+ overlay.endDream();
+ } catch (RemoteException e) {
+ Log.e(mTag, "could not inform overlay of dream end:" + e);
+ }
+ });
+ mOverlayConnection.clearConsumers();
+ }
+
if (mActivity == null || !mActivity.isChangingConfigurations()) {
// Only stop the dream if the view is not detached by relaunching
// activity for configuration changes. It is important to also clear
@@ -1391,9 +1408,6 @@
mActivity = null;
finish();
}
- if (mOverlayConnection != null && mDreamStartOverlayConsumer != null) {
- mOverlayConnection.removeConsumer(mDreamStartOverlayConsumer);
- }
}
});
}
diff --git a/core/java/android/service/dreams/IDreamOverlay.aidl b/core/java/android/service/dreams/IDreamOverlay.aidl
index 7aeceb2c..0e4bd3b 100644
--- a/core/java/android/service/dreams/IDreamOverlay.aidl
+++ b/core/java/android/service/dreams/IDreamOverlay.aidl
@@ -41,4 +41,7 @@
/** Called when the dream is waking, to do any exit animations */
void wakeUp();
+
+ /** Called when the dream has ended. */
+ void endDream();
}
diff --git a/core/java/com/android/internal/util/ObservableServiceConnection.java b/core/java/com/android/internal/util/ObservableServiceConnection.java
index 3165d29..45256fd 100644
--- a/core/java/com/android/internal/util/ObservableServiceConnection.java
+++ b/core/java/com/android/internal/util/ObservableServiceConnection.java
@@ -165,6 +165,13 @@
}
/**
+ * Executes code on the executor specified at construction.
+ */
+ public void execute(Runnable runnable) {
+ mExecutor.execute(runnable);
+ }
+
+ /**
* Initiate binding to the service.
*
* @return {@code true} if initiating binding succeed, {@code false} if the binding failed or
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 79c5196..3a393b6 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -1,7 +1,7 @@
package com.android.internal.util;
import static android.content.Intent.ACTION_USER_SWITCHED;
-import static android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE;
+import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -11,29 +11,18 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.graphics.Bitmap;
-import android.graphics.ColorSpace;
-import android.graphics.Insets;
-import android.graphics.ParcelableColorSpace;
-import android.graphics.Rect;
-import android.hardware.HardwareBuffer;
import android.net.Uri;
-import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import android.view.WindowManager.ScreenshotSource;
-import android.view.WindowManager.ScreenshotType;
import com.android.internal.annotations.VisibleForTesting;
-import java.util.Objects;
import java.util.function.Consumer;
public class ScreenshotHelper {
@@ -41,212 +30,6 @@
public static final int SCREENSHOT_MSG_URI = 1;
public static final int SCREENSHOT_MSG_PROCESS_COMPLETE = 2;
- /**
- * Describes a screenshot request.
- */
- public static class ScreenshotRequest implements Parcelable {
- @ScreenshotType
- private final int mType;
-
- @ScreenshotSource
- private final int mSource;
-
- private final Bundle mBitmapBundle;
- private final Rect mBoundsInScreen;
- private final Insets mInsets;
- private final int mTaskId;
- private final int mUserId;
- private final ComponentName mTopComponent;
-
-
- public ScreenshotRequest(@ScreenshotType int type, @ScreenshotSource int source) {
- this(type, source, /* topComponent */ null);
- }
-
- public ScreenshotRequest(@ScreenshotType int type, @ScreenshotSource int source,
- ComponentName topComponent) {
- this(type,
- source,
- /* bitmapBundle*/ null,
- /* boundsInScreen */ null,
- /* insets */ null,
- /* taskId */ -1,
- /* userId */ -1,
- topComponent);
- }
-
- public ScreenshotRequest(@ScreenshotType int type, @ScreenshotSource int source,
- Bundle bitmapBundle, Rect boundsInScreen, Insets insets, int taskId, int userId,
- ComponentName topComponent) {
- mType = type;
- mSource = source;
- mBitmapBundle = bitmapBundle;
- mBoundsInScreen = boundsInScreen;
- mInsets = insets;
- mTaskId = taskId;
- mUserId = userId;
- mTopComponent = topComponent;
- }
-
- ScreenshotRequest(Parcel in) {
- mType = in.readInt();
- mSource = in.readInt();
- if (in.readInt() == 1) {
- mBitmapBundle = in.readBundle(getClass().getClassLoader());
- mBoundsInScreen = in.readParcelable(Rect.class.getClassLoader(), Rect.class);
- mInsets = in.readParcelable(Insets.class.getClassLoader(), Insets.class);
- mTaskId = in.readInt();
- mUserId = in.readInt();
- mTopComponent = in.readParcelable(ComponentName.class.getClassLoader(),
- ComponentName.class);
- } else {
- mBitmapBundle = null;
- mBoundsInScreen = null;
- mInsets = null;
- mTaskId = -1;
- mUserId = -1;
- mTopComponent = null;
- }
- }
-
- @ScreenshotType
- public int getType() {
- return mType;
- }
-
- @ScreenshotSource
- public int getSource() {
- return mSource;
- }
-
- public Bundle getBitmapBundle() {
- return mBitmapBundle;
- }
-
- public Rect getBoundsInScreen() {
- return mBoundsInScreen;
- }
-
- public Insets getInsets() {
- return mInsets;
- }
-
- public int getTaskId() {
- return mTaskId;
- }
-
- public int getUserId() {
- return mUserId;
- }
-
- public ComponentName getTopComponent() {
- return mTopComponent;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mType);
- dest.writeInt(mSource);
- if (mBitmapBundle == null) {
- dest.writeInt(0);
- } else {
- dest.writeInt(1);
- dest.writeBundle(mBitmapBundle);
- dest.writeParcelable(mBoundsInScreen, 0);
- dest.writeParcelable(mInsets, 0);
- dest.writeInt(mTaskId);
- dest.writeInt(mUserId);
- dest.writeParcelable(mTopComponent, 0);
- }
- }
-
- @NonNull
- public static final Parcelable.Creator<ScreenshotRequest> CREATOR =
- new Parcelable.Creator<ScreenshotRequest>() {
-
- @Override
- public ScreenshotRequest createFromParcel(Parcel source) {
- return new ScreenshotRequest(source);
- }
-
- @Override
- public ScreenshotRequest[] newArray(int size) {
- return new ScreenshotRequest[size];
- }
- };
- }
-
- /**
- * Bundler used to convert between a hardware bitmap and a bundle without copying the internal
- * content. This is expected to be used together with {@link #provideScreenshot} to handle a
- * hardware bitmap as a screenshot.
- */
- public static final class HardwareBitmapBundler {
- private static final String KEY_BUFFER = "bitmap_util_buffer";
- private static final String KEY_COLOR_SPACE = "bitmap_util_color_space";
-
- private HardwareBitmapBundler() {
- }
-
- /**
- * Creates a Bundle that represents the given Bitmap.
- * <p>The Bundle will contain a wrapped version of the Bitmaps HardwareBuffer, so will avoid
- * copies when passing across processes, only pass to processes you trust.
- *
- * <p>Returns a new Bundle rather than modifying an exiting one to avoid key collisions, the
- * returned Bundle should be treated as a standalone object.
- *
- * @param bitmap to convert to bundle
- * @return a Bundle representing the bitmap, should only be parsed by
- * {@link #bundleToHardwareBitmap(Bundle)}
- */
- public static Bundle hardwareBitmapToBundle(Bitmap bitmap) {
- if (bitmap.getConfig() != Bitmap.Config.HARDWARE) {
- throw new IllegalArgumentException(
- "Passed bitmap must have hardware config, found: " + bitmap.getConfig());
- }
-
- // Bitmap assumes SRGB for null color space
- ParcelableColorSpace colorSpace =
- bitmap.getColorSpace() == null
- ? new ParcelableColorSpace(ColorSpace.get(ColorSpace.Named.SRGB))
- : new ParcelableColorSpace(bitmap.getColorSpace());
-
- Bundle bundle = new Bundle();
- bundle.putParcelable(KEY_BUFFER, bitmap.getHardwareBuffer());
- bundle.putParcelable(KEY_COLOR_SPACE, colorSpace);
-
- return bundle;
- }
-
- /**
- * Extracts the Bitmap added to a Bundle with {@link #hardwareBitmapToBundle(Bitmap)} .}
- *
- * <p>This Bitmap contains the HardwareBuffer from the original caller, be careful passing
- * this Bitmap on to any other source.
- *
- * @param bundle containing the bitmap
- * @return a hardware Bitmap
- */
- public static Bitmap bundleToHardwareBitmap(Bundle bundle) {
- if (!bundle.containsKey(KEY_BUFFER) || !bundle.containsKey(KEY_COLOR_SPACE)) {
- throw new IllegalArgumentException("Bundle does not contain a hardware bitmap");
- }
-
- HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER, HardwareBuffer.class);
- ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE,
- ParcelableColorSpace.class);
-
- return Bitmap.wrapHardwareBuffer(Objects.requireNonNull(buffer),
- colorSpace.getColorSpace());
- }
- }
-
private static final String TAG = "ScreenshotHelper";
// Time until we give up on the screenshot & show an error instead.
@@ -277,20 +60,35 @@
/**
* Request a screenshot be taken.
* <p>
- * Added to support reducing unit test duration; the method variant without a timeout argument
- * is recommended for general use.
+ * Convenience method for taking a full screenshot with provided source.
*
- * @param type The type of screenshot, defined by {@link ScreenshotType}
- * @param source The source of the screenshot request, defined by {@link ScreenshotSource}
- * @param handler used to process messages received from the screenshot service
+ * @param source source of the screenshot request, defined by {@link
+ * ScreenshotSource}
+ * @param handler used to process messages received from the screenshot service
* @param completionConsumer receives the URI of the captured screenshot, once saved or
- * null if no screenshot was saved
+ * null if no screenshot was saved
*/
- public void takeScreenshot(@ScreenshotType int type, @ScreenshotSource int source,
- @NonNull Handler handler, @Nullable Consumer<Uri> completionConsumer) {
- ScreenshotRequest screenshotRequest = new ScreenshotRequest(type, source);
- takeScreenshot(handler, screenshotRequest, SCREENSHOT_TIMEOUT_MS,
- completionConsumer);
+ public void takeScreenshot(@ScreenshotSource int source, @NonNull Handler handler,
+ @Nullable Consumer<Uri> completionConsumer) {
+ ScreenshotRequest request =
+ new ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, source).build();
+ takeScreenshot(request, handler, completionConsumer);
+ }
+
+ /**
+ * Request a screenshot be taken.
+ * <p>
+ *
+ * @param request description of the screenshot request, either for taking a
+ * screenshot or
+ * providing a bitmap
+ * @param handler used to process messages received from the screenshot service
+ * @param completionConsumer receives the URI of the captured screenshot, once saved or
+ * null if no screenshot was saved
+ */
+ public void takeScreenshot(ScreenshotRequest request, @NonNull Handler handler,
+ @Nullable Consumer<Uri> completionConsumer) {
+ takeScreenshotInternal(request, handler, completionConsumer, SCREENSHOT_TIMEOUT_MS);
}
/**
@@ -299,46 +97,16 @@
* Added to support reducing unit test duration; the method variant without a timeout argument
* is recommended for general use.
*
- * @param type The type of screenshot, defined by {@link ScreenshotType}
- * @param source The source of the screenshot request, defined by {@link ScreenshotSource}
- * @param handler used to process messages received from the screenshot service
- * @param timeoutMs time limit for processing, intended only for testing
+ * @param request description of the screenshot request, either for taking a
+ * screenshot or providing a bitmap
+ * @param handler used to process messages received from the screenshot service
+ * @param timeoutMs time limit for processing, intended only for testing
* @param completionConsumer receives the URI of the captured screenshot, once saved or
- * null if no screenshot was saved
+ * null if no screenshot was saved
*/
@VisibleForTesting
- public void takeScreenshot(@ScreenshotType int type, @ScreenshotSource int source,
- @NonNull Handler handler, long timeoutMs, @Nullable Consumer<Uri> completionConsumer) {
- ScreenshotRequest screenshotRequest = new ScreenshotRequest(type, source);
- takeScreenshot(handler, screenshotRequest, timeoutMs, completionConsumer);
- }
-
- /**
- * Request that provided image be handled as if it was a screenshot.
- *
- * @param screenshotBundle Bundle containing the buffer and color space of the screenshot.
- * @param boundsInScreen The bounds in screen coordinates that the bitmap originated from.
- * @param insets The insets that the image was shown with, inside the screen bounds.
- * @param taskId The taskId of the task that the screen shot was taken of.
- * @param userId The userId of user running the task provided in taskId.
- * @param topComponent The component name of the top component running in the task.
- * @param source The source of the screenshot request, defined by {@link ScreenshotSource}
- * @param handler A handler used in case the screenshot times out
- * @param completionConsumer receives the URI of the captured screenshot, once saved or
- * null if no screenshot was saved
- */
- public void provideScreenshot(@NonNull Bundle screenshotBundle, @NonNull Rect boundsInScreen,
- @NonNull Insets insets, int taskId, int userId, ComponentName topComponent,
- @ScreenshotSource int source, @NonNull Handler handler,
- @Nullable Consumer<Uri> completionConsumer) {
- ScreenshotRequest screenshotRequest = new ScreenshotRequest(TAKE_SCREENSHOT_PROVIDED_IMAGE,
- source, screenshotBundle, boundsInScreen, insets, taskId, userId, topComponent);
- takeScreenshot(handler, screenshotRequest, SCREENSHOT_TIMEOUT_MS, completionConsumer);
- }
-
- private void takeScreenshot(@NonNull Handler handler,
- ScreenshotRequest screenshotRequest, long timeoutMs,
- @Nullable Consumer<Uri> completionConsumer) {
+ public void takeScreenshotInternal(ScreenshotRequest request, @NonNull Handler handler,
+ @Nullable Consumer<Uri> completionConsumer, long timeoutMs) {
synchronized (mScreenshotLock) {
final Runnable mScreenshotTimeout = () -> {
@@ -354,7 +122,7 @@
}
};
- Message msg = Message.obtain(null, 0, screenshotRequest);
+ Message msg = Message.obtain(null, 0, request);
Handler h = new Handler(handler.getLooper()) {
@Override
@@ -471,5 +239,4 @@
Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(errorIntent, UserHandle.CURRENT);
}
-
}
diff --git a/core/java/com/android/internal/util/ScreenshotRequest.aidl b/core/java/com/android/internal/util/ScreenshotRequest.aidl
new file mode 100644
index 0000000..b08905d
--- /dev/null
+++ b/core/java/com/android/internal/util/ScreenshotRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.internal.util;
+
+parcelable ScreenshotRequest;
\ No newline at end of file
diff --git a/core/java/com/android/internal/util/ScreenshotRequest.java b/core/java/com/android/internal/util/ScreenshotRequest.java
new file mode 100644
index 0000000..1902f80
--- /dev/null
+++ b/core/java/com/android/internal/util/ScreenshotRequest.java
@@ -0,0 +1,332 @@
+/*
+ * 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.internal.util;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.os.UserHandle.USER_NULL;
+import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
+import static android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.graphics.Insets;
+import android.graphics.ParcelableColorSpace;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.view.WindowManager;
+
+import java.util.Objects;
+
+/**
+ * Describes a screenshot request.
+ */
+public class ScreenshotRequest implements Parcelable {
+ private static final String TAG = "ScreenshotRequest";
+
+ @WindowManager.ScreenshotType
+ private final int mType;
+ @WindowManager.ScreenshotSource
+ private final int mSource;
+ private final ComponentName mTopComponent;
+ private final int mTaskId;
+ private final int mUserId;
+ private final Bitmap mBitmap;
+ private final Rect mBoundsInScreen;
+ private final Insets mInsets;
+
+ private ScreenshotRequest(
+ @WindowManager.ScreenshotType int type, @WindowManager.ScreenshotSource int source,
+ ComponentName topComponent, int taskId, int userId,
+ Bitmap bitmap, Rect boundsInScreen, Insets insets) {
+ mType = type;
+ mSource = source;
+ mTopComponent = topComponent;
+ mTaskId = taskId;
+ mUserId = userId;
+ mBitmap = bitmap;
+ mBoundsInScreen = boundsInScreen;
+ mInsets = insets;
+ }
+
+ ScreenshotRequest(Parcel in) {
+ mType = in.readInt();
+ mSource = in.readInt();
+ mTopComponent = in.readTypedObject(ComponentName.CREATOR);
+ mTaskId = in.readInt();
+ mUserId = in.readInt();
+ mBitmap = HardwareBitmapBundler.bundleToHardwareBitmap(in.readTypedObject(Bundle.CREATOR));
+ mBoundsInScreen = in.readTypedObject(Rect.CREATOR);
+ mInsets = in.readTypedObject(Insets.CREATOR);
+ }
+
+ @WindowManager.ScreenshotType
+ public int getType() {
+ return mType;
+ }
+
+ @WindowManager.ScreenshotSource
+ public int getSource() {
+ return mSource;
+ }
+
+ public Bitmap getBitmap() {
+ return mBitmap;
+ }
+
+ public Rect getBoundsInScreen() {
+ return mBoundsInScreen;
+ }
+
+ public Insets getInsets() {
+ return mInsets;
+ }
+
+ public int getTaskId() {
+ return mTaskId;
+ }
+
+ public int getUserId() {
+ return mUserId;
+ }
+
+ public ComponentName getTopComponent() {
+ return mTopComponent;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeInt(mSource);
+ dest.writeTypedObject(mTopComponent, 0);
+ dest.writeInt(mTaskId);
+ dest.writeInt(mUserId);
+ dest.writeTypedObject(HardwareBitmapBundler.hardwareBitmapToBundle(mBitmap), 0);
+ dest.writeTypedObject(mBoundsInScreen, 0);
+ dest.writeTypedObject(mInsets, 0);
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<ScreenshotRequest> CREATOR =
+ new Parcelable.Creator<ScreenshotRequest>() {
+
+ @Override
+ public ScreenshotRequest createFromParcel(Parcel source) {
+ return new ScreenshotRequest(source);
+ }
+
+ @Override
+ public ScreenshotRequest[] newArray(int size) {
+ return new ScreenshotRequest[size];
+ }
+ };
+
+ /**
+ * Builder class for {@link ScreenshotRequest} objects.
+ */
+ public static class Builder {
+ @WindowManager.ScreenshotType
+ private final int mType;
+
+ @WindowManager.ScreenshotSource
+ private final int mSource;
+
+ private Bitmap mBitmap;
+ private Rect mBoundsInScreen;
+ private Insets mInsets = Insets.NONE;
+ private int mTaskId = INVALID_TASK_ID;
+ private int mUserId = USER_NULL;
+ private ComponentName mTopComponent;
+
+ /**
+ * Begin building a ScreenshotRequest.
+ *
+ * @param type The type of the screenshot request, defined by {@link
+ * WindowManager.ScreenshotType}
+ * @param source The source of the screenshot request, defined by {@link
+ * WindowManager.ScreenshotSource}
+ */
+ public Builder(
+ @WindowManager.ScreenshotType int type,
+ @WindowManager.ScreenshotSource int source) {
+ mType = type;
+ mSource = source;
+ }
+
+ /**
+ * Construct a new {@link ScreenshotRequest} with the set parameters.
+ */
+ public ScreenshotRequest build() {
+ if (mType == TAKE_SCREENSHOT_FULLSCREEN && mBitmap != null) {
+ Log.w(TAG, "Bitmap provided, but request is fullscreen. Bitmap will be ignored.");
+ }
+ if (mType == TAKE_SCREENSHOT_PROVIDED_IMAGE && mBitmap == null) {
+ throw new IllegalStateException(
+ "Request is PROVIDED_IMAGE, but no bitmap is provided!");
+ }
+
+ return new ScreenshotRequest(mType, mSource, mTopComponent, mTaskId, mUserId, mBitmap,
+ mBoundsInScreen, mInsets);
+ }
+
+ /**
+ * Set the top component associated with this request.
+ *
+ * @param topComponent The component name of the top component running in the task.
+ */
+ public Builder setTopComponent(ComponentName topComponent) {
+ mTopComponent = topComponent;
+ return this;
+ }
+
+ /**
+ * Set the task id associated with this request.
+ *
+ * @param taskId The taskId of the task that the screenshot was taken of.
+ */
+ public Builder setTaskId(int taskId) {
+ mTaskId = taskId;
+ return this;
+ }
+
+ /**
+ * Set the user id associated with this request.
+ *
+ * @param userId The userId of user running the task provided in taskId.
+ */
+ public Builder setUserId(int userId) {
+ mUserId = userId;
+ return this;
+ }
+
+ /**
+ * Set the bitmap associated with this request.
+ *
+ * @param bitmap The provided screenshot.
+ */
+ public Builder setBitmap(Bitmap bitmap) {
+ mBitmap = bitmap;
+ return this;
+ }
+
+ /**
+ * Set the bounds for the provided bitmap.
+ *
+ * @param bounds The bounds in screen coordinates that the bitmap originated from.
+ */
+ public Builder setBoundsOnScreen(Rect bounds) {
+ mBoundsInScreen = bounds;
+ return this;
+ }
+
+ /**
+ * Set the insets for the provided bitmap.
+ *
+ * @param insets The insets that the image was shown with, inside the screen bounds.
+ */
+ public Builder setInsets(@NonNull Insets insets) {
+ mInsets = insets;
+ return this;
+ }
+ }
+
+ /**
+ * Bundler used to convert between a hardware bitmap and a bundle without copying the internal
+ * content. This is used together with a fully-defined ScreenshotRequest to handle a hardware
+ * bitmap as a screenshot.
+ */
+ private static final class HardwareBitmapBundler {
+ private static final String KEY_BUFFER = "bitmap_util_buffer";
+ private static final String KEY_COLOR_SPACE = "bitmap_util_color_space";
+
+ private HardwareBitmapBundler() {
+ }
+
+ /**
+ * Creates a Bundle that represents the given Bitmap.
+ * <p>The Bundle will contain a wrapped version of the Bitmaps HardwareBuffer, so will
+ * avoid
+ * copies when passing across processes, only pass to processes you trust.
+ *
+ * <p>Returns a new Bundle rather than modifying an exiting one to avoid key collisions,
+ * the
+ * returned Bundle should be treated as a standalone object.
+ *
+ * @param bitmap to convert to bundle
+ * @return a Bundle representing the bitmap, should only be parsed by
+ * {@link #bundleToHardwareBitmap(Bundle)}
+ */
+ private static Bundle hardwareBitmapToBundle(Bitmap bitmap) {
+ if (bitmap == null) {
+ return null;
+ }
+ if (bitmap.getConfig() != Bitmap.Config.HARDWARE) {
+ throw new IllegalArgumentException(
+ "Passed bitmap must have hardware config, found: "
+ + bitmap.getConfig());
+ }
+
+ // Bitmap assumes SRGB for null color space
+ ParcelableColorSpace colorSpace =
+ bitmap.getColorSpace() == null
+ ? new ParcelableColorSpace(ColorSpace.get(ColorSpace.Named.SRGB))
+ : new ParcelableColorSpace(bitmap.getColorSpace());
+
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(KEY_BUFFER, bitmap.getHardwareBuffer());
+ bundle.putParcelable(KEY_COLOR_SPACE, colorSpace);
+
+ return bundle;
+ }
+
+ /**
+ * Extracts the Bitmap added to a Bundle with {@link #hardwareBitmapToBundle(Bitmap)}.
+ *
+ * <p>This Bitmap contains the HardwareBuffer from the original caller, be careful
+ * passing
+ * this Bitmap on to any other source.
+ *
+ * @param bundle containing the bitmap
+ * @return a hardware Bitmap
+ */
+ private static Bitmap bundleToHardwareBitmap(Bundle bundle) {
+ if (bundle == null) {
+ return null;
+ }
+ if (!bundle.containsKey(KEY_BUFFER) || !bundle.containsKey(KEY_COLOR_SPACE)) {
+ throw new IllegalArgumentException("Bundle does not contain a hardware bitmap");
+ }
+
+ HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER, HardwareBuffer.class);
+ ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE,
+ ParcelableColorSpace.class);
+
+ return Bitmap.wrapHardwareBuffer(Objects.requireNonNull(buffer),
+ colorSpace.getColorSpace());
+ }
+ }
+}
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index ea6e1f1..a99ba15 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -72,8 +72,8 @@
<item name="secondary_content_alpha_material_dark" format="float" type="dimen">.7</item>
<item name="secondary_content_alpha_material_light" format="float" type="dimen">0.60</item>
- <item name="highlight_alpha_material_light" format="float" type="dimen">0.10</item>
- <item name="highlight_alpha_material_dark" format="float" type="dimen">0.10</item>
+ <item name="highlight_alpha_material_light" format="float" type="dimen">0.5</item>
+ <item name="highlight_alpha_material_dark" format="float" type="dimen">0.5</item>
<item name="highlight_alpha_material_colored" format="float" type="dimen">0.10</item>
<!-- Primary & accent colors -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5f0753bb..a6be07d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2078,6 +2078,9 @@
STREAM_MUSIC as if it's on TV platform. -->
<bool name="config_single_volume">false</bool>
+ <!-- Volume policy -->
+ <bool name="config_volume_down_to_enter_silent">false</bool>
+
<!-- The number of volume steps for the notification stream -->
<integer name="config_audio_notif_vol_steps">7</integer>
@@ -5356,6 +5359,9 @@
<!-- Whether using split screen aspect ratio as a default aspect ratio for unresizable apps. -->
<bool name="config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled">false</bool>
+ <!-- Whether using display aspect ratio as a default aspect ratio for all letterboxed apps. -->
+ <bool name="config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled">false</bool>
+
<!-- Whether the specific behaviour for translucent activities letterboxing is enabled.
TODO(b/255532890) Enable when ignoreOrientationRequest is set -->
<bool name="config_letterboxIsEnabledForTranslucentActivities">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4d4be22..be9f6d2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -321,6 +321,7 @@
<java-symbol type="bool" name="config_use_strict_phone_number_comparation_for_kazakhstan" />
<java-symbol type="integer" name="config_phonenumber_compare_min_match" />
<java-symbol type="bool" name="config_single_volume" />
+ <java-symbol type="bool" name="config_volume_down_to_enter_silent" />
<java-symbol type="bool" name="config_voice_capable" />
<java-symbol type="bool" name="config_requireCallCapableAccountForHandle" />
<java-symbol type="bool" name="config_user_notification_of_restrictied_mobile_access" />
@@ -4463,6 +4464,7 @@
<java-symbol type="bool" name="config_letterboxIsEducationEnabled" />
<java-symbol type="dimen" name="config_letterboxDefaultMinAspectRatioForUnresizableApps" />
<java-symbol type="bool" name="config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled" />
+ <java-symbol type="bool" name="config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled" />
<java-symbol type="bool" name="config_isCompatFakeFocusEnabled" />
<java-symbol type="bool" name="config_isWindowManagerCameraCompatTreatmentEnabled" />
<java-symbol type="bool" name="config_isCameraCompatControlForStretchedIssuesEnabled" />
diff --git a/core/tests/screenshothelpertests/Android.bp b/core/tests/screenshothelpertests/Android.bp
index 37af99c..3c71e6e 100644
--- a/core/tests/screenshothelpertests/Android.bp
+++ b/core/tests/screenshothelpertests/Android.bp
@@ -13,7 +13,7 @@
srcs: [
"src/**/*.java",
],
-
+
static_libs: [
"frameworks-base-testutils",
"androidx.test.runner",
@@ -21,6 +21,7 @@
"androidx.test.ext.junit",
"mockito-target-minus-junit4",
"platform-test-annotations",
+ "testng",
],
libs: [
diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
index 2719431..5c9894e 100644
--- a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
+++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
@@ -17,6 +17,7 @@
package com.android.internal.util;
import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
+import static android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.fail;
@@ -31,9 +32,11 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
import android.graphics.Insets;
import android.graphics.Rect;
-import android.os.Bundle;
+import android.hardware.HardwareBuffer;
import android.os.Handler;
import android.os.Looper;
import android.view.WindowManager;
@@ -79,30 +82,48 @@
@Test
public void testFullscreenScreenshot() {
- mScreenshotHelper.takeScreenshot(TAKE_SCREENSHOT_FULLSCREEN,
+ mScreenshotHelper.takeScreenshot(
WindowManager.ScreenshotSource.SCREENSHOT_OTHER, mHandler, null);
}
@Test
+ public void testFullscreenScreenshotRequest() {
+ ScreenshotRequest request = new ScreenshotRequest.Builder(
+ TAKE_SCREENSHOT_FULLSCREEN, WindowManager.ScreenshotSource.SCREENSHOT_OTHER)
+ .build();
+ mScreenshotHelper.takeScreenshot(request, mHandler, null);
+ }
+
+ @Test
public void testProvidedImageScreenshot() {
- mScreenshotHelper.provideScreenshot(
- new Bundle(), new Rect(), Insets.of(0, 0, 0, 0), 1, 1, new ComponentName("", ""),
- WindowManager.ScreenshotSource.SCREENSHOT_OTHER, mHandler, null);
+ HardwareBuffer buffer = HardwareBuffer.create(
+ 10, 10, HardwareBuffer.RGBA_8888, 1, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
+ Bitmap bitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB));
+ ScreenshotRequest request = new ScreenshotRequest.Builder(
+ TAKE_SCREENSHOT_PROVIDED_IMAGE, WindowManager.ScreenshotSource.SCREENSHOT_OTHER)
+ .setTopComponent(new ComponentName("", ""))
+ .setTaskId(1)
+ .setUserId(1)
+ .setBitmap(bitmap)
+ .setBoundsOnScreen(new Rect())
+ .setInsets(Insets.NONE)
+ .build();
+ mScreenshotHelper.takeScreenshot(request, mHandler, null);
}
@Test
public void testScreenshotTimesOut() {
long timeoutMs = 10;
+ ScreenshotRequest request = new ScreenshotRequest.Builder(
+ TAKE_SCREENSHOT_FULLSCREEN, WindowManager.ScreenshotSource.SCREENSHOT_OTHER)
+ .build();
CountDownLatch lock = new CountDownLatch(1);
- mScreenshotHelper.takeScreenshot(TAKE_SCREENSHOT_FULLSCREEN,
- WindowManager.ScreenshotSource.SCREENSHOT_OTHER,
- mHandler,
- timeoutMs,
+ mScreenshotHelper.takeScreenshotInternal(request, mHandler,
uri -> {
assertNull(uri);
lock.countDown();
- });
+ }, timeoutMs);
try {
// Add tolerance for delay to prevent flakes.
diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotRequestTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotRequestTest.java
new file mode 100644
index 0000000..30540a5
--- /dev/null
+++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotRequestTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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.internal.util;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.os.UserHandle.USER_NULL;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER;
+import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
+import static android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.ComponentName;
+import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.os.Parcel;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public final class ScreenshotRequestTest {
+ private final ComponentName mComponentName =
+ new ComponentName("android.test", "android.test.Component");
+
+ @Test
+ public void testSimpleScreenshot() {
+ ScreenshotRequest in =
+ new ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_OTHER).build();
+
+ Parcel parcel = Parcel.obtain();
+ in.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ ScreenshotRequest out = ScreenshotRequest.CREATOR.createFromParcel(parcel);
+
+ assertEquals(TAKE_SCREENSHOT_FULLSCREEN, out.getType());
+ assertEquals(SCREENSHOT_OTHER, out.getSource());
+ assertNull("Top component was expected to be null", out.getTopComponent());
+ assertEquals(INVALID_TASK_ID, out.getTaskId());
+ assertEquals(USER_NULL, out.getUserId());
+ assertNull("Bitmap was expected to be null", out.getBitmap());
+ assertNull("Bounds were expected to be null", out.getBoundsInScreen());
+ assertEquals(Insets.NONE, out.getInsets());
+ }
+
+ @Test
+ public void testProvidedScreenshot() {
+ Bitmap bitmap = makeHardwareBitmap(50, 50);
+ ScreenshotRequest in =
+ new ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER)
+ .setTopComponent(mComponentName)
+ .setTaskId(2)
+ .setUserId(3)
+ .setBitmap(bitmap)
+ .setBoundsOnScreen(new Rect(10, 10, 60, 60))
+ .setInsets(Insets.of(2, 3, 4, 5))
+ .build();
+
+ Parcel parcel = Parcel.obtain();
+ in.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ ScreenshotRequest out = ScreenshotRequest.CREATOR.createFromParcel(parcel);
+
+ assertEquals(TAKE_SCREENSHOT_PROVIDED_IMAGE, out.getType());
+ assertEquals(SCREENSHOT_OTHER, out.getSource());
+ assertEquals(mComponentName, out.getTopComponent());
+ assertEquals(2, out.getTaskId());
+ assertEquals(3, out.getUserId());
+ assertTrue("Bitmaps should be equal", out.getBitmap().sameAs(bitmap));
+ assertEquals(new Rect(10, 10, 60, 60), out.getBoundsInScreen());
+ assertEquals(Insets.of(2, 3, 4, 5), out.getInsets());
+ }
+
+ @Test
+ public void testProvidedScreenshot_nullBitmap() {
+ ScreenshotRequest.Builder inBuilder =
+ new ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER)
+ .setTopComponent(mComponentName)
+ .setTaskId(2)
+ .setUserId(3)
+ .setBoundsOnScreen(new Rect(10, 10, 60, 60))
+ .setInsets(Insets.of(2, 3, 4, 5));
+
+ assertThrows(IllegalStateException.class, inBuilder::build);
+ }
+
+ @Test
+ public void testFullScreenshot_withBitmap() {
+ // A bitmap added to a FULLSCREEN request will be ignored, but it's technically valid
+ Bitmap bitmap = makeHardwareBitmap(50, 50);
+ ScreenshotRequest in =
+ new ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_OTHER)
+ .setBitmap(bitmap)
+ .build();
+
+ Parcel parcel = Parcel.obtain();
+ in.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ ScreenshotRequest out = ScreenshotRequest.CREATOR.createFromParcel(parcel);
+
+ assertEquals(TAKE_SCREENSHOT_FULLSCREEN, out.getType());
+ assertEquals(SCREENSHOT_OTHER, out.getSource());
+ assertNull(out.getTopComponent());
+ assertEquals(INVALID_TASK_ID, out.getTaskId());
+ assertEquals(USER_NULL, out.getUserId());
+ assertTrue("Bitmaps should be equal", out.getBitmap().sameAs(bitmap));
+ assertNull("Bounds expected to be null", out.getBoundsInScreen());
+ assertEquals(Insets.NONE, out.getInsets());
+ }
+
+ private Bitmap makeHardwareBitmap(int width, int height) {
+ HardwareBuffer buffer = HardwareBuffer.create(
+ width, height, HardwareBuffer.RGBA_8888, 1, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
+ return Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB));
+ }
+}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 417a27d..7e2cac1 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -23,7 +23,6 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.animation.ValueAnimator;
-import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -983,9 +982,9 @@
RippleShader shader = new RippleShader();
// Grab the color for the current state and cut the alpha channel in
// half so that the ripple and background together yield full alpha.
- final int color = clampAlpha(mMaskColorFilter == null
+ final int color = mMaskColorFilter == null
? mState.mColor.getColorForState(getState(), Color.BLACK)
- : mMaskColorFilter.getColor());
+ : mMaskColorFilter.getColor();
final int effectColor = mState.mEffectColor.getColorForState(getState(), Color.MAGENTA);
final float noisePhase = AnimationUtils.currentAnimationTimeMillis();
shader.setColor(color, effectColor);
@@ -1008,13 +1007,6 @@
return properties;
}
- private int clampAlpha(@ColorInt int color) {
- if (Color.alpha(color) < 128) {
- return (color & 0x00FFFFFF) | 0x80000000;
- }
- return color;
- }
-
@Override
public void invalidateSelf() {
invalidateSelf(true);
@@ -1229,7 +1221,7 @@
// Grab the color for the current state and cut the alpha channel in
// half so that the ripple and background together yield full alpha.
- final int color = clampAlpha(mState.mColor.getColorForState(getState(), Color.BLACK));
+ final int color = mState.mColor.getColorForState(getState(), Color.BLACK);
final Paint p = mRipplePaint;
if (mMaskColorFilter != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 96efeeb..8484013 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -96,8 +96,7 @@
/**
* Different from {@link #equals(Object)}, this method compares the basic geometry properties
- * of two {@link DisplayLayout} objects including width, height, rotation, density, cutout and
- * insets.
+ * of two {@link DisplayLayout} objects including width, height, rotation, density, cutout.
* @return {@code true} if the given {@link DisplayLayout} is identical geometry wise.
*/
public boolean isSameGeometry(@NonNull DisplayLayout other) {
@@ -105,8 +104,7 @@
&& mHeight == other.mHeight
&& mRotation == other.mRotation
&& mDensityDpi == other.mDensityDpi
- && Objects.equals(mCutout, other.mCutout)
- && Objects.equals(mStableInsets, other.mStableInsets);
+ && Objects.equals(mCutout, other.mCutout);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
index b9caf62..26f47fc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -114,7 +114,9 @@
t.setPosition(leash, positionInParent.x, positionInParent.y);
t.setAlpha(leash, 1f);
t.setMatrix(leash, 1, 0, 0, 1);
- t.show(leash);
+ if (taskInfo.isVisible) {
+ t.show(leash);
+ }
});
}
}
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 e83854e..525beb1 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
@@ -616,7 +616,7 @@
return;
}
int oldMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
- onDisplayChanged(
+ onDisplayChangedUncheck(
mDisplayController.getDisplayLayout(mPipBoundsState.getDisplayId()),
false /* saveRestoreSnapFraction */);
int newMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
@@ -702,9 +702,12 @@
}
private void onDisplayChanged(DisplayLayout layout, boolean saveRestoreSnapFraction) {
- if (mPipBoundsState.getDisplayLayout().isSameGeometry(layout)) {
- return;
+ if (!mPipBoundsState.getDisplayLayout().isSameGeometry(layout)) {
+ onDisplayChangedUncheck(layout, saveRestoreSnapFraction);
}
+ }
+
+ private void onDisplayChangedUncheck(DisplayLayout layout, boolean saveRestoreSnapFraction) {
Runnable updateDisplayLayout = () -> {
final boolean fromRotation = Transitions.ENABLE_SHELL_TRANSITIONS
&& mPipBoundsState.getDisplayLayout().rotation() != layout.rotation();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 38099fc..21eeaa2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -18,6 +18,7 @@
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -535,17 +536,11 @@
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
- try {
- adapter.getRunner().onAnimationCancelled(false /* isKeyguardOccluded */);
- ActivityTaskManager.getService().startActivityFromRecents(taskId, options2);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error starting remote animation", e);
- }
+ taskId = INVALID_TASK_ID;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
"Cancel entering split as not supporting multi-instances");
Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
Toast.LENGTH_SHORT).show();
- return;
}
}
mStageCoordinator.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
@@ -586,17 +581,11 @@
fillInIntent2.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
- try {
- adapter.getRunner().onAnimationCancelled(false /* isKeyguardOccluded */);
- pendingIntent1.send();
- } catch (RemoteException | PendingIntent.CanceledException e) {
- Slog.e(TAG, "Error starting remote animation", e);
- }
+ pendingIntent2 = null;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
"Cancel entering split as not supporting multi-instances");
Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
Toast.LENGTH_SHORT).show();
- return;
}
}
mStageCoordinator.startIntentsWithLegacyTransition(pendingIntent1, fillInIntent1, options1,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index a6c4ac2..39cf5f1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -629,9 +629,19 @@
RemoteAnimationAdapter adapter, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (options1 == null) options1 = new Bundle();
+ if (pendingIntent2 == null) {
+ // Launching a solo task.
+ ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
+ activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
+ options1 = activityOptions.toBundle();
+ addActivityOptions(options1, null /* launchTarget */);
+ wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
+ mSyncQueue.queue(wct);
+ return;
+ }
+
addActivityOptions(options1, mSideStage);
wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
-
startWithLegacyTransition(wct, pendingIntent2, fillInIntent2, options2, splitPosition,
splitRatio, adapter, instanceId);
}
@@ -666,9 +676,19 @@
InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (options1 == null) options1 = new Bundle();
+ if (taskId == INVALID_TASK_ID) {
+ // Launching a solo task.
+ ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
+ activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
+ options1 = activityOptions.toBundle();
+ addActivityOptions(options1, null /* launchTarget */);
+ wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
+ mSyncQueue.queue(wct);
+ return;
+ }
+
addActivityOptions(options1, mSideStage);
wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
-
startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter,
instanceId);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
index ff1d2990..d5bb901 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
@@ -28,9 +28,11 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -82,13 +84,14 @@
@Mock
SyncTransactionQueue mSyncQueue;
@Mock
- TaskViewTransitions mTaskViewTransitions;
+ Transitions mTransitions;
SurfaceSession mSession;
SurfaceControl mLeash;
Context mContext;
TaskView mTaskView;
+ TaskViewTransitions mTaskViewTransitions;
@Before
public void setUp() {
@@ -118,6 +121,10 @@
return null;
}).when(mSyncQueue).runInSync(any());
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ doReturn(true).when(mTransitions).isRegistered();
+ }
+ mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions));
mTaskView = new TaskView(mContext, mOrganizer, mTaskViewTransitions, mSyncQueue);
mTaskView.setListener(mExecutor, mViewListener);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 65e1ea8..0bb809d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -29,11 +29,13 @@
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -45,6 +47,8 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
@@ -57,6 +61,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -65,6 +70,8 @@
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener;
+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;
@@ -98,11 +105,7 @@
@Mock
private DisplayInsetsController mDisplayInsetsController;
@Mock
- private Transitions mTransitions;
- @Mock
private TransactionPool mTransactionPool;
- @Mock
- private ShellExecutor mMainExecutor;
private final Rect mBounds1 = new Rect(10, 20, 30, 40);
private final Rect mBounds2 = new Rect(5, 10, 15, 20);
@@ -112,11 +115,16 @@
private SurfaceControl mRootLeash;
private ActivityManager.RunningTaskInfo mRootTask;
private StageCoordinator mStageCoordinator;
+ private Transitions mTransitions;
+ private final TestShellExecutor mMainExecutor = new TestShellExecutor();
+ private final ShellExecutor mAnimExecutor = new TestShellExecutor();
+ private final Handler mMainHandler = new Handler(Looper.getMainLooper());
@Before
@UiThreadTest
public void setup() {
MockitoAnnotations.initMocks(this);
+ mTransitions = createTestTransitions();
mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
@@ -329,7 +337,20 @@
mStageCoordinator.onFoldedStateChanged(true);
- verify(mStageCoordinator).onSplitScreenExit();
- verify(mMainStage).deactivate(any(WindowContainerTransaction.class), eq(false));
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ verify(mTaskOrganizer).startNewTransition(eq(TRANSIT_SPLIT_DISMISS), notNull());
+ } else {
+ verify(mStageCoordinator).onSplitScreenExit();
+ verify(mMainStage).deactivate(any(WindowContainerTransaction.class), eq(false));
+ }
+ }
+
+ private Transitions createTestTransitions() {
+ ShellInit shellInit = new ShellInit(mMainExecutor);
+ final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
+ mTaskOrganizer, mTransactionPool, mock(DisplayController.class), mMainExecutor,
+ mMainHandler, mAnimExecutor);
+ shellInit.init();
+ return t;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
index 44a37f4..d4d2b48 100644
--- a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.Resources;
+import android.hardware.display.DisplayManager;
import android.os.AsyncTask;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -32,6 +33,9 @@
import com.android.settingslib.R;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Predicate;
/**
* Utility methods for working with display density.
@@ -70,120 +74,169 @@
*/
private static final int MIN_DIMENSION_DP = 320;
- private final String[] mEntries;
- private final int[] mValues;
+ private static final Predicate<DisplayInfo> INTERNAL_ONLY =
+ (info) -> info.type == Display.TYPE_INTERNAL;
- private final int mDefaultDensity;
- private final int mCurrentIndex;
+ private final Predicate<DisplayInfo> mPredicate;
+
+ private final DisplayManager mDisplayManager;
+
+ /**
+ * The text description of the density values of the default display.
+ */
+ private String[] mDefaultDisplayDensityEntries;
+
+ /**
+ * The density values of the default display.
+ */
+ private int[] mDefaultDisplayDensityValues;
+
+ /**
+ * The density values, indexed by display unique ID.
+ */
+ private final Map<String, int[]> mValuesPerDisplay = new HashMap();
+
+ private int mDefaultDensityForDefaultDisplay;
+ private int mCurrentIndex = -1;
public DisplayDensityUtils(Context context) {
- final int defaultDensity = DisplayDensityUtils.getDefaultDisplayDensity(
- Display.DEFAULT_DISPLAY);
- if (defaultDensity <= 0) {
- mEntries = null;
- mValues = null;
- mDefaultDensity = 0;
- mCurrentIndex = -1;
- return;
- }
+ this(context, INTERNAL_ONLY);
+ }
- final Resources res = context.getResources();
- DisplayInfo info = new DisplayInfo();
- context.getDisplayNoVerify().getDisplayInfo(info);
+ /**
+ * Creates an instance that stores the density values for the displays that satisfy
+ * the predicate.
+ * @param context The context
+ * @param predicate Determines what displays the density should be set for. The default display
+ * must satisfy this predicate.
+ */
+ public DisplayDensityUtils(Context context, Predicate predicate) {
+ mPredicate = predicate;
+ mDisplayManager = context.getSystemService(DisplayManager.class);
- final int currentDensity = info.logicalDensityDpi;
- int currentDensityIndex = -1;
-
- // Compute number of "larger" and "smaller" scales for this display.
- final int minDimensionPx = Math.min(info.logicalWidth, info.logicalHeight);
- final int maxDensity = DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP;
- final float maxScaleDimen = context.getResources().getFraction(
- R.fraction.display_density_max_scale, 1, 1);
- final float maxScale = Math.min(maxScaleDimen, maxDensity / (float) defaultDensity);
- final float minScale = context.getResources().getFraction(
- R.fraction.display_density_min_scale, 1, 1);
- final float minScaleInterval = context.getResources().getFraction(
- R.fraction.display_density_min_scale_interval, 1, 1);
- final int numLarger = (int) MathUtils.constrain((maxScale - 1) / minScaleInterval,
- 0, SUMMARIES_LARGER.length);
- final int numSmaller = (int) MathUtils.constrain((1 - minScale) / minScaleInterval,
- 0, SUMMARIES_SMALLER.length);
-
- String[] entries = new String[1 + numSmaller + numLarger];
- int[] values = new int[entries.length];
- int curIndex = 0;
-
- if (numSmaller > 0) {
- final float interval = (1 - minScale) / numSmaller;
- for (int i = numSmaller - 1; i >= 0; i--) {
- // Round down to a multiple of 2 by truncating the low bit.
- final int density = ((int) (defaultDensity * (1 - (i + 1) * interval))) & ~1;
- if (currentDensity == density) {
- currentDensityIndex = curIndex;
- }
- entries[curIndex] = res.getString(SUMMARIES_SMALLER[i]);
- values[curIndex] = density;
- curIndex++;
+ for (Display display : mDisplayManager.getDisplays(
+ DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)) {
+ DisplayInfo info = new DisplayInfo();
+ if (!display.getDisplayInfo(info)) {
+ Log.w(LOG_TAG, "Cannot fetch display info for display " + display.getDisplayId());
+ continue;
}
- }
-
- if (currentDensity == defaultDensity) {
- currentDensityIndex = curIndex;
- }
- values[curIndex] = defaultDensity;
- entries[curIndex] = res.getString(SUMMARY_DEFAULT);
- curIndex++;
-
- if (numLarger > 0) {
- final float interval = (maxScale - 1) / numLarger;
- for (int i = 0; i < numLarger; i++) {
- // Round down to a multiple of 2 by truncating the low bit.
- final int density = ((int) (defaultDensity * (1 + (i + 1) * interval))) & ~1;
- if (currentDensity == density) {
- currentDensityIndex = curIndex;
+ if (!mPredicate.test(info)) {
+ if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
+ throw new IllegalArgumentException("Predicate must not filter out the default "
+ + "display.");
}
- values[curIndex] = density;
- entries[curIndex] = res.getString(SUMMARIES_LARGER[i]);
- curIndex++;
+ continue;
}
+
+ final int defaultDensity = DisplayDensityUtils.getDefaultDensityForDisplay(
+ display.getDisplayId());
+ if (defaultDensity <= 0) {
+ Log.w(LOG_TAG, "Cannot fetch default density for display "
+ + display.getDisplayId());
+ continue;
+ }
+
+ final Resources res = context.getResources();
+
+ final int currentDensity = info.logicalDensityDpi;
+ int currentDensityIndex = -1;
+
+ // Compute number of "larger" and "smaller" scales for this display.
+ final int minDimensionPx = Math.min(info.logicalWidth, info.logicalHeight);
+ final int maxDensity =
+ DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP;
+ final float maxScaleDimen = context.getResources().getFraction(
+ R.fraction.display_density_max_scale, 1, 1);
+ final float maxScale = Math.min(maxScaleDimen, maxDensity / (float) defaultDensity);
+ final float minScale = context.getResources().getFraction(
+ R.fraction.display_density_min_scale, 1, 1);
+ final float minScaleInterval = context.getResources().getFraction(
+ R.fraction.display_density_min_scale_interval, 1, 1);
+ final int numLarger = (int) MathUtils.constrain((maxScale - 1) / minScaleInterval,
+ 0, SUMMARIES_LARGER.length);
+ final int numSmaller = (int) MathUtils.constrain((1 - minScale) / minScaleInterval,
+ 0, SUMMARIES_SMALLER.length);
+
+ String[] entries = new String[1 + numSmaller + numLarger];
+ int[] values = new int[entries.length];
+ int curIndex = 0;
+
+ if (numSmaller > 0) {
+ final float interval = (1 - minScale) / numSmaller;
+ for (int i = numSmaller - 1; i >= 0; i--) {
+ // Round down to a multiple of 2 by truncating the low bit.
+ final int density = ((int) (defaultDensity * (1 - (i + 1) * interval))) & ~1;
+ if (currentDensity == density) {
+ currentDensityIndex = curIndex;
+ }
+ entries[curIndex] = res.getString(SUMMARIES_SMALLER[i]);
+ values[curIndex] = density;
+ curIndex++;
+ }
+ }
+
+ if (currentDensity == defaultDensity) {
+ currentDensityIndex = curIndex;
+ }
+ values[curIndex] = defaultDensity;
+ entries[curIndex] = res.getString(SUMMARY_DEFAULT);
+ curIndex++;
+
+ if (numLarger > 0) {
+ final float interval = (maxScale - 1) / numLarger;
+ for (int i = 0; i < numLarger; i++) {
+ // Round down to a multiple of 2 by truncating the low bit.
+ final int density = ((int) (defaultDensity * (1 + (i + 1) * interval))) & ~1;
+ if (currentDensity == density) {
+ currentDensityIndex = curIndex;
+ }
+ values[curIndex] = density;
+ entries[curIndex] = res.getString(SUMMARIES_LARGER[i]);
+ curIndex++;
+ }
+ }
+
+ final int displayIndex;
+ if (currentDensityIndex >= 0) {
+ displayIndex = currentDensityIndex;
+ } else {
+ // We don't understand the current density. Must have been set by
+ // someone else. Make room for another entry...
+ int newLength = values.length + 1;
+ values = Arrays.copyOf(values, newLength);
+ values[curIndex] = currentDensity;
+
+ entries = Arrays.copyOf(entries, newLength);
+ entries[curIndex] = res.getString(SUMMARY_CUSTOM, currentDensity);
+
+ displayIndex = curIndex;
+ }
+
+ if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
+ mDefaultDensityForDefaultDisplay = defaultDensity;
+ mCurrentIndex = displayIndex;
+ mDefaultDisplayDensityEntries = entries;
+ mDefaultDisplayDensityValues = values;
+ }
+ mValuesPerDisplay.put(info.uniqueId, values);
}
-
- final int displayIndex;
- if (currentDensityIndex >= 0) {
- displayIndex = currentDensityIndex;
- } else {
- // We don't understand the current density. Must have been set by
- // someone else. Make room for another entry...
- int newLength = values.length + 1;
- values = Arrays.copyOf(values, newLength);
- values[curIndex] = currentDensity;
-
- entries = Arrays.copyOf(entries, newLength);
- entries[curIndex] = res.getString(SUMMARY_CUSTOM, currentDensity);
-
- displayIndex = curIndex;
- }
-
- mDefaultDensity = defaultDensity;
- mCurrentIndex = displayIndex;
- mEntries = entries;
- mValues = values;
}
- public String[] getEntries() {
- return mEntries;
+ public String[] getDefaultDisplayDensityEntries() {
+ return mDefaultDisplayDensityEntries;
}
- public int[] getValues() {
- return mValues;
+ public int[] getDefaultDisplayDensityValues() {
+ return mDefaultDisplayDensityValues;
}
- public int getCurrentIndex() {
+ public int getCurrentIndexForDefaultDisplay() {
return mCurrentIndex;
}
- public int getDefaultDensity() {
- return mDefaultDensity;
+ public int getDefaultDensityForDefaultDisplay() {
+ return mDefaultDensityForDefaultDisplay;
}
/**
@@ -193,7 +246,7 @@
* @return the default density of the specified display, or {@code -1} if
* the display does not exist or the density could not be obtained
*/
- private static int getDefaultDisplayDensity(int displayId) {
+ private static int getDefaultDensityForDisplay(int displayId) {
try {
final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
return wm.getInitialDisplayDensity(displayId);
@@ -203,19 +256,31 @@
}
/**
- * Asynchronously applies display density changes to the specified display.
+ * Asynchronously applies display density changes to the displays that satisfy the predicate.
* <p>
* The change will be applied to the user specified by the value of
* {@link UserHandle#myUserId()} at the time the method is called.
- *
- * @param displayId the identifier of the display to modify
*/
- public static void clearForcedDisplayDensity(final int displayId) {
+ public void clearForcedDisplayDensity() {
final int userId = UserHandle.myUserId();
AsyncTask.execute(() -> {
try {
- final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
- wm.clearForcedDisplayDensityForUser(displayId, userId);
+ for (Display display : mDisplayManager.getDisplays(
+ DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)) {
+ int displayId = display.getDisplayId();
+ DisplayInfo info = new DisplayInfo();
+ if (!display.getDisplayInfo(info)) {
+ Log.w(LOG_TAG, "Unable to clear forced display density setting "
+ + "for display " + displayId);
+ continue;
+ }
+ if (!mPredicate.test(info)) {
+ continue;
+ }
+
+ final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+ wm.clearForcedDisplayDensityForUser(displayId, userId);
+ }
} catch (RemoteException exc) {
Log.w(LOG_TAG, "Unable to clear forced display density setting");
}
@@ -223,20 +288,39 @@
}
/**
- * Asynchronously applies display density changes to the specified display.
+ * Asynchronously applies display density changes to the displays that satisfy the predicate.
* <p>
* The change will be applied to the user specified by the value of
* {@link UserHandle#myUserId()} at the time the method is called.
*
- * @param displayId the identifier of the display to modify
- * @param density the density to force for the specified display
+ * @param index The index of the density value
*/
- public static void setForcedDisplayDensity(final int displayId, final int density) {
+ public void setForcedDisplayDensity(final int index) {
final int userId = UserHandle.myUserId();
AsyncTask.execute(() -> {
try {
- final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
- wm.setForcedDisplayDensityForUser(displayId, density, userId);
+ for (Display display : mDisplayManager.getDisplays(
+ DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)) {
+ int displayId = display.getDisplayId();
+ DisplayInfo info = new DisplayInfo();
+ if (!display.getDisplayInfo(info)) {
+ Log.w(LOG_TAG, "Unable to save forced display density setting "
+ + "for display " + displayId);
+ continue;
+ }
+ if (!mPredicate.test(info)) {
+ continue;
+ }
+ if (!mValuesPerDisplay.containsKey(info.uniqueId)) {
+ Log.w(LOG_TAG, "Unable to save forced display density setting "
+ + "for display " + info.uniqueId);
+ continue;
+ }
+
+ final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+ wm.setForcedDisplayDensityForUser(displayId,
+ mValuesPerDisplay.get(info.uniqueId)[index], userId);
+ }
} catch (RemoteException exc) {
Log.w(LOG_TAG, "Unable to save forced display density setting");
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index c685d62..b6e006f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -921,7 +921,7 @@
android:excludeFromRecents="true"
android:theme="@android:style/Theme.NoDisplay"
android:label="@string/note_task_button_label"
- android:icon="@drawable/ic_note_task_button">
+ android:icon="@drawable/ic_note_task_shortcut_widget">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
diff --git a/packages/SystemUI/README.md b/packages/SystemUI/README.md
index ee8d023..2910bba 100644
--- a/packages/SystemUI/README.md
+++ b/packages/SystemUI/README.md
@@ -5,46 +5,72 @@
SystemUI is a persistent process that provides UI for the system but outside
of the system_server process.
-The starting point for most of sysui code is a list of services that extend
-SystemUI that are started up by SystemUIApplication. These services then depend
-on some custom dependency injection provided by Dependency.
-
Inputs directed at sysui (as opposed to general listeners) generally come in
through IStatusBar. Outputs from sysui are through a variety of private APIs to
the android platform all over.
## SystemUIApplication
-When SystemUIApplication starts up, it will start up the services listed in
-config_systemUIServiceComponents or config_systemUIServiceComponentsPerUser.
+When SystemUIApplication starts up, it instantiates a Dagger graph from which
+various pieces of the application are built.
-Each of these services extend SystemUI. SystemUI provides them with a Context
-and gives them callbacks for onConfigurationChanged (this historically was
-the main path for onConfigurationChanged, now also happens through
-ConfigurationController). They also receive a callback for onBootCompleted
+To support customization, SystemUIApplication relies on the AndroidManifest.xml
+having an `android.app.AppComponentFactory` specified. Specifically, it relies
+on an `AppComponentFactory` that subclases `SystemUIAppComponentFactoryBase`.
+Implementations of this abstract base class must override
+`#createSystemUIInitializer(Context)` which returns a `SystemUIInitializer`.
+`SystemUIInitializer` primary job in turn is to intialize and return the Dagger
+root component back to the `SystemUIApplication`.
+
+Writing a custom `SystemUIAppComponentFactoryBase` and `SystemUIInitializer`,
+should be enough for most implementations to stand up a customized Dagger
+graph, and launch a custom version of SystemUI.
+
+## Dagger / Dependency Injection
+
+See [dagger.md](docs/dagger.md) and https://dagger.dev/.
+
+## CoreStartable
+
+The starting point for most of SystemUI code is a list of classes that
+implement `CoreStartable` that are started up by SystemUIApplication.
+CoreStartables are like miniature services. They have their `#start` method
+called after being instantiated, and a reference to them is stored inside
+SystemUIApplication. They are in charge of their own behavior beyond this,
+registering and unregistering with the rest of the system as needed.
+
+`CoreStartable` also receives a callback for `#onBootCompleted`
since these objects may be started before the device has finished booting.
-Each SystemUI service is expected to be a major part of system ui and the
-goal is to minimize communication between them. So in general they should be
-relatively silo'd.
+`CoreStartable` is an ideal place to add new features and functionality
+that does not belong directly under the umbrella of an existing feature.
+It is better to define a new `CoreStartable` than to stick unrelated
+initialization code together in catch-all methods.
-## Dependencies
+CoreStartables are tied to application startup via Dagger:
-The first SystemUI service that is started should always be Dependency.
-Dependency provides a static method for getting a hold of dependencies that
-have a lifecycle that spans sysui. Dependency has code for how to create all
-dependencies manually added. SystemUIFactory is also capable of
-adding/replacing these dependencies.
+```kotlin
+class FeatureStartable
+@Inject
+constructor(
+ /* ... */
+) : CoreStartable {
+ override fun start() {
+ // ...
+ }
+}
-Dependencies are lazily initialized, so if a Dependency is never referenced at
-runtime, it will never be created.
+@Module
+abstract class FeatureModule {
+ @Binds
+ @IntoMap
+ @ClassKey(FeatureStartable::class)
+ abstract fun bind(impl: FeatureStartable): CoreStartable
+}
+```
-If an instantiated dependency implements Dumpable it will be included in dumps
-of sysui (and bug reports), allowing it to include current state information.
-This is how \*Controllers dump state to bug reports.
-
-If an instantiated dependency implements ConfigurationChangeReceiver it will
-receive onConfigurationChange callbacks when the configuration changes.
+Including `FeatureModule` in the Dagger graph such as this will ensure that
+`FeatureStartable` gets constructed and that its `#start` method is called.
## IStatusBar
@@ -64,12 +90,6 @@
This is generally used a shortcut to directly trigger CommandQueue rather than
calling StatusManager and waiting for the call to come back to IStatusBar.
-## Default SystemUI services list
-
-### [com.android.systemui.Dependency](/packages/SystemUI/src/com/android/systemui/Dependency.java)
-
-Provides custom dependency injection.
-
### [com.android.systemui.util.NotificationChannels](/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java)
Creates/initializes the channels sysui uses when posting notifications.
@@ -88,11 +108,11 @@
Registers all the callbacks/listeners required to show the Volume dialog when
it should be shown.
-### [com.android.systemui.status.phone.StatusBar](/packages/SystemUI/src/com/android/systemui/status/phone/StatusBar.java)
+### [com.android.systemui.status.phone.CentralSurfaces](/packages/SystemUI/src/com/android/systemui/status/phone/CentralSurfaces.java)
This shows the UI for the status bar and the notification shade it contains.
It also contains a significant amount of other UI that interacts with these
-surfaces (keyguard, AOD, etc.). StatusBar also contains a notification listener
+surfaces (keyguard, AOD, etc.). CentralSurfaces also contains a notification listener
to receive notification callbacks.
### [com.android.systemui.usb.StorageNotification](/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java)
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index cd45b8ea..60bfdb2 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -16,6 +16,9 @@
},
{
"exclude-annotation": "android.platform.test.annotations.Postsubmit"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
}
]
}
@@ -72,16 +75,16 @@
// Curious where your @Scenario tests will run?
//
// @Ignore: nowhere
- // @Staging or @FlakyTest: in staged-postsubmit, but not postsubmit or
- // presubmit
+ // @FlakyTest: in staged-postsubmit, but not blocking postsubmit or
+ // presubmit
// @Postsubmit: in postsubmit and staged-postsubmit, but not presubmit
// none of the above: in presubmit, postsubmit, and staged-postsubmit
//
- // Therefore, please annotate new tests with @Staging, then with @Postsubmit
- // once they're ready for postsubmit, then with neither once they're ready
- // for presubmit.
+ // Ideally, please annotate new tests with @FlakyTest, then with @Postsubmit
+ // once they're ready for postsubmit as they will immediately block go/android-platinum,
+ // then with neither once they're ready for presubmit.
//
- // If you don't use @Staging or @Postsubmit, your new test will immediately
+ // If you don't use @Postsubmit, your new test will immediately
// block presubmit, which is probably not what you want!
"sysui-platinum-postsubmit": [
{
@@ -98,6 +101,9 @@
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
}
]
}
@@ -130,6 +136,9 @@
},
{
"include-filter": "android.platform.test.scenario.sysui"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
}
]
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 8f70dcc..c729b09 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -301,10 +301,13 @@
interface Callback {
/** Whether we are currently on the keyguard or not. */
- fun isOnKeyguard(): Boolean
+ @JvmDefault fun isOnKeyguard(): Boolean = false
/** Hide the keyguard and animate using [runner]. */
- fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner)
+ @JvmDefault
+ fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner) {
+ throw UnsupportedOperationException()
+ }
/* Get the background color of [task]. */
fun getBackgroundColor(task: TaskInfo): Int
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
index 3d341af..2903288 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
@@ -33,9 +33,7 @@
private const val FONT_ITALIC_ANIMATION_STEP = 0.1f
private const val FONT_ITALIC_DEFAULT_VALUE = 0f
-/**
- * Provide interpolation of two fonts by adjusting font variation settings.
- */
+/** Provide interpolation of two fonts by adjusting font variation settings. */
class FontInterpolator {
/**
@@ -61,11 +59,14 @@
var index: Int,
val sortedAxes: MutableList<FontVariationAxis>
) {
- constructor(font: Font, axes: List<FontVariationAxis>) :
- this(font.sourceIdentifier,
- font.ttcIndex,
- axes.toMutableList().apply { sortBy { it.tag } }
- )
+ constructor(
+ font: Font,
+ axes: List<FontVariationAxis>
+ ) : this(
+ font.sourceIdentifier,
+ font.ttcIndex,
+ axes.toMutableList().apply { sortBy { it.tag } }
+ )
fun set(font: Font, axes: List<FontVariationAxis>) {
sourceId = font.sourceIdentifier
@@ -86,9 +87,7 @@
private val tmpInterpKey = InterpKey(null, null, 0f)
private val tmpVarFontKey = VarFontKey(0, 0, mutableListOf())
- /**
- * Linear interpolate the font variation settings.
- */
+ /** Linear interpolate the font variation settings. */
fun lerp(start: Font, end: Font, progress: Float): Font {
if (progress == 0f) {
return start
@@ -115,27 +114,34 @@
// this doesn't take much time since the variation axes is usually up to 5. If we need to
// support more number of axes, we may want to preprocess the font and store the sorted axes
// and also pre-fill the missing axes value with default value from 'fvar' table.
- val newAxes = lerp(startAxes, endAxes) { tag, startValue, endValue ->
- when (tag) {
- // TODO: Good to parse 'fvar' table for retrieving default value.
- TAG_WGHT -> adjustWeight(
- MathUtils.lerp(
+ val newAxes =
+ lerp(startAxes, endAxes) { tag, startValue, endValue ->
+ when (tag) {
+ // TODO: Good to parse 'fvar' table for retrieving default value.
+ TAG_WGHT ->
+ adjustWeight(
+ MathUtils.lerp(
startValue ?: FONT_WEIGHT_DEFAULT_VALUE,
endValue ?: FONT_WEIGHT_DEFAULT_VALUE,
- progress))
- TAG_ITAL -> adjustItalic(
- MathUtils.lerp(
+ progress
+ )
+ )
+ TAG_ITAL ->
+ adjustItalic(
+ MathUtils.lerp(
startValue ?: FONT_ITALIC_DEFAULT_VALUE,
endValue ?: FONT_ITALIC_DEFAULT_VALUE,
- progress))
- else -> {
- require(startValue != null && endValue != null) {
- "Unable to interpolate due to unknown default axes value : $tag"
+ progress
+ )
+ )
+ else -> {
+ require(startValue != null && endValue != null) {
+ "Unable to interpolate due to unknown default axes value : $tag"
+ }
+ MathUtils.lerp(startValue, endValue, progress)
}
- MathUtils.lerp(startValue, endValue, progress)
}
}
- }
// Check if we already make font for this axes. This is typically happens if the animation
// happens backward.
@@ -149,9 +155,7 @@
// This is the first time to make the font for the axes. Build and store it to the cache.
// Font.Builder#build won't throw IOException since creating fonts from existing fonts will
// not do any IO work.
- val newFont = Font.Builder(start)
- .setFontVariationSettings(newAxes.toTypedArray())
- .build()
+ val newFont = Font.Builder(start).setFontVariationSettings(newAxes.toTypedArray()).build()
interpCache[InterpKey(start, end, progress)] = newFont
verFontCache[VarFontKey(start, newAxes)] = newFont
return newFont
@@ -173,26 +177,28 @@
val tagA = if (i < start.size) start[i].tag else null
val tagB = if (j < end.size) end[j].tag else null
- val comp = when {
- tagA == null -> 1
- tagB == null -> -1
- else -> tagA.compareTo(tagB)
- }
+ val comp =
+ when {
+ tagA == null -> 1
+ tagB == null -> -1
+ else -> tagA.compareTo(tagB)
+ }
- val axis = when {
- comp == 0 -> {
- val v = filter(tagA!!, start[i++].styleValue, end[j++].styleValue)
- FontVariationAxis(tagA, v)
+ val axis =
+ when {
+ comp == 0 -> {
+ val v = filter(tagA!!, start[i++].styleValue, end[j++].styleValue)
+ FontVariationAxis(tagA, v)
+ }
+ comp < 0 -> {
+ val v = filter(tagA!!, start[i++].styleValue, null)
+ FontVariationAxis(tagA, v)
+ }
+ else -> { // comp > 0
+ val v = filter(tagB!!, null, end[j++].styleValue)
+ FontVariationAxis(tagB, v)
+ }
}
- comp < 0 -> {
- val v = filter(tagA!!, start[i++].styleValue, null)
- FontVariationAxis(tagA, v)
- }
- else -> { // comp > 0
- val v = filter(tagB!!, null, end[j++].styleValue)
- FontVariationAxis(tagB, v)
- }
- }
result.add(axis)
}
@@ -202,21 +208,21 @@
// For the performance reasons, we animate weight with FONT_WEIGHT_ANIMATION_STEP. This helps
// Cache hit ratio in the Skia glyph cache.
private fun adjustWeight(value: Float) =
- coerceInWithStep(value, FONT_WEIGHT_MIN, FONT_WEIGHT_MAX, FONT_WEIGHT_ANIMATION_STEP)
+ coerceInWithStep(value, FONT_WEIGHT_MIN, FONT_WEIGHT_MAX, FONT_WEIGHT_ANIMATION_STEP)
// For the performance reasons, we animate italic with FONT_ITALIC_ANIMATION_STEP. This helps
// Cache hit ratio in the Skia glyph cache.
private fun adjustItalic(value: Float) =
- coerceInWithStep(value, FONT_ITALIC_MIN, FONT_ITALIC_MAX, FONT_ITALIC_ANIMATION_STEP)
+ coerceInWithStep(value, FONT_ITALIC_MIN, FONT_ITALIC_MAX, FONT_ITALIC_ANIMATION_STEP)
private fun coerceInWithStep(v: Float, min: Float, max: Float, step: Float) =
- (v.coerceIn(min, max) / step).toInt() * step
+ (v.coerceIn(min, max) / step).toInt() * step
companion object {
private val EMPTY_AXES = arrayOf<FontVariationAxis>()
// Returns true if given two font instance can be interpolated.
fun canInterpolate(start: Font, end: Font) =
- start.ttcIndex == end.ttcIndex && start.sourceIdentifier == end.sourceIdentifier
+ start.ttcIndex == end.ttcIndex && start.sourceIdentifier == end.sourceIdentifier
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index dfac02d..26aa0e8 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -26,6 +26,7 @@
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.InsetDrawable
import android.graphics.drawable.LayerDrawable
+import android.graphics.drawable.StateListDrawable
import android.util.Log
import android.view.GhostView
import android.view.View
@@ -35,6 +36,7 @@
import com.android.internal.jank.InteractionJankMonitor
import java.util.LinkedList
import kotlin.math.min
+import kotlin.math.roundToInt
private const val TAG = "GhostedViewLaunchAnimatorController"
@@ -49,7 +51,9 @@
* Note: Avoid instantiating this directly and call [ActivityLaunchAnimator.Controller.fromView]
* whenever possible instead.
*/
-open class GhostedViewLaunchAnimatorController @JvmOverloads constructor(
+open class GhostedViewLaunchAnimatorController
+@JvmOverloads
+constructor(
/** The view that will be ghosted and from which the background will be extracted. */
private val ghostedView: View,
@@ -145,7 +149,8 @@
val gradient = findGradientDrawable(drawable) ?: return 0f
// TODO(b/184121838): Support more than symmetric top & bottom radius.
- return gradient.cornerRadii?.get(CORNER_RADIUS_TOP_INDEX) ?: gradient.cornerRadius
+ val radius = gradient.cornerRadii?.get(CORNER_RADIUS_TOP_INDEX) ?: gradient.cornerRadius
+ return radius * ghostedView.scaleX
}
/** Return the current bottom corner radius of the background. */
@@ -154,7 +159,8 @@
val gradient = findGradientDrawable(drawable) ?: return 0f
// TODO(b/184121838): Support more than symmetric top & bottom radius.
- return gradient.cornerRadii?.get(CORNER_RADIUS_BOTTOM_INDEX) ?: gradient.cornerRadius
+ val radius = gradient.cornerRadii?.get(CORNER_RADIUS_BOTTOM_INDEX) ?: gradient.cornerRadius
+ return radius * ghostedView.scaleX
}
override fun createAnimatorState(): LaunchAnimator.State {
@@ -173,9 +179,13 @@
ghostedView.getLocationOnScreen(ghostedViewLocation)
val insets = backgroundInsets
state.top = ghostedViewLocation[1] + insets.top
- state.bottom = ghostedViewLocation[1] + ghostedView.height - insets.bottom
+ state.bottom =
+ ghostedViewLocation[1] + (ghostedView.height * ghostedView.scaleY).roundToInt() -
+ insets.bottom
state.left = ghostedViewLocation[0] + insets.left
- state.right = ghostedViewLocation[0] + ghostedView.width - insets.right
+ state.right =
+ ghostedViewLocation[0] + (ghostedView.width * ghostedView.scaleX).roundToInt() -
+ insets.right
}
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
@@ -341,6 +351,10 @@
}
}
+ if (drawable is StateListDrawable) {
+ return findGradientDrawable(drawable.current)
+ }
+
return null
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index 5f1bb83..fdab749 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -36,8 +36,8 @@
* Currently this class can provide text style animation for text weight and text size. For example
* the simple view that draws text with animating text size is like as follows:
*
- * <pre>
- * <code>
+ * <pre> <code>
+ * ```
* class SimpleTextAnimation : View {
* @JvmOverloads constructor(...)
*
@@ -53,83 +53,63 @@
* animator.setTextStyle(-1 /* unchanged weight */, sizePx, animate)
* }
* }
- * </code>
- * </pre>
+ * ```
+ * </code> </pre>
*/
-class TextAnimator(
- layout: Layout,
- private val invalidateCallback: () -> Unit
-) {
+class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
// Following two members are for mutable for testing purposes.
public var textInterpolator: TextInterpolator = TextInterpolator(layout)
- public var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply {
- duration = DEFAULT_ANIMATION_DURATION
- addUpdateListener {
- textInterpolator.progress = it.animatedValue as Float
- invalidateCallback()
- }
- addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- textInterpolator.rebase()
+ public var animator: ValueAnimator =
+ ValueAnimator.ofFloat(1f).apply {
+ duration = DEFAULT_ANIMATION_DURATION
+ addUpdateListener {
+ textInterpolator.progress = it.animatedValue as Float
+ invalidateCallback()
}
- override fun onAnimationCancel(animation: Animator?) = textInterpolator.rebase()
- })
- }
+ addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ textInterpolator.rebase()
+ }
+ override fun onAnimationCancel(animation: Animator?) = textInterpolator.rebase()
+ }
+ )
+ }
sealed class PositionedGlyph {
- /**
- * Mutable X coordinate of the glyph position relative from drawing offset.
- */
+ /** Mutable X coordinate of the glyph position relative from drawing offset. */
var x: Float = 0f
- /**
- * Mutable Y coordinate of the glyph position relative from the baseline.
- */
+ /** Mutable Y coordinate of the glyph position relative from the baseline. */
var y: Float = 0f
- /**
- * The current line of text being drawn, in a multi-line TextView.
- */
+ /** The current line of text being drawn, in a multi-line TextView. */
var lineNo: Int = 0
- /**
- * Mutable text size of the glyph in pixels.
- */
+ /** Mutable text size of the glyph in pixels. */
var textSize: Float = 0f
- /**
- * Mutable color of the glyph.
- */
+ /** Mutable color of the glyph. */
var color: Int = 0
- /**
- * Immutable character offset in the text that the current font run start.
- */
+ /** Immutable character offset in the text that the current font run start. */
abstract var runStart: Int
protected set
- /**
- * Immutable run length of the font run.
- */
+ /** Immutable run length of the font run. */
abstract var runLength: Int
protected set
- /**
- * Immutable glyph index of the font run.
- */
+ /** Immutable glyph index of the font run. */
abstract var glyphIndex: Int
protected set
- /**
- * Immutable font instance for this font run.
- */
+ /** Immutable font instance for this font run. */
abstract var font: Font
protected set
- /**
- * Immutable glyph ID for this glyph.
- */
+ /** Immutable glyph ID for this glyph. */
abstract var glyphId: Int
protected set
}
@@ -147,20 +127,18 @@
/**
* GlyphFilter applied just before drawing to canvas for tweaking positions and text size.
*
- * This callback is called for each glyphs just before drawing the glyphs. This function will
- * be called with the intrinsic position, size, color, glyph ID and font instance. You can
- * mutate the position, size and color for tweaking animations.
- * Do not keep the reference of passed glyph object. The interpolator reuses that object for
- * avoiding object allocations.
+ * This callback is called for each glyphs just before drawing the glyphs. This function will be
+ * called with the intrinsic position, size, color, glyph ID and font instance. You can mutate
+ * the position, size and color for tweaking animations. Do not keep the reference of passed
+ * glyph object. The interpolator reuses that object for avoiding object allocations.
*
- * Details:
- * The text is drawn with font run units. The font run is a text segment that draws with the
- * same font. The {@code runStart} and {@code runLimit} is a range of the font run in the text
- * that current glyph is in. Once the font run is determined, the system will convert characters
- * into glyph IDs. The {@code glyphId} is the glyph identifier in the font and
- * {@code glyphIndex} is the offset of the converted glyph array. Please note that the
- * {@code glyphIndex} is not a character index, because the character will not be converted to
- * glyph one-by-one. If there are ligatures including emoji sequence, etc, the glyph ID may be
+ * Details: The text is drawn with font run units. The font run is a text segment that draws
+ * with the same font. The {@code runStart} and {@code runLimit} is a range of the font run in
+ * the text that current glyph is in. Once the font run is determined, the system will convert
+ * characters into glyph IDs. The {@code glyphId} is the glyph identifier in the font and {@code
+ * glyphIndex} is the offset of the converted glyph array. Please note that the {@code
+ * glyphIndex} is not a character index, because the character will not be converted to glyph
+ * one-by-one. If there are ligatures including emoji sequence, etc, the glyph ID may be
* composed from multiple characters.
*
* Here is an example of font runs: "fin. 終わり"
@@ -193,7 +171,9 @@
*/
var glyphFilter: GlyphCallback?
get() = textInterpolator.glyphFilter
- set(value) { textInterpolator.glyphFilter = value }
+ set(value) {
+ textInterpolator.glyphFilter = value
+ }
fun draw(c: Canvas) = textInterpolator.draw(c)
@@ -208,7 +188,7 @@
* @param weight an optional text weight.
* @param textSize an optional font size.
* @param colors an optional colors array that must be the same size as numLines passed to
- * the TextInterpolator
+ * the TextInterpolator
* @param animate an optional boolean indicating true for showing style transition as animation,
* false for immediate style transition. True by default.
* @param duration an optional animation duration in milliseconds. This is ignored if animate is
@@ -237,10 +217,11 @@
if (weight >= 0) {
// Paint#setFontVariationSettings creates Typeface instance from scratch. To reduce the
// memory impact, cache the typeface result.
- textInterpolator.targetPaint.typeface = typefaceCache.getOrElse(weight) {
- textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
- textInterpolator.targetPaint.typeface
- }
+ textInterpolator.targetPaint.typeface =
+ typefaceCache.getOrElse(weight) {
+ textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
+ textInterpolator.targetPaint.typeface
+ }
}
if (color != null) {
textInterpolator.targetPaint.color = color
@@ -249,22 +230,24 @@
if (animate) {
animator.startDelay = delay
- animator.duration = if (duration == -1L) {
- DEFAULT_ANIMATION_DURATION
- } else {
- duration
- }
+ animator.duration =
+ if (duration == -1L) {
+ DEFAULT_ANIMATION_DURATION
+ } else {
+ duration
+ }
interpolator?.let { animator.interpolator = it }
if (onAnimationEnd != null) {
- val listener = object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- onAnimationEnd.run()
- animator.removeListener(this)
+ val listener =
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ onAnimationEnd.run()
+ animator.removeListener(this)
+ }
+ override fun onAnimationCancel(animation: Animator?) {
+ animator.removeListener(this)
+ }
}
- override fun onAnimationCancel(animation: Animator?) {
- animator.removeListener(this)
- }
- }
animator.addListener(listener)
}
animator.start()
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
index 0448c81..341784e 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
@@ -26,12 +26,8 @@
import com.android.internal.graphics.ColorUtils
import java.lang.Math.max
-/**
- * Provide text style linear interpolation for plain text.
- */
-class TextInterpolator(
- layout: Layout
-) {
+/** Provide text style linear interpolation for plain text. */
+class TextInterpolator(layout: Layout) {
/**
* Returns base paint used for interpolation.
@@ -64,12 +60,11 @@
var baseFont: Font,
var targetFont: Font
) {
- val length: Int get() = end - start
+ val length: Int
+ get() = end - start
}
- /**
- * A class represents text layout of a single run.
- */
+ /** A class represents text layout of a single run. */
private class Run(
val glyphIds: IntArray,
val baseX: FloatArray, // same length as glyphIds
@@ -79,12 +74,8 @@
val fontRuns: List<FontRun>
)
- /**
- * A class represents text layout of a single line.
- */
- private class Line(
- val runs: List<Run>
- )
+ /** A class represents text layout of a single line. */
+ private class Line(val runs: List<Run>)
private var lines = listOf<Line>()
private val fontInterpolator = FontInterpolator()
@@ -106,8 +97,8 @@
/**
* The layout used for drawing text.
*
- * Only non-styled text is supported. Even if the given layout is created from Spanned, the
- * span information is not used.
+ * Only non-styled text is supported. Even if the given layout is created from Spanned, the span
+ * information is not used.
*
* The paint objects used for interpolation are not changed by this method call.
*
@@ -122,6 +113,9 @@
shapeText(value)
}
+ var shapedText: String = ""
+ private set
+
init {
// shapeText needs to be called after all members are initialized.
shapeText(layout)
@@ -130,8 +124,8 @@
/**
* Recalculate internal text layout for interpolation.
*
- * Whenever the target paint is modified, call this method to recalculate internal
- * text layout used for interpolation.
+ * Whenever the target paint is modified, call this method to recalculate internal text layout
+ * used for interpolation.
*/
fun onTargetPaintModified() {
updatePositionsAndFonts(shapeText(layout, targetPaint), updateBase = false)
@@ -140,8 +134,8 @@
/**
* Recalculate internal text layout for interpolation.
*
- * Whenever the base paint is modified, call this method to recalculate internal
- * text layout used for interpolation.
+ * Whenever the base paint is modified, call this method to recalculate internal text layout
+ * used for interpolation.
*/
fun onBasePaintModified() {
updatePositionsAndFonts(shapeText(layout, basePaint), updateBase = true)
@@ -152,11 +146,11 @@
*
* The text interpolator does not calculate all the text position by text shaper due to
* performance reasons. Instead, the text interpolator shape the start and end state and
- * calculate text position of the middle state by linear interpolation. Due to this trick,
- * the text positions of the middle state is likely different from the text shaper result.
- * So, if you want to start animation from the middle state, you will see the glyph jumps due to
- * this trick, i.e. the progress 0.5 of interpolation between weight 400 and 700 is different
- * from text shape result of weight 550.
+ * calculate text position of the middle state by linear interpolation. Due to this trick, the
+ * text positions of the middle state is likely different from the text shaper result. So, if
+ * you want to start animation from the middle state, you will see the glyph jumps due to this
+ * trick, i.e. the progress 0.5 of interpolation between weight 400 and 700 is different from
+ * text shape result of weight 550.
*
* After calling this method, do not call onBasePaintModified() since it reshape the text and
* update the base state. As in above notice, the text shaping result at current progress is
@@ -168,8 +162,8 @@
* animate weight from 200 to 400, then if you want to move back to 200 at the half of the
* animation, it will look like
*
- * <pre>
- * <code>
+ * <pre> <code>
+ * ```
* val interp = TextInterpolator(layout)
*
* // Interpolate between weight 200 to 400.
@@ -199,9 +193,8 @@
* // progress is 0.5
* animator.start()
* }
- * </code>
- * </pre>
- *
+ * ```
+ * </code> </pre>
*/
fun rebase() {
if (progress == 0f) {
@@ -263,69 +256,73 @@
}
var maxRunLength = 0
- lines = baseLayout.zip(targetLayout) { baseLine, targetLine ->
- val runs = baseLine.zip(targetLine) { base, target ->
-
- require(base.glyphCount() == target.glyphCount()) {
- "Inconsistent glyph count at line ${lines.size}"
- }
-
- val glyphCount = base.glyphCount()
-
- // Good to recycle the array if the existing array can hold the new layout result.
- val glyphIds = IntArray(glyphCount) {
- base.getGlyphId(it).also { baseGlyphId ->
- require(baseGlyphId == target.getGlyphId(it)) {
- "Inconsistent glyph ID at $it in line ${lines.size}"
+ lines =
+ baseLayout.zip(targetLayout) { baseLine, targetLine ->
+ val runs =
+ baseLine.zip(targetLine) { base, target ->
+ require(base.glyphCount() == target.glyphCount()) {
+ "Inconsistent glyph count at line ${lines.size}"
}
- }
- }
- val baseX = FloatArray(glyphCount) { base.getGlyphX(it) }
- val baseY = FloatArray(glyphCount) { base.getGlyphY(it) }
- val targetX = FloatArray(glyphCount) { target.getGlyphX(it) }
- val targetY = FloatArray(glyphCount) { target.getGlyphY(it) }
+ val glyphCount = base.glyphCount()
- // Calculate font runs
- val fontRun = mutableListOf<FontRun>()
- if (glyphCount != 0) {
- var start = 0
- var baseFont = base.getFont(start)
- var targetFont = target.getFont(start)
- require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
- "Cannot interpolate font at $start ($baseFont vs $targetFont)"
- }
-
- for (i in 1 until glyphCount) {
- val nextBaseFont = base.getFont(i)
- val nextTargetFont = target.getFont(i)
-
- if (baseFont !== nextBaseFont) {
- require(targetFont !== nextTargetFont) {
- "Base font has changed at $i but target font has not changed."
+ // Good to recycle the array if the existing array can hold the new layout
+ // result.
+ val glyphIds =
+ IntArray(glyphCount) {
+ base.getGlyphId(it).also { baseGlyphId ->
+ require(baseGlyphId == target.getGlyphId(it)) {
+ "Inconsistent glyph ID at $it in line ${lines.size}"
+ }
+ }
}
- // Font transition point. push run and reset context.
- fontRun.add(FontRun(start, i, baseFont, targetFont))
- maxRunLength = max(maxRunLength, i - start)
- baseFont = nextBaseFont
- targetFont = nextTargetFont
- start = i
+
+ val baseX = FloatArray(glyphCount) { base.getGlyphX(it) }
+ val baseY = FloatArray(glyphCount) { base.getGlyphY(it) }
+ val targetX = FloatArray(glyphCount) { target.getGlyphX(it) }
+ val targetY = FloatArray(glyphCount) { target.getGlyphY(it) }
+
+ // Calculate font runs
+ val fontRun = mutableListOf<FontRun>()
+ if (glyphCount != 0) {
+ var start = 0
+ var baseFont = base.getFont(start)
+ var targetFont = target.getFont(start)
require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
"Cannot interpolate font at $start ($baseFont vs $targetFont)"
}
- } else { // baseFont === nextBaseFont
- require(targetFont === nextTargetFont) {
- "Base font has not changed at $i but target font has changed."
+
+ for (i in 1 until glyphCount) {
+ val nextBaseFont = base.getFont(i)
+ val nextTargetFont = target.getFont(i)
+
+ if (baseFont !== nextBaseFont) {
+ require(targetFont !== nextTargetFont) {
+ "Base font has changed at $i but target font is unchanged."
+ }
+ // Font transition point. push run and reset context.
+ fontRun.add(FontRun(start, i, baseFont, targetFont))
+ maxRunLength = max(maxRunLength, i - start)
+ baseFont = nextBaseFont
+ targetFont = nextTargetFont
+ start = i
+ require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
+ "Cannot interpolate font at $start" +
+ " ($baseFont vs $targetFont)"
+ }
+ } else { // baseFont === nextBaseFont
+ require(targetFont === nextTargetFont) {
+ "Base font is unchanged at $i but target font has changed."
+ }
+ }
}
+ fontRun.add(FontRun(start, glyphCount, baseFont, targetFont))
+ maxRunLength = max(maxRunLength, glyphCount - start)
}
+ Run(glyphIds, baseX, baseY, targetX, targetY, fontRun)
}
- fontRun.add(FontRun(start, glyphCount, baseFont, targetFont))
- maxRunLength = max(maxRunLength, glyphCount - start)
- }
- Run(glyphIds, baseX, baseY, targetX, targetY, fontRun)
+ Line(runs)
}
- Line(runs)
- }
// Update float array used for drawing.
if (tmpPositionArray.size < maxRunLength * 2) {
@@ -357,9 +354,9 @@
if (glyphFilter == null) {
for (i in run.start until run.end) {
tmpPositionArray[arrayIndex++] =
- MathUtils.lerp(line.baseX[i], line.targetX[i], progress)
+ MathUtils.lerp(line.baseX[i], line.targetX[i], progress)
tmpPositionArray[arrayIndex++] =
- MathUtils.lerp(line.baseY[i], line.targetY[i], progress)
+ MathUtils.lerp(line.baseY[i], line.targetY[i], progress)
}
c.drawGlyphs(line.glyphIds, run.start, tmpPositionArray, 0, run.length, font, paint)
return
@@ -388,13 +385,14 @@
tmpPaintForGlyph.color = tmpGlyph.color
c.drawGlyphs(
- line.glyphIds,
- prevStart,
- tmpPositionArray,
- 0,
- i - prevStart,
- font,
- tmpPaintForGlyph)
+ line.glyphIds,
+ prevStart,
+ tmpPositionArray,
+ 0,
+ i - prevStart,
+ font,
+ tmpPaintForGlyph
+ )
prevStart = i
arrayIndex = 0
}
@@ -404,13 +402,14 @@
}
c.drawGlyphs(
- line.glyphIds,
- prevStart,
- tmpPositionArray,
- 0,
- run.end - prevStart,
- font,
- tmpPaintForGlyph)
+ line.glyphIds,
+ prevStart,
+ tmpPositionArray,
+ 0,
+ run.end - prevStart,
+ font,
+ tmpPaintForGlyph
+ )
}
private fun updatePositionsAndFonts(
@@ -418,9 +417,7 @@
updateBase: Boolean
) {
// Update target positions with newly calculated text layout.
- check(layoutResult.size == lines.size) {
- "The new layout result has different line count."
- }
+ check(layoutResult.size == lines.size) { "The new layout result has different line count." }
lines.zip(layoutResult) { line, runs ->
line.runs.zip(runs) { lineRun, newGlyphs ->
@@ -436,7 +433,7 @@
}
require(newFont === newGlyphs.getFont(i)) {
"The new layout has different font run." +
- " $newFont vs ${newGlyphs.getFont(i)} at $i"
+ " $newFont vs ${newGlyphs.getFont(i)} at $i"
}
}
@@ -444,7 +441,7 @@
// check new font can be interpolatable with base font.
require(FontInterpolator.canInterpolate(newFont, run.baseFont)) {
"New font cannot be interpolated with existing font. $newFont," +
- " ${run.baseFont}"
+ " ${run.baseFont}"
}
if (updateBase) {
@@ -480,14 +477,13 @@
}
// Shape the text and stores the result to out argument.
- private fun shapeText(
- layout: Layout,
- paint: TextPaint
- ): List<List<PositionedGlyphs>> {
+ private fun shapeText(layout: Layout, paint: TextPaint): List<List<PositionedGlyphs>> {
+ var text = StringBuilder()
val out = mutableListOf<List<PositionedGlyphs>>()
for (lineNo in 0 until layout.lineCount) { // Shape all lines.
val lineStart = layout.getLineStart(lineNo)
- var count = layout.getLineEnd(lineNo) - lineStart
+ val lineEnd = layout.getLineEnd(lineNo)
+ var count = lineEnd - lineStart
// Do not render the last character in the line if it's a newline and unprintable
val last = lineStart + count - 1
if (last > lineStart && last < layout.text.length && layout.text[last] == '\n') {
@@ -495,19 +491,28 @@
}
val runs = mutableListOf<PositionedGlyphs>()
- TextShaper.shapeText(layout.text, lineStart, count, layout.textDirectionHeuristic,
- paint) { _, _, glyphs, _ ->
- runs.add(glyphs)
- }
+ TextShaper.shapeText(
+ layout.text,
+ lineStart,
+ count,
+ layout.textDirectionHeuristic,
+ paint
+ ) { _, _, glyphs, _ -> runs.add(glyphs) }
out.add(runs)
+
+ if (lineNo > 0) {
+ text.append("\n")
+ }
+ text.append(layout.text.substring(lineStart, lineEnd))
}
+ shapedText = text.toString()
return out
}
}
private fun Layout.getDrawOrigin(lineNo: Int) =
- if (getParagraphDirection(lineNo) == Layout.DIR_LEFT_TO_RIGHT) {
- getLineLeft(lineNo)
- } else {
- getLineRight(lineNo)
- }
+ if (getParagraphDirection(lineNo) == Layout.DIR_LEFT_TO_RIGHT) {
+ getLineLeft(lineNo)
+ } else {
+ getLineRight(lineNo)
+ }
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetector.kt
new file mode 100644
index 0000000..1f6e603
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetector.kt
@@ -0,0 +1,150 @@
+/*
+ * 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.internal.systemui.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UFile
+import org.jetbrains.uast.UImportStatement
+
+/**
+ * Detects violations of the Dependency Rule of Clean Architecture.
+ *
+ * The rule states that code in each layer may only depend on code in the same layer or the layer
+ * directly "beneath" that layer in the layer diagram.
+ *
+ * In System UI, we have three layers; from top to bottom, they are: ui, domain, and data. As a
+ * convention, was used packages with those names to place code in the appropriate layer. We also
+ * make an exception and allow for shared models to live under a separate package named "shared" to
+ * avoid code duplication.
+ *
+ * For more information, please see go/sysui-arch.
+ */
+@Suppress("UnstableApiUsage")
+class CleanArchitectureDependencyViolationDetector : Detector(), Detector.UastScanner {
+ override fun getApplicableUastTypes(): List<Class<out UElement>> {
+ return listOf(UFile::class.java)
+ }
+
+ override fun createUastHandler(context: JavaContext): UElementHandler {
+ return object : UElementHandler() {
+ override fun visitFile(node: UFile) {
+ // Check which Clean Architecture layer this file belongs to:
+ matchingLayer(node.packageName)?.let { layer ->
+ // The file matches with a Clean Architecture layer. Let's check all of its
+ // imports.
+ node.imports.forEach { importStatement ->
+ visitImportStatement(context, layer, importStatement)
+ }
+ }
+ }
+ }
+ }
+
+ private fun visitImportStatement(
+ context: JavaContext,
+ layer: Layer,
+ importStatement: UImportStatement,
+ ) {
+ val importText = importStatement.importReference?.asSourceString() ?: return
+ val importedLayer = matchingLayer(importText) ?: return
+
+ // Now check whether the layer of the file may depend on the layer of the import.
+ if (!layer.mayDependOn(importedLayer)) {
+ context.report(
+ issue = ISSUE,
+ scope = importStatement,
+ location = context.getLocation(importStatement),
+ message =
+ "The ${layer.packageNamePart} layer may not depend on" +
+ " the ${importedLayer.packageNamePart} layer.",
+ )
+ }
+ }
+
+ private fun matchingLayer(packageName: String): Layer? {
+ val packageNameParts = packageName.split(".").toSet()
+ return Layer.values()
+ .filter { layer -> packageNameParts.contains(layer.packageNamePart) }
+ .takeIf { it.size == 1 }
+ ?.first()
+ }
+
+ private enum class Layer(
+ val packageNamePart: String,
+ val canDependOn: Set<Layer>,
+ ) {
+ SHARED(
+ packageNamePart = "shared",
+ canDependOn = emptySet(), // The shared layer may not depend on any other layer.
+ ),
+ DATA(
+ packageNamePart = "data",
+ canDependOn = setOf(SHARED),
+ ),
+ DOMAIN(
+ packageNamePart = "domain",
+ canDependOn = setOf(SHARED, DATA),
+ ),
+ UI(
+ packageNamePart = "ui",
+ canDependOn = setOf(DOMAIN, SHARED),
+ ),
+ ;
+
+ fun mayDependOn(otherLayer: Layer): Boolean {
+ return this == otherLayer || canDependOn.contains(otherLayer)
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ val ISSUE =
+ Issue.create(
+ id = "CleanArchitectureDependencyViolation",
+ briefDescription = "Violation of the Clean Architecture Dependency Rule.",
+ explanation =
+ """
+ Following the \"Dependency Rule\" from Clean Architecture, every layer of code \
+ can only depend code in its own layer or code in the layer directly \
+ \"beneath\" it. Therefore, the UI layer can only depend on the" Domain layer \
+ and the Domain layer can only depend on the Data layer. We" do make an \
+ exception to allow shared models to exist and be shared across layers by \
+ placing them under shared/model, which should be done with care. For more \
+ information about Clean Architecture in System UI, please see go/sysui-arch. \
+ NOTE: if your code is not using Clean Architecture, please feel free to ignore \
+ this warning.
+ """,
+ category = Category.CORRECTNESS,
+ priority = 8,
+ severity = Severity.WARNING,
+ implementation =
+ Implementation(
+ CleanArchitectureDependencyViolationDetector::class.java,
+ Scope.JAVA_FILE_SCOPE,
+ ),
+ )
+ }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index 3f334c1c..254a6fb 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -27,9 +27,11 @@
class SystemUIIssueRegistry : IssueRegistry() {
override val issues: List<Issue>
- get() = listOf(
+ get() =
+ listOf(
BindServiceOnMainThreadDetector.ISSUE,
BroadcastSentViaContextDetector.ISSUE,
+ CleanArchitectureDependencyViolationDetector.ISSUE,
SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY,
NonInjectedMainThreadDetector.ISSUE,
@@ -37,7 +39,7 @@
SoftwareBitmapDetector.ISSUE,
NonInjectedServiceDetector.ISSUE,
StaticSettingsProviderDetector.ISSUE
- )
+ )
override val api: Int
get() = CURRENT_API
@@ -45,9 +47,9 @@
get() = 8
override val vendor: Vendor =
- Vendor(
- vendorName = "Android",
- feedbackUrl = "http://b/issues/new?component=78010",
- contact = "jernej@google.com"
- )
+ Vendor(
+ vendorName = "Android",
+ feedbackUrl = "http://b/issues/new?component=78010",
+ contact = "jernej@google.com"
+ )
}
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt
new file mode 100644
index 0000000..a4b59fd
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt
@@ -0,0 +1,296 @@
+/*
+ * 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.internal.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.checks.infrastructure.TestMode
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Ignore
+import org.junit.Test
+
+@Suppress("UnstableApiUsage")
+@Ignore("b/254533331")
+class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTest() {
+ override fun getDetector(): Detector {
+ return CleanArchitectureDependencyViolationDetector()
+ }
+
+ override fun getIssues(): List<Issue> {
+ return listOf(
+ CleanArchitectureDependencyViolationDetector.ISSUE,
+ )
+ }
+
+ @Test
+ fun `No violations`() {
+ lint()
+ .files(
+ *LEGITIMATE_FILES,
+ )
+ .issues(
+ CleanArchitectureDependencyViolationDetector.ISSUE,
+ )
+ .run()
+ .expectWarningCount(0)
+ }
+
+ @Test
+ fun `Violation - domain depends on ui`() {
+ lint()
+ .files(
+ *LEGITIMATE_FILES,
+ TestFiles.kotlin(
+ """
+ package test.domain.interactor
+
+ import test.ui.viewmodel.ViewModel
+
+ class BadClass(
+ private val viewModel: ViewModel,
+ )
+ """.trimIndent()
+ )
+ )
+ .issues(
+ CleanArchitectureDependencyViolationDetector.ISSUE,
+ )
+ .testModes(TestMode.DEFAULT)
+ .run()
+ .expectWarningCount(1)
+ .expect(
+ expectedText =
+ """
+ src/test/domain/interactor/BadClass.kt:3: Warning: The domain layer may not depend on the ui layer. [CleanArchitectureDependencyViolation]
+ import test.ui.viewmodel.ViewModel
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """,
+ )
+ }
+
+ @Test
+ fun `Violation - ui depends on data`() {
+ lint()
+ .files(
+ *LEGITIMATE_FILES,
+ TestFiles.kotlin(
+ """
+ package test.ui.viewmodel
+
+ import test.data.repository.Repository
+
+ class BadClass(
+ private val repository: Repository,
+ )
+ """.trimIndent()
+ )
+ )
+ .issues(
+ CleanArchitectureDependencyViolationDetector.ISSUE,
+ )
+ .testModes(TestMode.DEFAULT)
+ .run()
+ .expectWarningCount(1)
+ .expect(
+ expectedText =
+ """
+ src/test/ui/viewmodel/BadClass.kt:3: Warning: The ui layer may not depend on the data layer. [CleanArchitectureDependencyViolation]
+ import test.data.repository.Repository
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """,
+ )
+ }
+
+ @Test
+ fun `Violation - shared depends on all other layers`() {
+ lint()
+ .files(
+ *LEGITIMATE_FILES,
+ TestFiles.kotlin(
+ """
+ package test.shared.model
+
+ import test.data.repository.Repository
+ import test.domain.interactor.Interactor
+ import test.ui.viewmodel.ViewModel
+
+ class BadClass(
+ private val repository: Repository,
+ private val interactor: Interactor,
+ private val viewmodel: ViewModel,
+ )
+ """.trimIndent()
+ )
+ )
+ .issues(
+ CleanArchitectureDependencyViolationDetector.ISSUE,
+ )
+ .testModes(TestMode.DEFAULT)
+ .run()
+ .expectWarningCount(3)
+ .expect(
+ expectedText =
+ """
+ src/test/shared/model/BadClass.kt:3: Warning: The shared layer may not depend on the data layer. [CleanArchitectureDependencyViolation]
+ import test.data.repository.Repository
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/shared/model/BadClass.kt:4: Warning: The shared layer may not depend on the domain layer. [CleanArchitectureDependencyViolation]
+ import test.domain.interactor.Interactor
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/shared/model/BadClass.kt:5: Warning: The shared layer may not depend on the ui layer. [CleanArchitectureDependencyViolation]
+ import test.ui.viewmodel.ViewModel
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 3 warnings
+ """,
+ )
+ }
+
+ @Test
+ fun `Violation - data depends on domain`() {
+ lint()
+ .files(
+ *LEGITIMATE_FILES,
+ TestFiles.kotlin(
+ """
+ package test.data.repository
+
+ import test.domain.interactor.Interactor
+
+ class BadClass(
+ private val interactor: Interactor,
+ )
+ """.trimIndent()
+ )
+ )
+ .issues(
+ CleanArchitectureDependencyViolationDetector.ISSUE,
+ )
+ .testModes(TestMode.DEFAULT)
+ .run()
+ .expectWarningCount(1)
+ .expect(
+ expectedText =
+ """
+ src/test/data/repository/BadClass.kt:3: Warning: The data layer may not depend on the domain layer. [CleanArchitectureDependencyViolation]
+ import test.domain.interactor.Interactor
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """,
+ )
+ }
+
+ companion object {
+ private val MODEL_FILE =
+ TestFiles.kotlin(
+ """
+ package test.shared.model
+
+ import test.some.other.thing.SomeOtherThing
+
+ data class Model(
+ private val name: String,
+ )
+ """.trimIndent()
+ )
+ private val REPOSITORY_FILE =
+ TestFiles.kotlin(
+ """
+ package test.data.repository
+
+ import test.shared.model.Model
+ import test.some.other.thing.SomeOtherThing
+
+ class Repository {
+ private val models = listOf(
+ Model("one"),
+ Model("two"),
+ Model("three"),
+ )
+
+ fun getModels(): List<Model> {
+ return models
+ }
+ }
+ """.trimIndent()
+ )
+ private val INTERACTOR_FILE =
+ TestFiles.kotlin(
+ """
+ package test.domain.interactor
+
+ import test.data.repository.Repository
+ import test.shared.model.Model
+
+ class Interactor(
+ private val repository: Repository,
+ ) {
+ fun getModels(): List<Model> {
+ return repository.getModels()
+ }
+ }
+ """.trimIndent()
+ )
+ private val VIEW_MODEL_FILE =
+ TestFiles.kotlin(
+ """
+ package test.ui.viewmodel
+
+ import test.domain.interactor.Interactor
+ import test.some.other.thing.SomeOtherThing
+
+ class ViewModel(
+ private val interactor: Interactor,
+ ) {
+ fun getNames(): List<String> {
+ return interactor.getModels().map { model -> model.name }
+ }
+ }
+ """.trimIndent()
+ )
+ private val NON_CLEAN_ARCHITECTURE_FILE =
+ TestFiles.kotlin(
+ """
+ package test.some.other.thing
+
+ import test.data.repository.Repository
+ import test.domain.interactor.Interactor
+ import test.ui.viewmodel.ViewModel
+
+ class SomeOtherThing {
+ init {
+ val viewModel = ViewModel(
+ interactor = Interactor(
+ repository = Repository(),
+ ),
+ )
+ }
+ }
+ """.trimIndent()
+ )
+ private val LEGITIMATE_FILES =
+ arrayOf(
+ MODEL_FILE,
+ REPOSITORY_FILE,
+ INTERACTOR_FILE,
+ VIEW_MODEL_FILE,
+ NON_CLEAN_ARCHITECTURE_FILE,
+ )
+ }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
index 7f1c78f..e4e9c46 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
@@ -178,6 +178,9 @@
/** Flag denoting whether the customizable clocks feature is enabled. */
const val FLAG_NAME_CUSTOM_CLOCKS_ENABLED = "is_custom_clocks_feature_enabled"
+ /** Flag denoting whether the Wallpaper preview should use the full screen UI. */
+ const val FLAG_NAME_WALLPAPER_FULLSCREEN_PREVIEW = "wallpaper_fullscreen_preview"
+
object Columns {
/** String. Unique ID for the flag. */
const val NAME = "name"
diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md
index 8917013..9b4c21e 100644
--- a/packages/SystemUI/docs/dagger.md
+++ b/packages/SystemUI/docs/dagger.md
@@ -8,105 +8,110 @@
- [User's guide](https://google.github.io/dagger/users-guide)
-TODO: Add some links.
-
## State of the world
-Dagger 2 has been turned on for SystemUI and a early first pass has been taken
-for converting everything in [Dependency.java](packages/systemui/src/com/android/systemui/Dependency.java)
-to use Dagger. Since a lot of SystemUI depends on Dependency, stubs have been added to Dependency
-to proxy any gets through to the instances provided by dagger, this will allow migration of SystemUI
-through a number of CLs.
+Dagger 2 has been turned on for SystemUI and much of
+[Dependency.java](../src/com/android/systemui/Dependency.java)
+has been converted to use Dagger. Since a lot of SystemUI depends on Dependency,
+stubs have been added to Dependency to proxy any gets through to the instances
+provided by dagger, this will allow migration of SystemUI through a number of CLs.
### How it works in SystemUI
+There are three high level "scopes" of concern in SystemUI. They all represent
+singleton scopes, but serve different purposes.
+
+* `@Singleton` - Instances that are shared everywhere. There isn't a lot of
+ code in this scope. Things like the main thread, and Android Framework
+ provided instances mostly.
+* `@WMShell` - WindowManager related code in the SystemUI process. We don't
+ want this code relying on the rest of SystemUI, and we don't want the rest
+ of SystemUI peeking into its internals, so it runs in its own Subcomponent.
+* `@SysUISingleton` - Most of what would be considered "SystemUI". Most feature
+ work by SystemUI developers goes into this scope. Useful interfaces from
+ WindowManager are made available inside this Subcomponent.
+
+The root dagger graph is created by an instance of `SystemUIInitializer`.
+See [README.md](../README.md) for more details.
For the classes that we're using in Dependency and are switching to dagger, the
equivalent dagger version is using `@Singleton` and therefore only has one instance.
To have the single instance span all of SystemUI and be easily accessible for
other components, there is a single root `@Component` that exists that generates
-these. The component lives in [SystemUIFactory](packages/systemui/src/com/android/systemui/SystemUIFactory.java)
-and is called `SystemUIRootComponent`.
-
-```java
-
-@Singleton
-@Component(modules = {SystemUIFactory.class, DependencyProvider.class, DependencyBinder.class,
- ContextHolder.class})
-public interface SystemUIRootComponent {
- @Singleton
- Dependency.DependencyInjector createDependency();
-}
-```
-
-The root component is composed of root modules, which in turn provide the global singleton
-dependencies across all of SystemUI.
-
-- `SystemUIFactory` `@Provides` dependencies that need to be overridden by SystemUI
-variants (like other form factors e.g. Car).
-
-- `DependencyBinder` creates the mapping from interfaces to implementation classes.
-
-- `DependencyProvider` provides or binds any remaining depedencies required.
-
-### Adding injection to a new SystemUI object
-
-SystemUI object are made injectable by adding an entry in `SystemUIBinder`. SystemUIApplication uses
-information in that file to locate and construct an instance of the requested SystemUI class.
+these. The component lives in
+[ReferenceGlobalRootComponent.java](../src/com/android/systemui/dagger/ReferenceGlobalRootComponent.java).
### Adding a new injectable object
-First tag the constructor with `@Inject`. Also tag it with `@Singleton` if only one
-instance should be created.
+First annotate the constructor with `@Inject`. Also annotate it with
+`@SysUISingleton` if only one instance should be created.
-```java
-@Singleton
-public class SomethingController {
- @Inject
- public SomethingController(Context context,
- @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
- // context and mainHandler will be automatically populated.
- }
+```kotlin
+@SysUISingleton
+class FeatureStartable
+@Inject
+constructor(
+/* ... */
+) {
+ // ...
}
```
-If you have an interface class and an implementation class, dagger needs to know
-how to map it. The simplest way to do this is to add an `@Provides` method to
-DependencyProvider. The type of the return value tells dagger which dependency it's providing.
+If you have an interface class and an implementation class, Dagger needs to
+know how to map it. The simplest way to do this is to add an `@Binds` method
+in a module. The type of the return value tells dagger which dependency it's
+providing:
-```java
-public class DependencyProvider {
- //...
- @Singleton
- @Provides
- public SomethingController provideSomethingController(Context context,
- @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
- return new SomethingControllerImpl(context, mainHandler);
- }
+```kotlin
+@Module
+abstract class FeatureModule {
+ @Binds
+ abstract fun bindsFeature(impl: FeatureImpl): Feature
}
```
-If you need to access this from Dependency#get, then add an adapter to Dependency
-that maps to the instance provided by Dagger. The changes should be similar
-to the following diff.
+If you have a class that you want to make injectable that has can not
+be easily constructed by Dagger, write a `@Provides` method for it:
-```java
-public class Dependency {
- //...
- @Inject Lazy<SomethingController> mSomethingController;
- //...
- public void start() {
- //...
- mProviders.put(SomethingController.class, mSomethingController::get);
- }
+```kotlin
+@Module
+abstract class FeatureModule {
+ @Module
+ companion object {
+ @Provides
+ fun providesFeature(ctx: Context): Feature {
+ return FeatureImpl.constructFromContext(ctx)
+ }
+ }
}
```
+### Module Organization
+
+Please define your modules on _at least_ per-package level. If the scope of a
+package grows to encompass a great number of features, create per-feature
+modules.
+
+**Do not create catch-all modules.** Those quickly grow unwieldy and
+unmaintainable. Any that exist today should be refactored into obsolescence.
+
+You can then include your module in one of three places:
+
+1) Within another module that depends on it. Ideally, this creates a clean
+ dependency graph between features and utilities.
+2) For features that should exist in all versions of SystemUI (AOSP and
+ any variants), include the module in
+ [SystemUIModule.java](../src/com/android/systemui/dagger/SystemUIModule.java).
+3) For features that should exist only in AOSP, include the module in
+ [ReferenceSystemUIModule.java](../src/com/android/systemui/dagger/ReferenceSystemUIModule.java).
+ Similarly, if you are working on a custom version of SystemUI and have code
+ specific to your version, include it in a module specific to your version.
+
### Using injection with Fragments
Fragments are created as part of the FragmentManager, so they need to be
setup so the manager knows how to create them. To do that, add a method
to com.android.systemui.fragments.FragmentService$FragmentCreator that
-returns your fragment class. Thats all thats required, once the method
+returns your fragment class. That is all that is required, once the method
exists, FragmentService will automatically pick it up and use injection
whenever your fragment needs to be created.
@@ -123,48 +128,11 @@
FragmentHostManager.get(view).create(NavigationBarFragment.class);
```
-### Using injection with Views
-
-DO NOT ADD NEW VIEW INJECTION. VIEW INJECTION IS BEING ACTIVELY DEPRECATED.
-
-Needing to inject objects into your View's constructor generally implies you
-are doing more work in your presentation layer than is advisable.
-Instead, create an injected controller for you view, inject into the
-controller, and then attach the view to the controller after inflation.
-
-View injection generally causes headaches while testing, as inflating a view
-(which may in turn inflate other views) implicitly causes a Dagger graph to
-be stood up, which may or may not contain the appropriately
-faked/mocked/stubbed objects. It is a hard to control process.
-
## Updating Dagger2
We depend on the Dagger source found in external/dagger2. We should automatically pick up on updates
when that repository is updated.
-
-*Deprecated:*
-
-Binaries can be downloaded from https://repo1.maven.org/maven2/com/google/dagger/ and then loaded
-into
-[/prebuilts/tools/common/m2/repository/com/google/dagger/](http://cs/android/prebuilts/tools/common/m2/repository/com/google/dagger/)
-
-The following commands should work, substituting in the version that you are looking for:
-
-````
-cd prebuilts/tools/common/m2/repository/com/google/dagger/
-
-wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger/2.28.1/
-
-wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-compiler/2.28.1/
-
-wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-spi/2.28.1/
-
-wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-producers/2.28.1/
-````
-
-Then update `prebuilts/tools/common/m2/Android.bp` to point at your new jars.
## TODO List
- - Eliminate usages of Dependency#get
- - Add links in above TODO
+ - Eliminate usages of Dependency#get: http://b/hotlists/3940788
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index f53e3f6..1f00812 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -1,8 +1,5 @@
+packages/SystemUI
--packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
-packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
--packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
-packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
-packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceViaContextDetector.kt
-packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt
@@ -322,8 +319,8 @@
-packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
-packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
-packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
--packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
--packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureLogger.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt
-packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt
-packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
-packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index dee0f5c..314c736 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -80,6 +80,7 @@
internal class HueVibrantSecondary() : Hue {
val hueToRotations = listOf(Pair(0, 18), Pair(41, 15), Pair(61, 10), Pair(101, 12),
Pair(131, 15), Pair(181, 18), Pair(251, 15), Pair(301, 12), Pair(360, 12))
+
override fun get(sourceColor: Cam): Double {
return getHueRotation(sourceColor.hue, hueToRotations)
}
@@ -88,6 +89,7 @@
internal class HueVibrantTertiary() : Hue {
val hueToRotations = listOf(Pair(0, 35), Pair(41, 30), Pair(61, 20), Pair(101, 25),
Pair(131, 30), Pair(181, 35), Pair(251, 30), Pair(301, 25), Pair(360, 25))
+
override fun get(sourceColor: Cam): Double {
return getHueRotation(sourceColor.hue, hueToRotations)
}
@@ -96,6 +98,7 @@
internal class HueExpressiveSecondary() : Hue {
val hueToRotations = listOf(Pair(0, 45), Pair(21, 95), Pair(51, 45), Pair(121, 20),
Pair(151, 45), Pair(191, 90), Pair(271, 45), Pair(321, 45), Pair(360, 45))
+
override fun get(sourceColor: Cam): Double {
return getHueRotation(sourceColor.hue, hueToRotations)
}
@@ -104,6 +107,7 @@
internal class HueExpressiveTertiary() : Hue {
val hueToRotations = listOf(Pair(0, 120), Pair(21, 120), Pair(51, 20), Pair(121, 45),
Pair(151, 20), Pair(191, 15), Pair(271, 20), Pair(321, 120), Pair(360, 120))
+
override fun get(sourceColor: Cam): Double {
return getHueRotation(sourceColor.hue, hueToRotations)
}
@@ -148,11 +152,11 @@
}
internal class CoreSpec(
- val a1: TonalSpec,
- val a2: TonalSpec,
- val a3: TonalSpec,
- val n1: TonalSpec,
- val n2: TonalSpec
+ val a1: TonalSpec,
+ val a2: TonalSpec,
+ val a3: TonalSpec,
+ val n1: TonalSpec,
+ val n2: TonalSpec
)
enum class Style(internal val coreSpec: CoreSpec) {
@@ -214,51 +218,86 @@
)),
}
+class TonalPalette {
+ val shadeKeys = listOf(10, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000)
+ val allShades: List<Int>
+ val allShadesMapped: Map<Int, Int>
+ val baseColor: Int
+
+ internal constructor(spec: TonalSpec, seedColor: Int) {
+ val seedCam = Cam.fromInt(seedColor)
+ allShades = spec.shades(seedCam)
+ allShadesMapped = shadeKeys.zip(allShades).toMap()
+
+ val h = spec.hue.get(seedCam).toFloat()
+ val c = spec.chroma.get(seedCam).toFloat()
+ baseColor = ColorUtils.CAMToColor(h, c, CamUtils.lstarFromInt(seedColor))
+ }
+
+ val s10: Int get() = this.allShades[0]
+ val s50: Int get() = this.allShades[1]
+ val s100: Int get() = this.allShades[2]
+ val s200: Int get() = this.allShades[3]
+ val s300: Int get() = this.allShades[4]
+ val s400: Int get() = this.allShades[5]
+ val s500: Int get() = this.allShades[6]
+ val s600: Int get() = this.allShades[7]
+ val s700: Int get() = this.allShades[8]
+ val s800: Int get() = this.allShades[9]
+ val s900: Int get() = this.allShades[10]
+ val s1000: Int get() = this.allShades[11]
+}
+
class ColorScheme(
- @ColorInt val seed: Int,
- val darkTheme: Boolean,
- val style: Style = Style.TONAL_SPOT
+ @ColorInt val seed: Int,
+ val darkTheme: Boolean,
+ val style: Style = Style.TONAL_SPOT
) {
- val accent1: List<Int>
- val accent2: List<Int>
- val accent3: List<Int>
- val neutral1: List<Int>
- val neutral2: List<Int>
+ val accent1: TonalPalette
+ val accent2: TonalPalette
+ val accent3: TonalPalette
+ val neutral1: TonalPalette
+ val neutral2: TonalPalette
constructor(@ColorInt seed: Int, darkTheme: Boolean) :
this(seed, darkTheme, Style.TONAL_SPOT)
@JvmOverloads
constructor(
- wallpaperColors: WallpaperColors,
- darkTheme: Boolean,
- style: Style = Style.TONAL_SPOT
+ wallpaperColors: WallpaperColors,
+ darkTheme: Boolean,
+ style: Style = Style.TONAL_SPOT
) :
this(getSeedColor(wallpaperColors, style != Style.CONTENT), darkTheme, style)
+ val allHues: List<TonalPalette>
+ get() {
+ return listOf(accent1, accent2, accent3, neutral1, neutral2)
+ }
+
val allAccentColors: List<Int>
get() {
val allColors = mutableListOf<Int>()
- allColors.addAll(accent1)
- allColors.addAll(accent2)
- allColors.addAll(accent3)
+ allColors.addAll(accent1.allShades)
+ allColors.addAll(accent2.allShades)
+ allColors.addAll(accent3.allShades)
return allColors
}
val allNeutralColors: List<Int>
get() {
val allColors = mutableListOf<Int>()
- allColors.addAll(neutral1)
- allColors.addAll(neutral2)
+ allColors.addAll(neutral1.allShades)
+ allColors.addAll(neutral2.allShades)
return allColors
}
val backgroundColor
- get() = ColorUtils.setAlphaComponent(if (darkTheme) neutral1[8] else neutral1[0], 0xFF)
+ get() = ColorUtils.setAlphaComponent(if (darkTheme) neutral1.s700 else neutral1.s10, 0xFF)
val accentColor
- get() = ColorUtils.setAlphaComponent(if (darkTheme) accent1[2] else accent1[6], 0xFF)
+ get() = ColorUtils.setAlphaComponent(if (darkTheme) accent1.s100 else accent1.s500, 0xFF)
init {
val proposedSeedCam = Cam.fromInt(seed)
@@ -269,24 +308,26 @@
} else {
seed
}
- val camSeed = Cam.fromInt(seedArgb)
- accent1 = style.coreSpec.a1.shades(camSeed)
- accent2 = style.coreSpec.a2.shades(camSeed)
- accent3 = style.coreSpec.a3.shades(camSeed)
- neutral1 = style.coreSpec.n1.shades(camSeed)
- neutral2 = style.coreSpec.n2.shades(camSeed)
+
+ accent1 = TonalPalette(style.coreSpec.a1, seedArgb)
+ accent2 = TonalPalette(style.coreSpec.a2, seedArgb)
+ accent3 = TonalPalette(style.coreSpec.a3, seedArgb)
+ neutral1 = TonalPalette(style.coreSpec.n1, seedArgb)
+ neutral2 = TonalPalette(style.coreSpec.n2, seedArgb)
}
+ val shadeCount get() = this.accent1.allShades.size
+
override fun toString(): String {
return "ColorScheme {\n" +
" seed color: ${stringForColor(seed)}\n" +
" style: $style\n" +
" palettes: \n" +
- " ${humanReadable("PRIMARY", accent1)}\n" +
- " ${humanReadable("SECONDARY", accent2)}\n" +
- " ${humanReadable("TERTIARY", accent3)}\n" +
- " ${humanReadable("NEUTRAL", neutral1)}\n" +
- " ${humanReadable("NEUTRAL VARIANT", neutral2)}\n" +
+ " ${humanReadable("PRIMARY", accent1.allShades)}\n" +
+ " ${humanReadable("SECONDARY", accent2.allShades)}\n" +
+ " ${humanReadable("TERTIARY", accent3.allShades)}\n" +
+ " ${humanReadable("NEUTRAL", neutral1.allShades)}\n" +
+ " ${humanReadable("NEUTRAL VARIANT", neutral2.allShades)}\n" +
"}"
}
@@ -385,7 +426,8 @@
val existingSeedNearby = seeds.find {
val hueA = intToCam[int]!!.hue
val hueB = intToCam[it]!!.hue
- hueDiff(hueA, hueB) < i } != null
+ hueDiff(hueA, hueB) < i
+ } != null
if (existingSeedNearby) {
continue
}
@@ -460,9 +502,9 @@
}
private fun huePopulations(
- camByColor: Map<Int, Cam>,
- populationByColor: Map<Int, Double>,
- filter: Boolean = true
+ camByColor: Map<Int, Cam>,
+ populationByColor: Map<Int, Double>,
+ filter: Boolean = true
): List<Double> {
val huePopulation = List(size = 360, init = { 0.0 }).toMutableList()
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt
index d3fabac..faf1b78 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt
@@ -21,6 +21,7 @@
import android.net.Uri
import android.os.Handler
import android.os.Looper
+import android.os.Trace
import android.provider.Settings
/**
@@ -51,14 +52,21 @@
}
}
+ private fun clearCache() {
+ Trace.beginSection("LogcatEchoTrackerDebug#clearCache")
+ cachedBufferLevels.clear()
+ Trace.endSection()
+ }
+
private fun attach(mainLooper: Looper) {
+ Trace.beginSection("LogcatEchoTrackerDebug#attach")
contentResolver.registerContentObserver(
Settings.Global.getUriFor(BUFFER_PATH),
true,
object : ContentObserver(Handler(mainLooper)) {
override fun onChange(selfChange: Boolean, uri: Uri?) {
super.onChange(selfChange, uri)
- cachedBufferLevels.clear()
+ clearCache()
}
}
)
@@ -69,10 +77,11 @@
object : ContentObserver(Handler(mainLooper)) {
override fun onChange(selfChange: Boolean, uri: Uri?) {
super.onChange(selfChange, uri)
- cachedTagLevels.clear()
+ clearCache()
}
}
)
+ Trace.endSection()
}
/** Whether [bufferName] should echo messages of [level] or higher to logcat. */
@@ -97,9 +106,12 @@
private fun readSetting(path: String): LogLevel {
return try {
+ Trace.beginSection("LogcatEchoTrackerDebug#readSetting")
parseProp(Settings.Global.getString(contentResolver, path))
} catch (_: Settings.SettingNotFoundException) {
DEFAULT_LEVEL
+ } finally {
+ Trace.endSection()
}
}
diff --git a/packages/SystemUI/res/drawable/ic_camera.xml b/packages/SystemUI/res/drawable/ic_camera.xml
new file mode 100644
index 0000000..ef1406c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_camera.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M7,42Q5.8,42 4.9,41.1Q4,40.2 4,39V13.35Q4,12.15 4.9,11.25Q5.8,10.35 7,10.35H14.35L18,6H30L33.65,10.35H41Q42.2,10.35 43.1,11.25Q44,12.15 44,13.35V39Q44,40.2 43.1,41.1Q42.2,42 41,42ZM7,39H41Q41,39 41,39Q41,39 41,39V13.35Q41,13.35 41,13.35Q41,13.35 41,13.35H7Q7,13.35 7,13.35Q7,13.35 7,13.35V39Q7,39 7,39Q7,39 7,39ZM7,39Q7,39 7,39Q7,39 7,39V13.35Q7,13.35 7,13.35Q7,13.35 7,13.35Q7,13.35 7,13.35Q7,13.35 7,13.35V39Q7,39 7,39Q7,39 7,39ZM24,34.7Q27.5,34.7 30,32.225Q32.5,29.75 32.5,26.2Q32.5,22.7 30,20.2Q27.5,17.7 24,17.7Q20.45,17.7 17.975,20.2Q15.5,22.7 15.5,26.2Q15.5,29.75 17.975,32.225Q20.45,34.7 24,34.7ZM24,26.2Q24,26.2 24,26.2Q24,26.2 24,26.2Q24,26.2 24,26.2Q24,26.2 24,26.2Q24,26.2 24,26.2Q24,26.2 24,26.2Q24,26.2 24,26.2Q24,26.2 24,26.2Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_note_task_button.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml
similarity index 92%
copy from packages/SystemUI/res/drawable/ic_note_task_button.xml
copy to packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml
index bb5e224..ee8d4883 100644
--- a/packages/SystemUI/res/drawable/ic_note_task_button.xml
+++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml
@@ -19,10 +19,13 @@
android:viewportHeight="24"
android:viewportWidth="24">
<path
- android:fillColor="#636C6F"
+ android:fillAlpha="1"
+ android:fillColor="@android:color/white"
+ android:fillType="nonZero"
android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z" />
<path
- android:fillColor="#636C6F"
- android:fillType="evenOdd"
+ android:fillAlpha="1"
+ android:fillColor="@android:color/white"
+ android:fillType="nonZero"
android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_note_task_button.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml
similarity index 95%
rename from packages/SystemUI/res/drawable/ic_note_task_button.xml
rename to packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml
index bb5e224..7590182 100644
--- a/packages/SystemUI/res/drawable/ic_note_task_button.xml
+++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml
@@ -19,10 +19,13 @@
android:viewportHeight="24"
android:viewportWidth="24">
<path
+ android:fillAlpha="1"
android:fillColor="#636C6F"
+ android:fillType="nonZero"
android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z" />
<path
+ android:fillAlpha="1"
android:fillColor="#636C6F"
- android:fillType="evenOdd"
+ android:fillType="nonZero"
android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_videocam.xml b/packages/SystemUI/res/drawable/ic_videocam.xml
new file mode 100644
index 0000000..de2bc7b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_videocam.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M7,40Q5.8,40 4.9,39.1Q4,38.2 4,37V11Q4,9.8 4.9,8.9Q5.8,8 7,8H33Q34.2,8 35.1,8.9Q36,9.8 36,11V21.75L44,13.75V34.25L36,26.25V37Q36,38.2 35.1,39.1Q34.2,40 33,40ZM7,37H33Q33,37 33,37Q33,37 33,37V11Q33,11 33,11Q33,11 33,11H7Q7,11 7,11Q7,11 7,11V37Q7,37 7,37Q7,37 7,37ZM7,37Q7,37 7,37Q7,37 7,37V11Q7,11 7,11Q7,11 7,11Q7,11 7,11Q7,11 7,11V37Q7,37 7,37Q7,37 7,37Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
index 18fcebb..87b5a4c 100644
--- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
+++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
@@ -16,53 +16,13 @@
* limitations under the License.
*/
-->
-<selector
+<shape
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
-
- <item android:state_selected="true">
- <layer-list>
- <item
- android:left="3dp"
- android:top="3dp"
- android:right="3dp"
- android:bottom="3dp">
- <shape android:shape="oval">
- <solid android:color="?androidprv:attr/colorSurface"/>
- <size
- android:width="@dimen/keyguard_affordance_width"
- android:height="@dimen/keyguard_affordance_height"/>
- </shape>
- </item>
-
- <item>
- <shape android:shape="oval">
- <stroke
- android:color="@color/control_primary_text"
- android:width="2dp"/>
- <size
- android:width="@dimen/keyguard_affordance_width"
- android:height="@dimen/keyguard_affordance_height"/>
- </shape>
- </item>
- </layer-list>
- </item>
-
- <item>
- <layer-list>
- <item
- android:left="3dp"
- android:top="3dp"
- android:right="3dp"
- android:bottom="3dp">
- <shape android:shape="oval">
- <solid android:color="?androidprv:attr/colorSurface"/>
- <size
- android:width="@dimen/keyguard_affordance_width"
- android:height="@dimen/keyguard_affordance_height"/>
- </shape>
- </item>
- </layer-list>
- </item>
-
-</selector>
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <solid android:color="?androidprv:attr/colorSurface"/>
+ <size
+ android:width="@dimen/keyguard_affordance_fixed_width"
+ android:height="@dimen/keyguard_affordance_fixed_height"/>
+ <corners android:radius="@dimen/keyguard_affordance_fixed_radius" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
new file mode 100644
index 0000000..acd2462
--- /dev/null
+++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright 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.
+*/
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_selected="true">
+ <shape android:shape="oval">
+ <stroke
+ android:color="@color/control_primary_text"
+ android:width="2dp"/>
+ <size
+ android:width="@dimen/keyguard_affordance_fixed_width"
+ android:height="@dimen/keyguard_affordance_fixed_height"/>
+ </shape>
+ </item>
+</selector>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 6120863..3f95515 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -67,6 +67,7 @@
android:scaleType="center"
android:tint="?android:attr/textColorPrimary"
android:background="@drawable/keyguard_bottom_affordance_bg"
+ android:foreground="@drawable/keyguard_bottom_affordance_selected_border"
android:layout_marginStart="@dimen/keyguard_affordance_horizontal_offset"
android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
android:visibility="gone" />
@@ -79,6 +80,7 @@
android:scaleType="center"
android:tint="?android:attr/textColorPrimary"
android:background="@drawable/keyguard_bottom_affordance_bg"
+ android:foreground="@drawable/keyguard_bottom_affordance_selected_border"
android:layout_marginEnd="@dimen/keyguard_affordance_horizontal_offset"
android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
android:visibility="gone" />
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index b76de5a..e182a6a 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -24,6 +24,7 @@
android:orientation="vertical">
<LinearLayout
+ android:id="@+id/media_metadata_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index bbb8df1c..db94c92 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -26,6 +26,17 @@
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content">
+ <TextView
+ android:id="@+id/unlock_prompt_footer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12dp"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center"
+ android:drawablePadding="8dp"
+ android:visibility="gone"
+ android:textAppearance="?android:attr/textAppearanceButton"
+ android:text="@string/unlock_to_see_notif_text"/>
<com.android.systemui.statusbar.notification.row.FooterViewButton
style="@style/TextAppearance.NotificationSectionHeaderButton"
android:id="@+id/manage_text"
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index f4434e8..ea3c012 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -36,4 +36,15 @@
<integer name="qs_security_footer_maxLines">1</integer>
<bool name="config_use_large_screen_shade_header">true</bool>
+
+ <!-- A collection of defaults for the quick affordances on the lock screen. Each item must be a
+ string with two parts: the ID of the slot and the comma-delimited list of affordance IDs,
+ separated by a colon ':' character. For example: <item>bottom_end:home,wallet</item>. The
+ default is displayed by System UI as long as the user hasn't made a different choice for that
+ slot. If the user did make a choice, even if the choice is the "None" option, the default is
+ ignored. -->
+ <string-array name="config_keyguardQuickAffordanceDefaults" translatable="false">
+ <item>bottom_start:home</item>
+ <item>bottom_end:create_note</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 9bc0dde..db7fb48 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -30,10 +30,6 @@
<!-- Margin on the left side of the carrier text on Keyguard -->
<dimen name="keyguard_carrier_text_margin">24dp</dimen>
- <!-- The width/height of the phone/camera/unlock icon on keyguard. -->
- <dimen name="keyguard_affordance_height">80dp</dimen>
- <dimen name="keyguard_affordance_width">120dp</dimen>
-
<!-- Screen pinning request width -->
<dimen name="screen_pinning_request_width">400dp</dimen>
<!-- Screen pinning request bottom button circle widths -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a68e759..1ef0206 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -762,12 +762,10 @@
<dimen name="go_to_full_shade_appearing_translation">200dp</dimen>
<!-- The width/height of the keyguard bottom area icon view on keyguard. -->
- <dimen name="keyguard_affordance_height">48dp</dimen>
- <dimen name="keyguard_affordance_width">48dp</dimen>
-
<dimen name="keyguard_affordance_fixed_height">48dp</dimen>
<dimen name="keyguard_affordance_fixed_width">48dp</dimen>
<dimen name="keyguard_affordance_fixed_radius">24dp</dimen>
+
<!-- Amount the button should shake when it's not long-pressed for long enough. -->
<dimen name="keyguard_affordance_shake_amplitude">8dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 43dea0a..066b185 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2778,6 +2778,12 @@
<!-- Subtitle for the notification sent when a stylus battery is low. [CHAR LIMIT=none]-->
<string name="stylus_battery_low_subtitle">Connect your stylus to a charger</string>
+ <!-- Title for notification of low stylus battery. [CHAR_LIMIT=NONE] -->
+ <string name="stylus_battery_low">Stylus battery low</string>
+
+ <!-- Label for a lock screen shortcut to start the camera in video mode. [CHAR_LIMIT=16] -->
+ <string name="video_camera">Video camera</string>
+
<!-- Switch to work profile dialer app for placing a call dialog. -->
<!-- Text for Switch to work profile dialog's Title. Switch to work profile dialog guide users to make call from work
profile dialer app as it's not possible to make call from current profile due to an admin policy. [CHAR LIMIT=60] -->
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index b679cfa1..b7d9df9 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -85,6 +85,9 @@
static_libs: [
"SystemUI-flag-types",
],
+ optimize: {
+ proguard_flags_files: ["proguard_flags.flags"],
+ },
java_version: "1.8",
min_sdk_version: "current",
}
diff --git a/packages/SystemUI/shared/proguard_flags.flags b/packages/SystemUI/shared/proguard_flags.flags
new file mode 100644
index 0000000..08859cd
--- /dev/null
+++ b/packages/SystemUI/shared/proguard_flags.flags
@@ -0,0 +1 @@
+-keep class * implements com.android.systemui.flags.ParcelableFlag
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 1c532fe..b8bddd1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -22,6 +22,7 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.view.MotionEvent;
+import com.android.internal.util.ScreenshotRequest;
import com.android.systemui.shared.recents.model.Task;
@@ -87,12 +88,6 @@
void notifyPrioritizedRotation(int rotation) = 25;
/**
- * Handle the provided image as if it was a screenshot.
- */
- void handleImageBundleAsScreenshot(in Bundle screenImageBundle, in Rect locationInScreen,
- in Insets visibleInsets, in Task.TaskKey task) = 28;
-
- /**
* Notifies to expand notification panel.
*/
void expandNotificationPanel() = 29;
@@ -125,5 +120,10 @@
*/
void toggleNotificationPanel() = 50;
- // Next id = 51
+ /**
+ * Handle the screenshot request.
+ */
+ void takeScreenshot(in ScreenshotRequest request) = 51;
+
+ // Next id = 52
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 88ce2a7..71cd18d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -199,7 +199,7 @@
}
mSecureSettings.registerContentObserverForUser(
- Settings.Secure.getUriFor(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK),
+ Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK,
false, /* notifyForDescendants */
mDoubleLineClockObserver,
UserHandle.USER_ALL
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/TrustRepositoryLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/TrustRepositoryLogger.kt
new file mode 100644
index 0000000..249b3fe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/logging/TrustRepositoryLogger.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.keyguard.logging
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.TrustModel
+import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import javax.inject.Inject
+
+/** Logging helper for trust repository. */
+@SysUISingleton
+class TrustRepositoryLogger
+@Inject
+constructor(
+ @KeyguardUpdateMonitorLog private val logBuffer: LogBuffer,
+) {
+ fun onTrustChanged(
+ enabled: Boolean,
+ newlyUnlocked: Boolean,
+ userId: Int,
+ flags: Int,
+ trustGrantedMessages: List<String>?
+ ) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ bool1 = enabled
+ bool2 = newlyUnlocked
+ int1 = userId
+ int2 = flags
+ str1 = trustGrantedMessages?.joinToString()
+ },
+ {
+ "onTrustChanged enabled: $bool1, newlyUnlocked: $bool2, " +
+ "userId: $int1, flags: $int2, grantMessages: $str1"
+ }
+ )
+ }
+
+ fun trustListenerRegistered() {
+ logBuffer.log(TAG, LogLevel.VERBOSE, "TrustRepository#registerTrustListener")
+ }
+
+ fun trustListenerUnregistered() {
+ logBuffer.log(TAG, LogLevel.VERBOSE, "TrustRepository#unregisterTrustListener")
+ }
+
+ fun trustModelEmitted(value: TrustModel) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ int1 = value.userId
+ bool1 = value.isTrusted
+ },
+ { "trustModel emitted: userId: $int1 isTrusted: $bool1" }
+ )
+ }
+
+ fun isCurrentUserTrusted(isCurrentUserTrusted: Boolean) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { bool1 = isCurrentUserTrusted },
+ { "isCurrentUserTrusted emitted: $bool1" }
+ )
+ }
+
+ companion object {
+ const val TAG = "TrustRepositoryLog"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java
index fbb909f..2c97d62 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java
@@ -27,6 +27,7 @@
import android.util.Log;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.settings.UserTracker;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -67,8 +68,8 @@
}
@Inject
- public AccessibilityButtonModeObserver(Context context) {
- super(context, Settings.Secure.ACCESSIBILITY_BUTTON_MODE);
+ public AccessibilityButtonModeObserver(Context context, UserTracker userTracker) {
+ super(context, userTracker, Settings.Secure.ACCESSIBILITY_BUTTON_MODE);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserver.java b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserver.java
index b32ebcc..53a21b3 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserver.java
@@ -23,6 +23,7 @@
import androidx.annotation.Nullable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.settings.UserTracker;
import javax.inject.Inject;
@@ -48,8 +49,8 @@
}
@Inject
- public AccessibilityButtonTargetsObserver(Context context) {
- super(context, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
+ public AccessibilityButtonTargetsObserver(Context context, UserTracker userTracker) {
+ super(context, userTracker, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java b/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java
index e4e0da6..326773f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java
@@ -27,6 +27,7 @@
import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.settings.UserTracker;
import java.util.ArrayList;
import java.util.List;
@@ -44,6 +45,7 @@
public abstract class SecureSettingsContentObserver<T> {
private final ContentResolver mContentResolver;
+ private final UserTracker mUserTracker;
@VisibleForTesting
final ContentObserver mContentObserver;
@@ -52,9 +54,11 @@
@VisibleForTesting
final List<T> mListeners = new ArrayList<>();
- protected SecureSettingsContentObserver(Context context, String secureSettingsKey) {
+ protected SecureSettingsContentObserver(Context context, UserTracker userTracker,
+ String secureSettingsKey) {
mKey = secureSettingsKey;
mContentResolver = context.getContentResolver();
+ mUserTracker = userTracker;
mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange) {
@@ -103,7 +107,7 @@
* See {@link Settings.Secure}.
*/
public final String getSettingsValue() {
- return Settings.Secure.getStringForUser(mContentResolver, mKey, UserHandle.USER_CURRENT);
+ return Settings.Secure.getStringForUser(mContentResolver, mKey, mUserTracker.getUserId());
}
private void updateValueChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index dab73e9..4c92598 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -35,14 +35,12 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.util.Log;
import android.view.Display;
import android.view.IWindowManager;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
-import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
@@ -52,6 +50,7 @@
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -179,6 +178,7 @@
private final SystemActionsBroadcastReceiver mReceiver;
private final Context mContext;
+ private final UserTracker mUserTracker;
private final Optional<Recents> mRecentsOptional;
private Locale mLocale;
private final AccessibilityManager mA11yManager;
@@ -190,11 +190,13 @@
@Inject
public SystemActions(Context context,
+ UserTracker userTracker,
NotificationShadeWindowController notificationShadeController,
ShadeController shadeController,
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Optional<Recents> recentsOptional) {
mContext = context;
+ mUserTracker = userTracker;
mShadeController = shadeController;
mRecentsOptional = recentsOptional;
mReceiver = new SystemActionsBroadcastReceiver();
@@ -343,6 +345,7 @@
/**
* Register a system action.
+ *
* @param actionId the action ID to register.
*/
public void register(int actionId) {
@@ -440,6 +443,7 @@
/**
* Unregister a system action.
+ *
* @param actionId the action ID to unregister.
*/
public void unregister(int actionId) {
@@ -475,7 +479,8 @@
}
private void handleNotifications() {
- mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::animateExpandNotificationsPanel);
+ mCentralSurfacesOptionalLazy.get().ifPresent(
+ CentralSurfaces::animateExpandNotificationsPanel);
}
private void handleQuickSettings() {
@@ -507,7 +512,7 @@
private void handleTakeScreenshot() {
ScreenshotHelper screenshotHelper = new ScreenshotHelper(mContext);
- screenshotHelper.takeScreenshot(WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
+ screenshotHelper.takeScreenshot(
SCREENSHOT_ACCESSIBILITY_ACTIONS, new Handler(Looper.getMainLooper()), null);
}
@@ -525,7 +530,7 @@
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
final String chooserClassName = AccessibilityButtonChooserActivity.class.getName();
intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ mContext.startActivityAsUser(intent, mUserTracker.getUserHandle());
}
private void handleAccessibilityShortcut() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
index 9af8300..7441e03 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
@@ -43,6 +43,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Prefs;
import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.util.settings.SecureSettings;
import java.util.List;
@@ -60,6 +61,7 @@
private static final float DEFAULT_POSITION_Y_PERCENT = 0.9f;
private final Context mContext;
+ private final SecureSettings mSecureSettings;
private final AccessibilityFloatingMenuView mMenuView;
private final MigrationTooltipView mMigrationTooltipView;
private final DockTooltipView mDockTooltipView;
@@ -77,7 +79,7 @@
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
- mMenuView.setSizeType(getSizeType(mContext));
+ mMenuView.setSizeType(getSizeType());
}
};
@@ -85,8 +87,8 @@
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
- mMenuView.updateOpacityWith(isFadeEffectEnabled(mContext),
- getOpacityValue(mContext));
+ mMenuView.updateOpacityWith(isFadeEffectEnabled(),
+ getOpacityValue());
}
};
@@ -98,16 +100,19 @@
}
};
- public AccessibilityFloatingMenu(Context context) {
+ public AccessibilityFloatingMenu(Context context, SecureSettings secureSettings) {
mContext = context;
+ mSecureSettings = secureSettings;
mMenuView = new AccessibilityFloatingMenuView(context, getPosition(context));
mMigrationTooltipView = new MigrationTooltipView(mContext, mMenuView);
mDockTooltipView = new DockTooltipView(mContext, mMenuView);
}
@VisibleForTesting
- AccessibilityFloatingMenu(Context context, AccessibilityFloatingMenuView menuView) {
+ AccessibilityFloatingMenu(Context context, SecureSettings secureSettings,
+ AccessibilityFloatingMenuView menuView) {
mContext = context;
+ mSecureSettings = secureSettings;
mMenuView = menuView;
mMigrationTooltipView = new MigrationTooltipView(mContext, mMenuView);
mDockTooltipView = new DockTooltipView(mContext, mMenuView);
@@ -130,10 +135,10 @@
mMenuView.show();
mMenuView.onTargetsChanged(targetList);
- mMenuView.updateOpacityWith(isFadeEffectEnabled(mContext),
- getOpacityValue(mContext));
- mMenuView.setSizeType(getSizeType(mContext));
- mMenuView.setShapeType(getShapeType(mContext));
+ mMenuView.updateOpacityWith(isFadeEffectEnabled(),
+ getOpacityValue());
+ mMenuView.setSizeType(getSizeType());
+ mMenuView.setShapeType(getShapeType());
mMenuView.setOnDragEndListener(this::onDragEnd);
showMigrationTooltipIfNecessary();
@@ -170,17 +175,17 @@
// Migration tooltip was the android S feature. It's just used on the Android version from R
// to S. In addition, it only shows once.
private void showMigrationTooltipIfNecessary() {
- if (isMigrationTooltipPromptEnabled(mContext)) {
+ if (isMigrationTooltipPromptEnabled()) {
mMigrationTooltipView.show();
- Settings.Secure.putInt(mContext.getContentResolver(),
+ mSecureSettings.putInt(
ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT, /* disabled */ 0);
}
}
- private static boolean isMigrationTooltipPromptEnabled(Context context) {
- return Settings.Secure.getInt(
- context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT,
+ private boolean isMigrationTooltipPromptEnabled() {
+ return mSecureSettings.getInt(
+ ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT,
DEFAULT_MIGRATION_TOOLTIP_PROMPT_IS_DISABLED) == /* enabled */ 1;
}
@@ -212,57 +217,61 @@
}
}
- private static boolean isFadeEffectEnabled(Context context) {
- return Settings.Secure.getInt(
- context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
+ private boolean isFadeEffectEnabled() {
+ return mSecureSettings.getInt(
+ ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
DEFAULT_FADE_EFFECT_IS_ENABLED) == /* enabled */ 1;
}
- private static float getOpacityValue(Context context) {
- return Settings.Secure.getFloat(
- context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_OPACITY,
+ private float getOpacityValue() {
+ return mSecureSettings.getFloat(
+ ACCESSIBILITY_FLOATING_MENU_OPACITY,
DEFAULT_OPACITY_VALUE);
}
- private static int getSizeType(Context context) {
- return Settings.Secure.getInt(
- context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_SIZE, SizeType.SMALL);
+ private int getSizeType() {
+ return mSecureSettings.getInt(
+ ACCESSIBILITY_FLOATING_MENU_SIZE, SizeType.SMALL);
}
- private static int getShapeType(Context context) {
- return Settings.Secure.getInt(
- context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_ICON_TYPE,
+ private int getShapeType() {
+ return mSecureSettings.getInt(
+ ACCESSIBILITY_FLOATING_MENU_ICON_TYPE,
ShapeType.OVAL);
}
private void registerContentObservers() {
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
/* notifyForDescendants */ false, mContentObserver,
UserHandle.USER_CURRENT);
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+ /* notifyForDescendants */ false, mContentObserver,
+ UserHandle.USER_CURRENT);
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
/* notifyForDescendants */ false, mSizeContentObserver,
UserHandle.USER_CURRENT);
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED),
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
/* notifyForDescendants */ false, mFadeOutContentObserver,
UserHandle.USER_CURRENT);
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY),
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY,
/* notifyForDescendants */ false, mFadeOutContentObserver,
UserHandle.USER_CURRENT);
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES),
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
/* notifyForDescendants */ false,
mEnabledA11yServicesContentObserver, UserHandle.USER_CURRENT);
}
private void unregisterContentObservers() {
- mContext.getContentResolver().unregisterContentObserver(mContentObserver);
- mContext.getContentResolver().unregisterContentObserver(mSizeContentObserver);
- mContext.getContentResolver().unregisterContentObserver(mFadeOutContentObserver);
- mContext.getContentResolver().unregisterContentObserver(
+ mSecureSettings.unregisterContentObserver(mContentObserver);
+ mSecureSettings.unregisterContentObserver(mSizeContentObserver);
+ mSecureSettings.unregisterContentObserver(mFadeOutContentObserver);
+ mSecureSettings.unregisterContentObserver(
mEnabledA11yServicesContentObserver);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
index 403941f..6216b89 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
@@ -31,6 +31,7 @@
import com.android.systemui.accessibility.AccessibilityButtonModeObserver.AccessibilityButtonMode;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.util.settings.SecureSettings;
import javax.inject.Inject;
@@ -44,6 +45,7 @@
private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
private final AccessibilityButtonTargetsObserver mAccessibilityButtonTargetsObserver;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final SecureSettings mSecureSettings;
private Context mContext;
@VisibleForTesting
@@ -85,11 +87,13 @@
public AccessibilityFloatingMenuController(Context context,
AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver,
AccessibilityButtonModeObserver accessibilityButtonModeObserver,
- KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecureSettings secureSettings) {
mContext = context;
mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver;
mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mSecureSettings = secureSettings;
mIsKeyguardVisible = false;
}
@@ -159,7 +163,7 @@
private void showFloatingMenu() {
if (mFloatingMenu == null) {
- mFloatingMenu = new AccessibilityFloatingMenu(mContext);
+ mFloatingMenu = new AccessibilityFloatingMenu(mContext, mSecureSettings);
}
mFloatingMenu.show();
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 7c2673c..da2e28c5 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -35,8 +35,10 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.settings.SecureSettings;
import javax.inject.Inject;
@@ -119,6 +121,8 @@
private final UiController mUiController;
protected final Lazy<SysUiState> mSysUiState;
protected final AssistLogger mAssistLogger;
+ private final UserTracker mUserTracker;
+ private final SecureSettings mSecureSettings;
private final DeviceProvisionedController mDeviceProvisionedController;
private final CommandQueue mCommandQueue;
@@ -135,7 +139,9 @@
Lazy<SysUiState> sysUiState,
DefaultUiController defaultUiController,
AssistLogger assistLogger,
- @Main Handler uiHandler) {
+ @Main Handler uiHandler,
+ UserTracker userTracker,
+ SecureSettings secureSettings) {
mContext = context;
mDeviceProvisionedController = controller;
mCommandQueue = commandQueue;
@@ -143,6 +149,8 @@
mAssistDisclosure = new AssistDisclosure(context, uiHandler);
mPhoneStateMonitor = phoneStateMonitor;
mAssistLogger = assistLogger;
+ mUserTracker = userTracker;
+ mSecureSettings = secureSettings;
registerVoiceInteractionSessionListener();
@@ -273,7 +281,7 @@
CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL | CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
false /* force */);
- boolean structureEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ boolean structureEnabled = mSecureSettings.getIntForUser(
Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
final SearchManager searchManager =
@@ -300,7 +308,7 @@
@Override
public void run() {
mContext.startActivityAsUser(intent, opts.toBundle(),
- new UserHandle(UserHandle.USER_CURRENT));
+ mUserTracker.getUserHandle());
}
});
} catch (ActivityNotFoundException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 3a9706d..3fd00ae 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -87,6 +87,7 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.SystemClock;
import java.io.PrintWriter;
@@ -151,6 +152,7 @@
@NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@Nullable private final TouchProcessor mTouchProcessor;
@NonNull private final AlternateBouncerInteractor mAlternateBouncerInteractor;
+ @NonNull private final SecureSettings mSecureSettings;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@@ -240,7 +242,7 @@
mLockscreenShadeTransitionController, mConfigurationController,
mKeyguardStateController,
mUnlockedScreenOffAnimationController,
- mUdfpsDisplayMode, requestId, reason, callback,
+ mUdfpsDisplayMode, mSecureSettings, requestId, reason, callback,
(view, event, fromUdfpsView) -> onTouch(requestId, event,
fromUdfpsView), mActivityLaunchAnimator, mFeatureFlags,
mPrimaryBouncerInteractor, mAlternateBouncerInteractor)));
@@ -723,7 +725,8 @@
@NonNull @BiometricsBackground Executor biometricsExecutor,
@NonNull PrimaryBouncerInteractor primaryBouncerInteractor,
@NonNull SinglePointerTouchProcessor singlePointerTouchProcessor,
- @NonNull AlternateBouncerInteractor alternateBouncerInteractor) {
+ @NonNull AlternateBouncerInteractor alternateBouncerInteractor,
+ @NonNull SecureSettings secureSettings) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -764,6 +767,7 @@
mBiometricExecutor = biometricsExecutor;
mPrimaryBouncerInteractor = primaryBouncerInteractor;
mAlternateBouncerInteractor = alternateBouncerInteractor;
+ mSecureSettings = secureSettings;
mTouchProcessor = mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
? singlePointerTouchProcessor : null;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index a3c4985..b6b5d26 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -60,6 +60,7 @@
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.settings.SecureSettings
private const val TAG = "UdfpsControllerOverlay"
@@ -89,6 +90,7 @@
private val keyguardStateController: KeyguardStateController,
private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider,
+ private val secureSettings: SecureSettings,
val requestId: Long,
@ShowReason val requestReason: Int,
private val controllerCallback: IUdfpsOverlayControllerCallback,
@@ -131,7 +133,7 @@
/** A helper if the [requestReason] was due to enrollment. */
val enrollHelper: UdfpsEnrollHelper? =
if (requestReason.isEnrollmentReason() && !shouldRemoveEnrollmentUi()) {
- UdfpsEnrollHelper(context, fingerprintManager, requestReason)
+ UdfpsEnrollHelper(context, fingerprintManager, secureSettings, requestReason)
} else {
null
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
index d5c763d3..cfa8ec5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -24,11 +24,12 @@
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
import android.util.TypedValue;
import android.view.accessibility.AccessibilityManager;
+import com.android.systemui.util.settings.SecureSettings;
+
import java.util.ArrayList;
import java.util.List;
@@ -51,8 +52,8 @@
void onLastStepAcquired();
}
- @NonNull private final Context mContext;
@NonNull private final FingerprintManager mFingerprintManager;
+ @NonNull private final SecureSettings mSecureSettings;
// IUdfpsOverlayController reason
private final int mEnrollReason;
private final boolean mAccessibilityEnabled;
@@ -70,10 +71,11 @@
@Nullable Listener mListener;
public UdfpsEnrollHelper(@NonNull Context context,
- @NonNull FingerprintManager fingerprintManager, int reason) {
+ @NonNull FingerprintManager fingerprintManager, SecureSettings secureSettings,
+ int reason) {
- mContext = context;
mFingerprintManager = fingerprintManager;
+ mSecureSettings = secureSettings;
mEnrollReason = reason;
final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
@@ -84,8 +86,7 @@
// Number of pixels per mm
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1,
context.getResources().getDisplayMetrics());
- boolean useNewCoords = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- NEW_COORDS_OVERRIDE, 0,
+ boolean useNewCoords = mSecureSettings.getIntForUser(NEW_COORDS_OVERRIDE, 0,
UserHandle.USER_CURRENT) != 0;
if (useNewCoords && (Build.IS_ENG || Build.IS_USERDEBUG)) {
Log.v(TAG, "Using new coordinates");
@@ -210,8 +211,7 @@
float scale = SCALE;
if (Build.IS_ENG || Build.IS_USERDEBUG) {
- scale = Settings.Secure.getFloatForUser(mContext.getContentResolver(),
- SCALE_OVERRIDE, SCALE,
+ scale = mSecureSettings.getFloatForUser(SCALE_OVERRIDE, SCALE,
UserHandle.USER_CURRENT);
}
final int index = mLocationsEnrolled - mCenterTouchCount;
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
index 58d40d3..4227a7a 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
@@ -25,13 +25,13 @@
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.os.RemoteException
-import android.os.UserHandle
import android.util.Log
import android.view.WindowManager
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.system.ActivityManagerKt.isInForeground
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -55,6 +55,7 @@
private val cameraIntents: CameraIntentsWrapper,
private val contentResolver: ContentResolver,
@Main private val uiExecutor: Executor,
+ private val userTracker: UserTracker
) {
/**
* Whether the camera application can be launched for the camera launch gesture.
@@ -111,7 +112,7 @@
Intent.FLAG_ACTIVITY_NEW_TASK,
null,
activityOptions.toBundle(),
- UserHandle.CURRENT.identifier,
+ userTracker.userId,
)
} catch (e: RemoteException) {
Log.w(
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt
index 867faf9..cc43e7e 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt
@@ -20,15 +20,13 @@
import android.content.Intent
import android.provider.MediaStore
import android.text.TextUtils
-
import com.android.systemui.R
class CameraIntents {
companion object {
- val DEFAULT_SECURE_CAMERA_INTENT_ACTION =
- MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE
- val DEFAULT_INSECURE_CAMERA_INTENT_ACTION =
- MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA
+ val DEFAULT_SECURE_CAMERA_INTENT_ACTION = MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE
+ val DEFAULT_INSECURE_CAMERA_INTENT_ACTION = MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA
+ private val VIDEO_CAMERA_INTENT_ACTION = MediaStore.INTENT_ACTION_VIDEO_CAMERA
const val EXTRA_LAUNCH_SOURCE = "com.android.systemui.camera_launch_source"
@JvmStatic
@@ -44,18 +42,14 @@
@JvmStatic
fun getInsecureCameraIntent(context: Context): Intent {
val intent = Intent(DEFAULT_INSECURE_CAMERA_INTENT_ACTION)
- getOverrideCameraPackage(context)?.let {
- intent.setPackage(it)
- }
+ getOverrideCameraPackage(context)?.let { intent.setPackage(it) }
return intent
}
@JvmStatic
fun getSecureCameraIntent(context: Context): Intent {
val intent = Intent(DEFAULT_SECURE_CAMERA_INTENT_ACTION)
- getOverrideCameraPackage(context)?.let {
- intent.setPackage(it)
- }
+ getOverrideCameraPackage(context)?.let { intent.setPackage(it) }
return intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
}
@@ -68,5 +62,11 @@
fun isInsecureCameraIntent(intent: Intent?): Boolean {
return intent?.getAction()?.equals(DEFAULT_INSECURE_CAMERA_INTENT_ACTION) ?: false
}
+
+ /** Returns an [Intent] that can be used to start the camera in video mode. */
+ @JvmStatic
+ fun getVideoCameraIntent(): Intent {
+ return Intent(VIDEO_CAMERA_INTENT_ACTION)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraIntentsWrapper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraIntentsWrapper.kt
index cf02f8f..a434617 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/CameraIntentsWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraIntentsWrapper.kt
@@ -21,7 +21,9 @@
import javax.inject.Inject
/** Injectable wrapper around [CameraIntents]. */
-class CameraIntentsWrapper @Inject constructor(
+class CameraIntentsWrapper
+@Inject
+constructor(
private val context: Context,
) {
@@ -40,4 +42,9 @@
fun getInsecureCameraIntent(): Intent {
return CameraIntents.getInsecureCameraIntent(context)
}
+
+ /** Returns an [Intent] that can be used to start the camera in video mode. */
+ fun getVideoCameraIntent(): Intent {
+ return CameraIntents.getVideoCameraIntent()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
index 1454210..fb0c0a6 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
@@ -20,7 +20,7 @@
import android.content.res.Configuration
import android.graphics.PixelFormat
import android.os.SystemProperties
-import android.util.DisplayMetrics
+import android.view.Surface
import android.view.View
import android.view.WindowManager
import com.android.internal.annotations.VisibleForTesting
@@ -36,7 +36,6 @@
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.leak.RotationUtils
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
import javax.inject.Inject
@@ -172,30 +171,28 @@
}
private fun layoutRipple() {
- val displayMetrics = DisplayMetrics()
- context.display.getRealMetrics(displayMetrics)
- val width = displayMetrics.widthPixels
- val height = displayMetrics.heightPixels
+ val bounds = windowManager.currentWindowMetrics.bounds
+ val width = bounds.width()
+ val height = bounds.height()
val maxDiameter = Integer.max(width, height) * 2f
rippleView.setMaxSize(maxDiameter, maxDiameter)
- when (RotationUtils.getExactRotation(context)) {
- RotationUtils.ROTATION_LANDSCAPE -> {
+ when (context.display.rotation) {
+ Surface.ROTATION_0 -> {
+ rippleView.setCenter(
+ width * normalizedPortPosX, height * normalizedPortPosY)
+ }
+ Surface.ROTATION_90 -> {
rippleView.setCenter(
width * normalizedPortPosY, height * (1 - normalizedPortPosX))
}
- RotationUtils.ROTATION_UPSIDE_DOWN -> {
+ Surface.ROTATION_180 -> {
rippleView.setCenter(
width * (1 - normalizedPortPosX), height * (1 - normalizedPortPosY))
}
- RotationUtils.ROTATION_SEASCAPE -> {
+ Surface.ROTATION_270 -> {
rippleView.setCenter(
width * (1 - normalizedPortPosY), height * normalizedPortPosX)
}
- else -> {
- // ROTATION_NONE
- rippleView.setCenter(
- width * normalizedPortPosX, height * normalizedPortPosY)
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
index 2a3d67f..c331164 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
@@ -17,12 +17,12 @@
package com.android.systemui.doze;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.os.UserHandle;
import android.util.Log;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeMachine.State;
import com.android.systemui.doze.dagger.DozeScope;
+import com.android.systemui.settings.UserTracker;
import java.io.PrintWriter;
@@ -40,14 +40,17 @@
private final AmbientDisplayConfiguration mConfig;
private DozeMachine mMachine;
private final DockManager mDockManager;
+ private final UserTracker mUserTracker;
private final DockEventListener mDockEventListener;
private int mDockState = DockManager.STATE_NONE;
@Inject
- DozeDockHandler(AmbientDisplayConfiguration config, DockManager dockManager) {
+ DozeDockHandler(AmbientDisplayConfiguration config, DockManager dockManager,
+ UserTracker userTracker) {
mConfig = config;
mDockManager = dockManager;
+ mUserTracker = userTracker;
mDockEventListener = new DockEventListener();
}
@@ -100,7 +103,7 @@
nextState = State.DOZE_AOD_DOCKED;
break;
case DockManager.STATE_NONE:
- nextState = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT) ? State.DOZE_AOD
+ nextState = mConfig.alwaysOnEnabled(mUserTracker.getUserId()) ? State.DOZE_AOD
: State.DOZE;
break;
case DockManager.STATE_DOCKED_HIDE:
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 96c35d4..fc3263f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -23,7 +23,6 @@
import android.content.res.Configuration;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Trace;
-import android.os.UserHandle;
import android.util.Log;
import android.view.Display;
@@ -33,6 +32,7 @@
import com.android.systemui.doze.dagger.WrappedService;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.Assert;
import com.android.systemui.util.wakelock.WakeLock;
@@ -149,6 +149,7 @@
private final DozeHost mDozeHost;
private final DockManager mDockManager;
private final Part[] mParts;
+ private final UserTracker mUserTracker;
private final ArrayList<State> mQueuedRequests = new ArrayList<>();
private State mState = State.UNINITIALIZED;
@@ -161,7 +162,7 @@
AmbientDisplayConfiguration ambientDisplayConfig,
WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle,
DozeLog dozeLog, DockManager dockManager,
- DozeHost dozeHost, Part[] parts) {
+ DozeHost dozeHost, Part[] parts, UserTracker userTracker) {
mDozeService = service;
mAmbientDisplayConfig = ambientDisplayConfig;
mWakefulnessLifecycle = wakefulnessLifecycle;
@@ -170,6 +171,7 @@
mDockManager = dockManager;
mDozeHost = dozeHost;
mParts = parts;
+ mUserTracker = userTracker;
for (Part part : parts) {
part.setDozeMachine(this);
}
@@ -429,7 +431,7 @@
nextState = State.FINISH;
} else if (mDockManager.isDocked()) {
nextState = mDockManager.isHidden() ? State.DOZE : State.DOZE_AOD_DOCKED;
- } else if (mAmbientDisplayConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) {
+ } else if (mAmbientDisplayConfig.alwaysOnEnabled(mUserTracker.getUserId())) {
nextState = State.DOZE_AOD;
} else {
nextState = State.DOZE;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 937884c..4cade77 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -43,6 +43,7 @@
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.settings.SystemSettings;
import java.io.PrintWriter;
import java.util.Objects;
@@ -78,6 +79,7 @@
private final DozeParameters mDozeParameters;
private final DevicePostureController mDevicePostureController;
private final DozeLog mDozeLog;
+ private final SystemSettings mSystemSettings;
private final int[] mSensorToBrightness;
private final int[] mSensorToScrimOpacity;
private final int mScreenBrightnessDim;
@@ -110,7 +112,8 @@
WakefulnessLifecycle wakefulnessLifecycle,
DozeParameters dozeParameters,
DevicePostureController devicePostureController,
- DozeLog dozeLog) {
+ DozeLog dozeLog,
+ SystemSettings systemSettings) {
mContext = context;
mDozeService = service;
mSensorManager = sensorManager;
@@ -122,6 +125,7 @@
mDozeHost = host;
mHandler = handler;
mDozeLog = dozeLog;
+ mSystemSettings = systemSettings;
mScreenBrightnessMinimumDimAmountFloat = context.getResources().getFloat(
R.dimen.config_screenBrightnessMinimumDimAmountFloat);
@@ -257,7 +261,7 @@
}
//TODO: brightnessfloat change usages to float.
private int clampToUserSetting(int brightness) {
- int userSetting = Settings.System.getIntForUser(mContext.getContentResolver(),
+ int userSetting = mSystemSettings.getIntForUser(
Settings.System.SCREEN_BRIGHTNESS, Integer.MAX_VALUE,
UserHandle.USER_CURRENT);
return Math.min(brightness, userSetting);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 2ef1262..e3c568a9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -252,7 +252,7 @@
new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
mConfig.wakeScreenGestureAvailable()
- && mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT),
+ && mConfig.alwaysOnEnabled(mUserTracker.getUserId()),
DozeLog.REASON_SENSOR_WAKE_UP_PRESENCE,
false /* reports touch coordinates */,
false /* touchscreen */
@@ -293,7 +293,7 @@
private boolean udfpsLongPressConfigured() {
return mUdfpsEnrolled
- && (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT) || mScreenOffUdfpsEnabled);
+ && (mConfig.alwaysOnEnabled(mUserTracker.getUserId()) || mScreenOffUdfpsEnabled);
}
private boolean quickPickUpConfigured() {
@@ -694,13 +694,13 @@
}
protected boolean enabledBySetting() {
- if (!mConfig.enabled(UserHandle.USER_CURRENT)) {
+ if (!mConfig.enabled(mUserTracker.getUserId())) {
return false;
} else if (TextUtils.isEmpty(mSetting)) {
return true;
}
return mSecureSettings.getIntForUser(mSetting, mSettingDefault ? 1 : 0,
- UserHandle.USER_CURRENT) != 0;
+ mUserTracker.getUserId()) != 0;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java
index e6d9865..de0bdd3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java
@@ -20,10 +20,10 @@
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.PowerManager;
-import android.os.UserHandle;
import android.text.TextUtils;
import com.android.systemui.doze.dagger.DozeScope;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import java.io.PrintWriter;
@@ -57,6 +57,7 @@
private final AmbientDisplayConfiguration mConfig;
private final DozeLog mDozeLog;
private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
+ private final UserTracker mUserTracker;
private boolean mIsCarModeEnabled = false;
@@ -65,11 +66,13 @@
DozeHost dozeHost,
AmbientDisplayConfiguration config,
DozeLog dozeLog,
- Lazy<BiometricUnlockController> biometricUnlockControllerLazy) {
+ Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
+ UserTracker userTracker) {
mDozeHost = dozeHost;
mConfig = config;
mDozeLog = dozeLog;
mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
+ mUserTracker = userTracker;
}
@Override
@@ -148,7 +151,7 @@
private void handleCarModeExited() {
mDozeLog.traceCarModeEnded();
- mMachine.requestState(mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)
+ mMachine.requestState(mConfig.alwaysOnEnabled(mUserTracker.getUserId())
? DozeMachine.State.DOZE_AOD : DozeMachine.State.DOZE);
}
@@ -166,7 +169,7 @@
if (mDozeHost.isPowerSaveActive()) {
nextState = DozeMachine.State.DOZE;
} else if (mMachine.getState() == DozeMachine.State.DOZE
- && mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) {
+ && mConfig.alwaysOnEnabled(mUserTracker.getUserId())) {
nextState = DozeMachine.State.DOZE_AOD;
}
@@ -181,7 +184,7 @@
// handles suppression changes, while DozeMachine#transitionPolicy handles gating
// transitions to DOZE_AOD
final DozeMachine.State nextState;
- if (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT) && !suppressed) {
+ if (mConfig.alwaysOnEnabled(mUserTracker.getUserId()) && !suppressed) {
nextState = DozeMachine.State.DOZE_AOD;
} else {
nextState = DozeMachine.State.DOZE;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index a9a9cae..dd01be0 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -208,6 +208,13 @@
});
}
+ @Override
+ public void onEndDream() {
+ mExecutor.execute(() -> {
+ resetCurrentDreamOverlayLocked();
+ });
+ }
+
private Lifecycle.State getCurrentStateLocked() {
return mLifecycleRegistry.getCurrentState();
}
@@ -291,6 +298,7 @@
mDreamOverlayContainerViewController = null;
mDreamOverlayTouchMonitor = null;
+ mWindow = null;
mStarted = false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index f1bb156..90c440c 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -25,7 +25,6 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
-import android.os.UserHandle;
import android.provider.Settings;
import android.text.format.DateFormat;
import android.util.PluralsMessageFormatter;
@@ -37,6 +36,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.statusbar.policy.NextAlarmController;
@@ -72,6 +72,7 @@
mDreamOverlayNotificationCountProvider;
private final ZenModeController mZenModeController;
private final DreamOverlayStateController mDreamOverlayStateController;
+ private final UserTracker mUserTracker;
private final StatusBarWindowStateController mStatusBarWindowStateController;
private final DreamOverlayStatusBarItemsProvider mStatusBarItemsProvider;
private final Executor mMainExecutor;
@@ -154,7 +155,8 @@
ZenModeController zenModeController,
StatusBarWindowStateController statusBarWindowStateController,
DreamOverlayStatusBarItemsProvider statusBarItemsProvider,
- DreamOverlayStateController dreamOverlayStateController) {
+ DreamOverlayStateController dreamOverlayStateController,
+ UserTracker userTracker) {
super(view);
mResources = resources;
mMainExecutor = mainExecutor;
@@ -169,6 +171,7 @@
mStatusBarItemsProvider = statusBarItemsProvider;
mZenModeController = zenModeController;
mDreamOverlayStateController = dreamOverlayStateController;
+ mUserTracker = userTracker;
// Register to receive show/hide updates for the system status bar. Our custom status bar
// needs to hide when the system status bar is showing to ovoid overlapping status bars.
@@ -259,7 +262,7 @@
private void updateAlarmStatusIcon() {
final AlarmManager.AlarmClockInfo alarm =
- mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
+ mAlarmManager.getNextAlarmClock(mUserTracker.getUserId());
final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
showIcon(
DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 012615b..ede4223 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -64,6 +64,10 @@
// TODO(b/259130119): Tracking Bug
val FSI_ON_DND_UPDATE = unreleasedFlag(259130119, "fsi_on_dnd_update", teamfood = true)
+ // TODO(b/265804648): Tracking Bug
+ @JvmField
+ val DISABLE_FSI = unreleasedFlag(265804648, "disable_fsi")
+
// TODO(b/254512538): Tracking Bug
val INSTANT_VOICE_REPLY = unreleasedFlag(111, "instant_voice_reply", teamfood = true)
@@ -100,7 +104,7 @@
unreleasedFlag(174148361, "notification_inline_reply_animation", teamfood = true)
val FILTER_UNSEEN_NOTIFS_ON_KEYGUARD =
- unreleasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
+ releasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
// TODO(b/263414400): Tracking Bug
@JvmField
@@ -212,6 +216,11 @@
val ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS =
unreleasedFlag(226, "enable_wallet_contextual_loyalty_cards", teamfood = false)
+ // TODO(b/242908637): Tracking Bug
+ @JvmField
+ val WALLPAPER_FULLSCREEN_PREVIEW =
+ unreleasedFlag(227, "wallpaper_fullscreen_preview", teamfood = true)
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -225,7 +234,8 @@
// TODO(b/258517050): Clean up after the feature is launched.
@JvmField
- val SMARTSPACE_DATE_WEATHER_DECOUPLED = unreleasedFlag(403, "smartspace_date_weather_decoupled")
+ val SMARTSPACE_DATE_WEATHER_DECOUPLED =
+ sysPropBooleanFlag(403, "persist.sysui.ss.dw_decoupled", default = false)
// 500 - quick settings
@@ -341,6 +351,10 @@
// TODO(b/263512203): Tracking Bug
val MEDIA_EXPLICIT_INDICATOR = unreleasedFlag(911, "media_explicit_indicator", teamfood = true)
+ // TODO(b/265813373): Tracking Bug
+ val MEDIA_TAP_TO_TRANSFER_DISMISS_GESTURE =
+ unreleasedFlag(912, "media_ttt_dismiss_gesture", teamfood = true)
+
// 1000 - dock
val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging")
@@ -456,6 +470,16 @@
val WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM =
unreleasedFlag(1206, "persist.wm.debug.predictive_back_bouncer_anim", teamfood = true)
+ // TODO(b/238475428): Tracking Bug
+ @JvmField
+ val WM_SHADE_ALLOW_BACK_GESTURE =
+ unreleasedFlag(1207, "persist.wm.debug.shade_allow_back_gesture", teamfood = false)
+
+ // TODO(b/238475428): Tracking Bug
+ @JvmField
+ val WM_SHADE_ANIMATE_BACK_GESTURE =
+ unreleasedFlag(1208, "persist.wm.debug.shade_animate_back_gesture", teamfood = true)
+
// 1300 - screenshots
// TODO(b/254513155): Tracking Bug
@JvmField
@@ -534,6 +558,13 @@
val OUTPUT_SWITCHER_SHOW_API_ENABLED =
unreleasedFlag(2503, "output_switcher_show_api_enabled", teamfood = true)
+ // 2700 - unfold transitions
+ // TODO(b/265764985): Tracking Bug
+ @Keep
+ @JvmField
+ val ENABLE_DARK_VIGNETTE_WHEN_FOLDING =
+ unreleasedFlag(2700, "enable_dark_vignette_when_folding")
+
// TODO(b259590361): Tracking bug
val EXPERIMENTAL_FLAG = unreleasedFlag(2, "exp_flag_release")
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index db2cd91..e80e71c 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -21,7 +21,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS;
-import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
@@ -901,7 +900,7 @@
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
EmergencyDialerConstants.ENTRY_TYPE_POWER_MENU);
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ mContext.startActivityAsUser(intent, mUserTracker.getUserHandle());
}
}
}
@@ -959,8 +958,7 @@
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
- mScreenshotHelper.takeScreenshot(TAKE_SCREENSHOT_FULLSCREEN,
- SCREENSHOT_GLOBAL_ACTIONS, mHandler, null);
+ mScreenshotHelper.takeScreenshot(SCREENSHOT_GLOBAL_ACTIONS, mHandler, null);
mMetricsLogger.action(MetricsEvent.ACTION_SCREENSHOT_POWER_MENU);
mUiEventLogger.log(GlobalActionsEvent.GA_SCREENSHOT_PRESS);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 4f1a2b3..ad7973e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -24,7 +24,6 @@
import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.hardware.input.InputManager;
@@ -53,6 +52,7 @@
import com.android.systemui.CoreStartable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.util.settings.SecureSettings;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -108,6 +108,7 @@
protected volatile Context mContext;
private final Provider<LocalBluetoothManager> mBluetoothManagerProvider;
+ private final SecureSettings mSecureSettings;
private boolean mEnabled;
private String mKeyboardName;
@@ -125,9 +126,11 @@
private int mState;
@Inject
- public KeyboardUI(Context context, Provider<LocalBluetoothManager> bluetoothManagerProvider) {
+ public KeyboardUI(Context context, Provider<LocalBluetoothManager> bluetoothManagerProvider,
+ SecureSettings secureSettings) {
mContext = context;
this.mBluetoothManagerProvider = bluetoothManagerProvider;
+ mSecureSettings = secureSettings;
}
@Override
@@ -298,9 +301,8 @@
}
private boolean isUserSetupComplete() {
- ContentResolver resolver = mContext.getContentResolver();
- return Secure.getIntForUser(
- resolver, Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+ return mSecureSettings.getIntForUser(
+ Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
}
private CachedBluetoothDevice getPairedKeyboard() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 9756cad..95bba13 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1334,7 +1334,7 @@
mHideAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.lock_screen_behind_enter);
- mWorkLockController = new WorkLockActivityController(mContext);
+ mWorkLockController = new WorkLockActivityController(mContext, mUserTracker);
}
@Override
@@ -2281,6 +2281,10 @@
}
if (!mKeyguardDonePending && mHideAnimationRun && !mHideAnimationRunning) {
handleKeyguardDone();
+ } else if (mSurfaceBehindRemoteAnimationRunning) {
+ // We're already running the keyguard exit animation, likely due to an in-progress swipe
+ // to unlock.
+ exitKeyguardAndFinishSurfaceBehindRemoteAnimation(false /* cancelled */);
} else if (!mHideAnimationRun) {
if (DEBUG) Log.d(TAG, "tryKeyguardDone: starting pre-hide animation");
mHideAnimationRun = true;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index 16817ed..b92499e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -26,10 +26,10 @@
import android.content.Intent;
import android.os.Bundle;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -37,16 +37,20 @@
private static final String TAG = WorkLockActivityController.class.getSimpleName();
private final Context mContext;
+ private final UserTracker mUserTracker;
private final IActivityTaskManager mIatm;
- public WorkLockActivityController(Context context) {
- this(context, TaskStackChangeListeners.getInstance(), ActivityTaskManager.getService());
+ public WorkLockActivityController(Context context, UserTracker userTracker) {
+ this(context, userTracker, TaskStackChangeListeners.getInstance(),
+ ActivityTaskManager.getService());
}
@VisibleForTesting
WorkLockActivityController(
- Context context, TaskStackChangeListeners tscl, IActivityTaskManager iAtm) {
+ Context context, UserTracker userTracker, TaskStackChangeListeners tscl,
+ IActivityTaskManager iAtm) {
mContext = context;
+ mUserTracker = userTracker;
mIatm = iAtm;
tscl.registerTaskStackListener(mLockListener);
@@ -65,7 +69,8 @@
options.setLaunchTaskId(info.taskId);
options.setTaskOverlay(true, false /* canResume */);
- final int result = startActivityAsUser(intent, options.toBundle(), UserHandle.USER_CURRENT);
+ final int result = startActivityAsUser(intent, options.toBundle(),
+ mUserTracker.getUserId());
if (ActivityManager.isStartResultSuccessful(result)) {
// OK
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
index ea5b4f4..76c2430 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
@@ -25,10 +25,12 @@
object BuiltInKeyguardQuickAffordanceKeys {
// Please keep alphabetical order of const names to simplify future maintenance.
const val CAMERA = "camera"
+ const val CREATE_NOTE = "create_note"
const val DO_NOT_DISTURB = "do_not_disturb"
const val FLASHLIGHT = "flashlight"
const val HOME_CONTROLS = "home"
const val QR_CODE_SCANNER = "qr_code_scanner"
const val QUICK_ACCESS_WALLET = "wallet"
+ const val VIDEO_CAMERA = "video_camera"
// Please keep alphabetical order of const names to simplify future maintenance.
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
index dbc376e..f6e6d6b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
@@ -26,6 +26,7 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.StatusBarState
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -46,7 +47,7 @@
get() = context.getString(R.string.accessibility_camera_button)
override val pickerIconResourceId: Int
- get() = com.android.internal.R.drawable.perm_group_camera
+ get() = R.drawable.ic_camera
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState>
get() =
@@ -54,12 +55,20 @@
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
icon =
Icon.Resource(
- com.android.internal.R.drawable.perm_group_camera,
+ R.drawable.ic_camera,
ContentDescription.Resource(R.string.accessibility_camera_button)
)
)
)
+ override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
+ return if (isLaunchable()) {
+ super.getPickerScreenState()
+ } else {
+ KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
+ }
+ }
+
override fun onTriggered(
expandable: Expandable?
): KeyguardQuickAffordanceConfig.OnTriggeredResult {
@@ -68,4 +77,8 @@
.launchCamera(StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE)
return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
}
+
+ private fun isLaunchable(): Boolean {
+ return cameraGestureHelper.get().canCameraGestureBeLaunched(StatusBarState.KEYGUARD)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
index ed1ff32..be73f85 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
@@ -128,7 +128,7 @@
awaitClose { controller.removeCallback(callback) }
},
secureSettings
- .observerFlow(Settings.Secure.ZEN_DURATION)
+ .observerFlow(userTracker.userId, Settings.Secure.ZEN_DURATION)
.onStart { emit(Unit) }
.map { secureSettings.getInt(Settings.Secure.ZEN_DURATION, ZEN_MODE_OFF) }
.flowOn(backgroundDispatcher)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
index 71d01eb..a1cce5c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
@@ -39,6 +39,7 @@
quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig,
camera: CameraQuickAffordanceConfig,
+ videoCamera: VideoCameraQuickAffordanceConfig,
): Set<KeyguardQuickAffordanceConfig> {
return setOf(
camera,
@@ -47,6 +48,7 @@
home,
quickAccessWallet,
qrCodeScanner,
+ videoCamera,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index 680c06b..4ba2eb9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -100,9 +100,9 @@
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
return when {
- !walletController.isWalletEnabled ->
+ !walletController.walletClient.isWalletServiceAvailable ->
KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
- walletController.walletClient.tileIcon == null || queryCards().isEmpty() -> {
+ !walletController.isWalletEnabled || queryCards().isEmpty() -> {
val componentName =
walletController.walletClient.createWalletSettingsIntent().toComponentName()
val actionText =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
new file mode 100644
index 0000000..d9ec3b1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
@@ -0,0 +1,105 @@
+/*
+ * 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.keyguard.data.quickaffordance
+
+import android.app.StatusBarManager
+import android.content.Context
+import android.content.Intent
+import com.android.systemui.ActivityIntentHelper
+import com.android.systemui.R
+import com.android.systemui.animation.Expandable
+import com.android.systemui.camera.CameraIntents
+import com.android.systemui.camera.CameraIntentsWrapper
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.settings.UserTracker
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+@SysUISingleton
+class VideoCameraQuickAffordanceConfig
+@Inject
+constructor(
+ @Application private val context: Context,
+ private val cameraIntents: CameraIntentsWrapper,
+ private val activityIntentHelper: ActivityIntentHelper,
+ private val userTracker: UserTracker,
+) : KeyguardQuickAffordanceConfig {
+
+ private val intent: Intent by lazy {
+ cameraIntents.getVideoCameraIntent().apply {
+ putExtra(
+ CameraIntents.EXTRA_LAUNCH_SOURCE,
+ StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE,
+ )
+ }
+ }
+
+ override val key: String
+ get() = BuiltInKeyguardQuickAffordanceKeys.VIDEO_CAMERA
+
+ override val pickerName: String
+ get() = context.getString(R.string.video_camera)
+
+ override val pickerIconResourceId: Int
+ get() = R.drawable.ic_videocam
+
+ override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState>
+ get() =
+ flowOf(
+ if (isLaunchable()) {
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+ icon =
+ Icon.Resource(
+ R.drawable.ic_videocam,
+ ContentDescription.Resource(R.string.video_camera)
+ )
+ )
+ } else {
+ KeyguardQuickAffordanceConfig.LockScreenState.Hidden
+ }
+ )
+
+ override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
+ return if (isLaunchable()) {
+ super.getPickerScreenState()
+ } else {
+ KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
+ }
+ }
+
+ override fun onTriggered(
+ expandable: Expandable?
+ ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity(
+ intent = intent,
+ canShowWhileLocked = false,
+ )
+ }
+
+ private fun isLaunchable(): Boolean {
+ return activityIntentHelper.getTargetActivityInfo(
+ intent,
+ userTracker.userId,
+ true,
+ ) != null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
new file mode 100644
index 0000000..08edbc6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.keyguard.data.repository
+
+import android.hardware.biometrics.BiometricSourceType
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/** Encapsulates state about device entry fingerprint auth mechanism. */
+interface DeviceEntryFingerprintAuthRepository {
+ /** Whether the device entry fingerprint auth is locked out. */
+ val isLockedOut: Flow<Boolean>
+}
+
+/**
+ * Implementation of [DeviceEntryFingerprintAuthRepository] that uses [KeyguardUpdateMonitor] as the
+ * source of truth.
+ *
+ * Dependency on [KeyguardUpdateMonitor] will be removed once fingerprint auth state is moved out of
+ * [KeyguardUpdateMonitor]
+ */
+@SysUISingleton
+class DeviceEntryFingerprintAuthRepositoryImpl
+@Inject
+constructor(
+ val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+) : DeviceEntryFingerprintAuthRepository {
+
+ override val isLockedOut: Flow<Boolean> = conflatedCallbackFlow {
+ val sendLockoutUpdate =
+ fun() {
+ trySendWithFailureLogging(
+ keyguardUpdateMonitor.isFingerprintLockedOut,
+ TAG,
+ "onLockedOutStateChanged"
+ )
+ }
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onLockedOutStateChanged(biometricSourceType: BiometricSourceType?) {
+ if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
+ sendLockoutUpdate()
+ }
+ }
+ }
+ keyguardUpdateMonitor.registerCallback(callback)
+ sendLockoutUpdate()
+ awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+ }
+
+ companion object {
+ const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt
new file mode 100644
index 0000000..d90f328
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.keyguard.data.repository
+
+import android.app.trust.TrustManager
+import com.android.keyguard.logging.TrustRepositoryLogger
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+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.keyguard.shared.model.TrustModel
+import com.android.systemui.user.data.repository.UserRepository
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
+
+/** Encapsulates any state relevant to trust agents and trust grants. */
+interface TrustRepository {
+ /** Flow representing whether the current user is trusted. */
+ val isCurrentUserTrusted: Flow<Boolean>
+}
+
+@SysUISingleton
+class TrustRepositoryImpl
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val userRepository: UserRepository,
+ private val trustManager: TrustManager,
+ private val logger: TrustRepositoryLogger,
+) : TrustRepository {
+ private val latestTrustModelForUser = mutableMapOf<Int, TrustModel>()
+
+ private val trust =
+ conflatedCallbackFlow {
+ val callback =
+ object : TrustManager.TrustListener {
+ override fun onTrustChanged(
+ enabled: Boolean,
+ newlyUnlocked: Boolean,
+ userId: Int,
+ flags: Int,
+ grantMsgs: List<String>?
+ ) {
+ logger.onTrustChanged(enabled, newlyUnlocked, userId, flags, grantMsgs)
+ trySendWithFailureLogging(
+ TrustModel(enabled, userId),
+ TrustRepositoryLogger.TAG,
+ "onTrustChanged"
+ )
+ }
+
+ override fun onTrustError(message: CharSequence?) = Unit
+
+ override fun onTrustManagedChanged(enabled: Boolean, userId: Int) = Unit
+ }
+ trustManager.registerTrustListener(callback)
+ logger.trustListenerRegistered()
+ awaitClose {
+ logger.trustListenerUnregistered()
+ trustManager.unregisterTrustListener(callback)
+ }
+ }
+ .onEach {
+ latestTrustModelForUser[it.userId] = it
+ logger.trustModelEmitted(it)
+ }
+ .shareIn(applicationScope, started = SharingStarted.Eagerly, replay = 1)
+
+ override val isCurrentUserTrusted: Flow<Boolean>
+ get() =
+ combine(trust, userRepository.selectedUserInfo, ::Pair)
+ .map { latestTrustModelForUser[it.second.id]?.isTrusted ?: false }
+ .distinctUntilChanged()
+ .onEach { logger.isCurrentUserTrusted(it) }
+ .onStart { emit(false) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 7134ec0..81a5828 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -31,8 +31,10 @@
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@SysUISingleton
@@ -87,6 +89,9 @@
private fun listenForDreamingToOccluded() {
scope.launch {
keyguardInteractor.isDreaming
+ // Add a slight delay, as dreaming and occluded events will arrive with a small gap
+ // in time. This prevents a transition to OCCLUSION happening prematurely.
+ .onEach { delay(50) }
.sample(
combine(
keyguardInteractor.isKeyguardOccluded,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index 9203a9b..14f918d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -50,10 +50,9 @@
private fun listenForGoneToDreaming() {
scope.launch {
keyguardInteractor.isAbleToDream
- .sample(keyguardTransitionInteractor.finishedKeyguardState, ::Pair)
- .collect { pair ->
- val (isAbleToDream, keyguardState) = pair
- if (isAbleToDream && keyguardState == KeyguardState.GONE) {
+ .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
+ .collect { (isAbleToDream, lastStartedStep) ->
+ if (isAbleToDream && lastStartedStep.to == KeyguardState.GONE) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
@@ -72,15 +71,15 @@
keyguardInteractor.wakefulnessModel
.sample(
combine(
- keyguardTransitionInteractor.finishedKeyguardState,
+ keyguardTransitionInteractor.startedKeyguardTransitionStep,
keyguardInteractor.isAodAvailable,
::Pair
),
::toTriple
)
- .collect { (wakefulnessState, keyguardState, isAodAvailable) ->
+ .collect { (wakefulnessState, lastStartedStep, isAodAvailable) ->
if (
- keyguardState == KeyguardState.GONE &&
+ lastStartedStep.to == KeyguardState.GONE &&
wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP
) {
keyguardTransitionRepository.startTransition(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index c219380..9ddc575 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -363,6 +363,10 @@
name = Contract.FlagsTable.FLAG_NAME_CUSTOM_CLOCKS_ENABLED,
value = featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
),
+ KeyguardPickerFlag(
+ name = Contract.FlagsTable.FLAG_NAME_WALLPAPER_FULLSCREEN_PREVIEW,
+ value = featureFlags.isEnabled(Flags.WALLPAPER_FULLSCREEN_PREVIEW),
+ ),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TrustModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TrustModel.kt
new file mode 100644
index 0000000..4fd14b1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TrustModel.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.keyguard.shared.model
+
+/** Represents the trust state */
+data class TrustModel(
+ /** If true, the system believes the environment to be trusted. */
+ val isTrusted: Boolean,
+ /** The user, for which the trust changed. */
+ val userId: Int,
+)
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 d7817e1..15306f9 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -191,15 +191,12 @@
false /* systrace */);
}
- /**
- * Provides a logging buffer for logs related to swiping away the status bar while in immersive
- * mode. See {@link com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureLogger}.
- */
+ /** Provides a logging buffer for logs related to swipe up gestures. */
@Provides
@SysUISingleton
- @SwipeStatusBarAwayLog
- public static LogBuffer provideSwipeAwayGestureLogBuffer(LogBufferFactory factory) {
- return factory.create("SwipeStatusBarAwayLog", 30);
+ @SwipeUpLog
+ public static LogBuffer provideSwipeUpLogBuffer(LogBufferFactory factory) {
+ return factory.create("SwipeUpLog", 30);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/SwipeStatusBarAwayLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/SwipeUpLog.java
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/log/dagger/SwipeStatusBarAwayLog.java
rename to packages/SystemUI/src/com/android/systemui/log/dagger/SwipeUpLog.java
index 4c276e2..d58b538 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/SwipeStatusBarAwayLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/SwipeUpLog.java
@@ -27,10 +27,10 @@
/**
* A {@link LogBuffer} for
- * {@link com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureLogger}.
+ * {@link com.android.systemui.statusbar.gesture.SwipeUpGestureLogger}.
*/
@Qualifier
@Documented
@Retention(RUNTIME)
-public @interface SwipeStatusBarAwayLog {
+public @interface SwipeUpLog {
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
index 93be6a7..4827a16 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
@@ -162,8 +162,8 @@
context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
UI_MODE_NIGHT_YES
)
- colorScheme.accent1[2]
- else colorScheme.accent1[3]
+ colorScheme.accent1.s100
+ else colorScheme.accent1.s200
},
{ seamlessColor: Int ->
val accentColorList = ColorStateList.valueOf(seamlessColor)
@@ -230,7 +230,14 @@
fun updateColorScheme(colorScheme: ColorScheme?): Boolean {
var anyChanged = false
- colorTransitions.forEach { anyChanged = it.updateColorScheme(colorScheme) || anyChanged }
+ colorTransitions.forEach {
+ val isChanged = it.updateColorScheme(colorScheme)
+
+ // Ignore changes to colorSeamless, since that is expected when toggling dark mode
+ if (it == colorSeamless) return@forEach
+
+ anyChanged = isChanged || anyChanged
+ }
colorScheme?.let { mediaViewHolder.gutsViewHolder.colorScheme = colorScheme }
return anyChanged
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaColorSchemes.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaColorSchemes.kt
index 82abf9b..2a8362b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaColorSchemes.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaColorSchemes.kt
@@ -19,28 +19,28 @@
import com.android.systemui.monet.ColorScheme
/** Returns the surface color for media controls based on the scheme. */
-internal fun surfaceFromScheme(scheme: ColorScheme) = scheme.accent2[9] // A2-800
+internal fun surfaceFromScheme(scheme: ColorScheme) = scheme.accent2.s800 // A2-800
/** Returns the primary accent color for media controls based on the scheme. */
-internal fun accentPrimaryFromScheme(scheme: ColorScheme) = scheme.accent1[2] // A1-100
+internal fun accentPrimaryFromScheme(scheme: ColorScheme) = scheme.accent1.s100 // A1-100
/** Returns the secondary accent color for media controls based on the scheme. */
-internal fun accentSecondaryFromScheme(scheme: ColorScheme) = scheme.accent1[3] // A1-200
+internal fun accentSecondaryFromScheme(scheme: ColorScheme) = scheme.accent1.s200 // A1-200
/** Returns the primary text color for media controls based on the scheme. */
-internal fun textPrimaryFromScheme(scheme: ColorScheme) = scheme.neutral1[1] // N1-50
+internal fun textPrimaryFromScheme(scheme: ColorScheme) = scheme.neutral1.s50 // N1-50
/** Returns the inverse of the primary text color for media controls based on the scheme. */
-internal fun textPrimaryInverseFromScheme(scheme: ColorScheme) = scheme.neutral1[10] // N1-900
+internal fun textPrimaryInverseFromScheme(scheme: ColorScheme) = scheme.neutral1.s900 // N1-900
/** Returns the secondary text color for media controls based on the scheme. */
-internal fun textSecondaryFromScheme(scheme: ColorScheme) = scheme.neutral2[3] // N2-200
+internal fun textSecondaryFromScheme(scheme: ColorScheme) = scheme.neutral2.s200 // N2-200
/** Returns the tertiary text color for media controls based on the scheme. */
-internal fun textTertiaryFromScheme(scheme: ColorScheme) = scheme.neutral2[5] // N2-400
+internal fun textTertiaryFromScheme(scheme: ColorScheme) = scheme.neutral2.s400 // N2-400
/** Returns the color for the start of the background gradient based on the scheme. */
-internal fun backgroundStartFromScheme(scheme: ColorScheme) = scheme.accent2[8] // A2-700
+internal fun backgroundStartFromScheme(scheme: ColorScheme) = scheme.accent2.s700 // A2-700
/** Returns the color for the end of the background gradient based on the scheme. */
-internal fun backgroundEndFromScheme(scheme: ColorScheme) = scheme.accent1[8] // A1-700
+internal fun backgroundEndFromScheme(scheme: ColorScheme) = scheme.accent1.s700 // A1-700
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index a9e1a4d..4803371 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -95,6 +95,7 @@
private RecyclerView mDevicesRecyclerView;
private LinearLayout mDeviceListLayout;
private LinearLayout mCastAppLayout;
+ private LinearLayout mMediaMetadataSectionLayout;
private Button mDoneButton;
private Button mStopButton;
private Button mAppButton;
@@ -240,6 +241,7 @@
mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);
mHeaderIcon = mDialogView.requireViewById(R.id.header_icon);
mDevicesRecyclerView = mDialogView.requireViewById(R.id.list_result);
+ mMediaMetadataSectionLayout = mDialogView.requireViewById(R.id.media_metadata_section);
mDeviceListLayout = mDialogView.requireViewById(R.id.device_list);
mDoneButton = mDialogView.requireViewById(R.id.done);
mStopButton = mDialogView.requireViewById(R.id.stop);
@@ -255,21 +257,17 @@
mDevicesRecyclerView.setLayoutManager(mLayoutManager);
mDevicesRecyclerView.setAdapter(mAdapter);
mDevicesRecyclerView.setHasFixedSize(false);
- // Init header icon
- mHeaderIcon.setOnClickListener(v -> onHeaderIconClick());
// Init bottom buttons
mDoneButton.setOnClickListener(v -> dismiss());
mStopButton.setOnClickListener(v -> {
mMediaOutputController.releaseSession();
dismiss();
});
- mAppButton.setOnClickListener(v -> {
- mBroadcastSender.closeSystemDialogs();
- if (mMediaOutputController.getAppLaunchIntent() != null) {
- mContext.startActivity(mMediaOutputController.getAppLaunchIntent());
- }
- dismiss();
- });
+ mAppButton.setOnClickListener(v -> mMediaOutputController.tryToLaunchMediaApplication());
+ if (mMediaOutputController.isAdvancedLayoutSupported()) {
+ mMediaMetadataSectionLayout.setOnClickListener(
+ v -> mMediaOutputController.tryToLaunchMediaApplication());
+ }
}
@Override
@@ -560,7 +558,7 @@
@Override
public void dismissDialog() {
- dismiss();
+ mBroadcastSender.closeSystemDialogs();
}
void onHeaderIconClick() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 7bc0c0c..f95da27 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -382,6 +382,15 @@
return mContext.getPackageManager().getLaunchIntentForPackage(mPackageName);
}
+ void tryToLaunchMediaApplication() {
+ Intent launchIntent = getAppLaunchIntent();
+ if (launchIntent != null) {
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mCallback.dismissDialog();
+ mContext.startActivity(launchIntent);
+ }
+ }
+
CharSequence getHeaderTitle() {
if (mMediaController != null) {
final MediaMetadata metadata = mMediaController.getMetadata();
@@ -493,20 +502,20 @@
ColorScheme mCurrentColorScheme = new ColorScheme(wallpaperColors,
isDarkTheme);
if (isDarkTheme) {
- mColorItemContent = mCurrentColorScheme.getAccent1().get(2); // A1-100
- mColorSeekbarProgress = mCurrentColorScheme.getAccent2().get(7); // A2-600
- mColorButtonBackground = mCurrentColorScheme.getAccent1().get(4); // A1-300
- mColorItemBackground = mCurrentColorScheme.getNeutral2().get(9); // N2-800
- mColorConnectedItemBackground = mCurrentColorScheme.getAccent2().get(9); // A2-800
- mColorPositiveButtonText = mCurrentColorScheme.getAccent2().get(9); // A2-800
- mColorDialogBackground = mCurrentColorScheme.getNeutral1().get(10); // N1-900
+ mColorItemContent = mCurrentColorScheme.getAccent1().getS100(); // A1-100
+ mColorSeekbarProgress = mCurrentColorScheme.getAccent2().getS600(); // A2-600
+ mColorButtonBackground = mCurrentColorScheme.getAccent1().getS300(); // A1-300
+ mColorItemBackground = mCurrentColorScheme.getNeutral2().getS800(); // N2-800
+ mColorConnectedItemBackground = mCurrentColorScheme.getAccent2().getS800(); // A2-800
+ mColorPositiveButtonText = mCurrentColorScheme.getAccent2().getS800(); // A2-800
+ mColorDialogBackground = mCurrentColorScheme.getNeutral1().getS900(); // N1-900
} else {
- mColorItemContent = mCurrentColorScheme.getAccent1().get(9); // A1-800
- mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(4); // A1-300
- mColorButtonBackground = mCurrentColorScheme.getAccent1().get(7); // A1-600
- mColorItemBackground = mCurrentColorScheme.getAccent2().get(1); // A2-50
- mColorConnectedItemBackground = mCurrentColorScheme.getAccent1().get(2); // A1-100
- mColorPositiveButtonText = mCurrentColorScheme.getNeutral1().get(1); // N1-50
+ mColorItemContent = mCurrentColorScheme.getAccent1().getS800(); // A1-800
+ mColorSeekbarProgress = mCurrentColorScheme.getAccent1().getS300(); // A1-300
+ mColorButtonBackground = mCurrentColorScheme.getAccent1().getS600(); // A1-600
+ mColorItemBackground = mCurrentColorScheme.getAccent2().getS50(); // A2-50
+ mColorConnectedItemBackground = mCurrentColorScheme.getAccent1().getS100(); // A1-100
+ mColorPositiveButtonText = mCurrentColorScheme.getNeutral1().getS50(); // N1-50
mColorDialogBackground = mCurrentColorScheme.getBackgroundColor();
}
}
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..60504e4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
@@ -30,4 +30,8 @@
/** Check whether the flag for the receiver success state is enabled. */
fun isMediaTttReceiverSuccessRippleEnabled(): Boolean =
featureFlags.isEnabled(Flags.MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE)
+
+ /** True if the media transfer chip can be dismissed via a gesture. */
+ fun isMediaTttDismissGestureEnabled(): Boolean =
+ featureFlags.isEnabled(Flags.MEDIA_TAP_TO_TRANSFER_DISMISS_GESTURE)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
index 935f38d..902a10a0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
@@ -30,6 +30,7 @@
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
import com.android.systemui.temporarydisplay.ViewPriority
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.android.systemui.temporarydisplay.chipbar.ChipbarEndItem
@@ -54,6 +55,7 @@
private var displayedState: ChipStateSender? = null
// A map to store current chip state per id.
+ // TODO(b/265455911): Log whenever we add or remove from the store.
private var stateMap: MutableMap<String, ChipStateSender> = mutableMapOf()
private val commandQueueCallbacks =
@@ -102,10 +104,9 @@
}
uiEventLogger.logSenderStateChange(chipState)
- stateMap.put(routeInfo.id, chipState)
if (chipState == ChipStateSender.FAR_FROM_RECEIVER) {
// No need to store the state since it is the default state
- stateMap.remove(routeInfo.id)
+ removeIdFromStore(routeInfo.id)
// Return early if we're not displaying a chip anyway
val currentDisplayedState = displayedState ?: return
@@ -126,7 +127,9 @@
displayedState = null
chipbarCoordinator.removeView(routeInfo.id, removalReason)
} else {
+ stateMap[routeInfo.id] = chipState
displayedState = chipState
+ chipbarCoordinator.registerListener(displayListener)
chipbarCoordinator.displayView(
createChipbarInfo(
chipState,
@@ -135,7 +138,7 @@
context,
logger,
)
- ) { stateMap.remove(routeInfo.id) }
+ )
}
}
@@ -182,6 +185,7 @@
}
},
vibrationEffect = chipStateSender.transferStatus.vibrationEffect,
+ allowSwipeToDismiss = true,
windowTitle = MediaTttUtils.WINDOW_TITLE_SENDER,
wakeReason = MediaTttUtils.WAKE_REASON_SENDER,
timeoutMs = chipStateSender.timeout,
@@ -225,4 +229,14 @@
onClickListener,
)
}
+
+ private val displayListener =
+ TemporaryViewDisplayController.Listener { id -> removeIdFromStore(id) }
+
+ private fun removeIdFromStore(id: String) {
+ stateMap.remove(id)
+ if (stateMap.isEmpty()) {
+ chipbarCoordinator.unregisterListener(displayListener)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index a92203c..1121e160 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -297,7 +297,7 @@
private void updateAssistantAvailability() {
boolean assistantAvailableForUser = mAssistManagerLazy.get()
- .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
+ .getAssistInfoForUser(mUserTracker.getUserId()) != null;
boolean longPressDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 61bb858..5993e2e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -72,7 +72,6 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.telecom.TelecomManager;
import android.text.TextUtils;
@@ -1477,7 +1476,7 @@
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
final String chooserClassName = AccessibilityButtonChooserActivity.class.getName();
intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ mContext.startActivityAsUser(intent, mUserTracker.getUserHandle());
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 5d43c5d..dce69bb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -24,7 +24,6 @@
import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -63,8 +62,8 @@
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
@@ -87,6 +86,7 @@
private final Handler mHandler;
private final NavigationBarComponent.Factory mNavigationBarComponentFactory;
private FeatureFlags mFeatureFlags;
+ private final SecureSettings mSecureSettings;
private final DisplayManager mDisplayManager;
private final TaskbarDelegate mTaskbarDelegate;
private int mNavMode;
@@ -118,11 +118,13 @@
TaskStackChangeListeners taskStackChangeListeners,
Optional<Pip> pipOptional,
Optional<BackAnimation> backAnimation,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ SecureSettings secureSettings) {
mContext = context;
mHandler = mainHandler;
mNavigationBarComponentFactory = navigationBarComponentFactory;
mFeatureFlags = featureFlags;
+ mSecureSettings = secureSettings;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
commandQueue.addCallback(this);
configurationController.addCallback(this);
@@ -192,8 +194,7 @@
}
private void updateAccessibilityButtonModeIfNeeded() {
- ContentResolver contentResolver = mContext.getContentResolver();
- final int mode = Settings.Secure.getIntForUser(contentResolver,
+ final int mode = mSecureSettings.getIntForUser(
Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
@@ -207,14 +208,14 @@
// force update to ACCESSIBILITY_BUTTON_MODE_GESTURE.
if (QuickStepContract.isGesturalMode(mNavMode)
&& mode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR) {
- Settings.Secure.putIntForUser(contentResolver,
+ mSecureSettings.putIntForUser(
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_GESTURE,
UserHandle.USER_CURRENT);
// ACCESSIBILITY_BUTTON_MODE_GESTURE is incompatible under non gestural mode. Need to
// force update to ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR.
} else if (!QuickStepContract.isGesturalMode(mNavMode)
&& mode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
- Settings.Secure.putIntForUser(contentResolver,
+ mSecureSettings.putIntForUser(
Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt
index 26e3f49..4b10d69 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt
@@ -72,6 +72,9 @@
companion object {
// TODO(b/254606432): Use Intent.ACTION_CREATE_NOTE instead.
const val ACTION_CREATE_NOTE = "android.intent.action.CREATE_NOTE"
+
+ // TODO(b/265912743): Use RoleManager.NOTES_ROLE instead.
+ const val NOTE_ROLE = "android.app.role.NOTES"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index 8bdf319..22ce121 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -18,11 +18,13 @@
import android.app.Activity
import android.app.KeyguardManager
+import android.app.role.RoleManager
import android.content.Context
import android.os.UserManager
import androidx.core.content.getSystemService
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.notetask.quickaffordance.NoteTaskQuickAffordanceModule
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
import dagger.Binds
@@ -33,20 +35,25 @@
import java.util.Optional
/** Compose all dependencies required by Note Task feature. */
-@Module
+@Module(includes = [NoteTaskQuickAffordanceModule::class])
internal interface NoteTaskModule {
@[Binds IntoMap ClassKey(LaunchNoteTaskActivity::class)]
- fun bindNoteTaskLauncherActivity(activity: LaunchNoteTaskActivity): Activity?
+ fun LaunchNoteTaskActivity.bindNoteTaskLauncherActivity(): Activity
@[Binds IntoMap ClassKey(CreateNoteTaskShortcutActivity::class)]
- fun bindNoteTaskShortcutActivity(activity: CreateNoteTaskShortcutActivity): Activity?
+ fun CreateNoteTaskShortcutActivity.bindNoteTaskShortcutActivity(): Activity
companion object {
@[Provides NoteTaskEnabledKey]
- fun provideIsNoteTaskEnabled(featureFlags: FeatureFlags): Boolean {
- return featureFlags.isEnabled(Flags.NOTE_TASKS)
+ fun provideIsNoteTaskEnabled(
+ featureFlags: FeatureFlags,
+ roleManager: RoleManager,
+ ): Boolean {
+ val isRoleAvailable = roleManager.isRoleAvailable(NoteTaskIntentResolver.NOTE_ROLE)
+ val isFeatureEnabled = featureFlags.isEnabled(Flags.NOTE_TASKS)
+ return isRoleAvailable && isFeatureEnabled
}
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
new file mode 100644
index 0000000..cfbaa48
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
@@ -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.systemui.notetask.quickaffordance
+
+import android.content.Context
+import com.android.systemui.R
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.PickerScreenState
+import com.android.systemui.notetask.NoteTaskController
+import com.android.systemui.notetask.NoteTaskEnabledKey
+import javax.inject.Inject
+import kotlinx.coroutines.flow.flowOf
+
+internal class NoteTaskQuickAffordanceConfig
+@Inject
+constructor(
+ context: Context,
+ private val noteTaskController: NoteTaskController,
+ @NoteTaskEnabledKey private val isEnabled: Boolean,
+) : KeyguardQuickAffordanceConfig {
+
+ override val key = BuiltInKeyguardQuickAffordanceKeys.CREATE_NOTE
+
+ override val pickerName: String = context.getString(R.string.note_task_button_label)
+
+ override val pickerIconResourceId = R.drawable.ic_note_task_shortcut_keyguard
+
+ override val lockScreenState = flowOf(getLockScreenState())
+
+ // TODO(b/265949213)
+ private fun getLockScreenState() =
+ if (isEnabled) {
+ val icon = Icon.Resource(pickerIconResourceId, ContentDescription.Loaded(pickerName))
+ LockScreenState.Visible(icon)
+ } else {
+ LockScreenState.Hidden
+ }
+
+ override suspend fun getPickerScreenState() =
+ if (isEnabled) {
+ PickerScreenState.Default()
+ } else {
+ PickerScreenState.UnavailableOnDevice
+ }
+
+ override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
+ noteTaskController.showNoteTask()
+ return OnTriggeredResult.Handled
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt
new file mode 100644
index 0000000..7cb932a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.notetask.quickaffordance
+
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+
+@Module
+internal interface NoteTaskQuickAffordanceModule {
+
+ @[Binds IntoSet]
+ fun NoteTaskQuickAffordanceConfig.bindNoteTaskQuickAffordance(): KeyguardQuickAffordanceConfig
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
index f6a623e..6ab0da6 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
@@ -46,7 +46,7 @@
id = SHORTCUT_ID,
shortLabel = getString(R.string.note_task_button_label),
intent = LaunchNoteTaskActivity.newIntent(context = this),
- iconResource = R.drawable.ic_note_task_button,
+ iconResource = R.drawable.ic_note_task_shortcut_widget,
)
setResult(Activity.RESULT_OK, intent)
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 90fc1d7..595b882 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -68,6 +68,7 @@
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.NotificationChannels;
@@ -175,7 +176,7 @@
private ActivityStarter mActivityStarter;
private final BroadcastSender mBroadcastSender;
private final UiEventLogger mUiEventLogger;
-
+ private final UserTracker mUserTracker;
private final Lazy<BatteryController> mBatteryControllerLazy;
private final DialogLaunchAnimator mDialogLaunchAnimator;
@@ -184,7 +185,8 @@
@Inject
public PowerNotificationWarnings(Context context, ActivityStarter activityStarter,
BroadcastSender broadcastSender, Lazy<BatteryController> batteryControllerLazy,
- DialogLaunchAnimator dialogLaunchAnimator, UiEventLogger uiEventLogger) {
+ DialogLaunchAnimator dialogLaunchAnimator, UiEventLogger uiEventLogger,
+ UserTracker userTracker) {
mContext = context;
mNoMan = mContext.getSystemService(NotificationManager.class);
mPowerMan = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -196,6 +198,7 @@
mDialogLaunchAnimator = dialogLaunchAnimator;
mUseSevereDialog = mContext.getResources().getBoolean(R.bool.config_severe_battery_dialog);
mUiEventLogger = uiEventLogger;
+ mUserTracker = userTracker;
}
@Override
@@ -692,7 +695,7 @@
Secure.putIntForUser(
resolver,
Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
- 1, UserHandle.USER_CURRENT);
+ 1, mUserTracker.getUserId());
});
} else {
d.setTitle(R.string.battery_saver_confirmation_title);
@@ -843,7 +846,8 @@
logEvent(BatteryWarningEvents
.LowBatteryWarningEvent.LOW_BATTERY_NOTIFICATION_SETTINGS);
dismissLowBatteryNotification();
- mContext.startActivityAsUser(mOpenBatterySaverSettings, UserHandle.CURRENT);
+ mContext.startActivityAsUser(mOpenBatterySaverSettings,
+ mUserTracker.getUserHandle());
} else if (action.equals(ACTION_START_SAVER)) {
logEvent(BatteryWarningEvents
.LowBatteryWarningEvent.LOW_BATTERY_NOTIFICATION_TURN_ON);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java
index 39d081d..36dc743 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java
@@ -83,8 +83,7 @@
if (mListeners.size() > 0) {
mSecureSettings.unregisterContentObserver(mContentObserver);
mSecureSettings.registerContentObserverForUser(
- Settings.Secure.getUriFor(
- Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED),
+ Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
false, mContentObserver, newUser);
}
}
@@ -100,8 +99,7 @@
mListeners.add(listener);
if (mListeners.size() == 1) {
mSecureSettings.registerContentObserverForUser(
- Settings.Secure.getUriFor(
- Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED),
+ Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
false, mContentObserver, mUserTracker.getUserId());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 4d005be..1151475 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -21,7 +21,6 @@
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
-import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
@@ -44,8 +43,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.graphics.Insets;
-import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
import android.os.Binder;
@@ -77,6 +74,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.ScreenshotHelper;
+import com.android.internal.util.ScreenshotRequest;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -94,7 +92,6 @@
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -311,7 +308,7 @@
intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
intent.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ mContext.startActivityAsUser(intent, mUserTracker.getUserHandle());
});
}
@@ -322,18 +319,8 @@
}
@Override
- public void handleImageBundleAsScreenshot(Bundle screenImageBundle, Rect locationInScreen,
- Insets visibleInsets, Task.TaskKey task) {
- mScreenshotHelper.provideScreenshot(
- screenImageBundle,
- locationInScreen,
- visibleInsets,
- task.id,
- task.userId,
- task.sourceComponent,
- SCREENSHOT_OVERVIEW,
- mHandler,
- null);
+ public void takeScreenshot(ScreenshotRequest request) {
+ mScreenshotHelper.takeScreenshot(request, mHandler, null);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index 5450db9..d64b33b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -49,6 +49,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.screenshot.ScrollCaptureController.LongScreenshot;
+import com.android.systemui.settings.UserTracker;
import com.google.common.util.concurrent.ListenableFuture;
@@ -79,6 +80,7 @@
private final LongScreenshotData mLongScreenshotHolder;
private final ActionIntentExecutor mActionExecutor;
private final FeatureFlags mFeatureFlags;
+ private final UserTracker mUserTracker;
private ImageView mPreview;
private ImageView mTransitionView;
@@ -110,7 +112,7 @@
public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter,
@Main Executor mainExecutor, @Background Executor bgExecutor,
LongScreenshotData longScreenshotHolder, ActionIntentExecutor actionExecutor,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags, UserTracker userTracker) {
mUiEventLogger = uiEventLogger;
mUiExecutor = mainExecutor;
mBackgroundExecutor = bgExecutor;
@@ -118,6 +120,7 @@
mLongScreenshotHolder = longScreenshotHolder;
mActionExecutor = actionExecutor;
mFeatureFlags = featureFlags;
+ mUserTracker = userTracker;
}
@@ -375,7 +378,7 @@
Intent sharingChooserIntent = Intent.createChooser(intent, null)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT);
+ startActivityAsUser(sharingChooserIntent, mUserTracker.getUserHandle());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
index 95cc0dc..f011aab 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -19,27 +19,26 @@
import android.graphics.Insets
import android.util.Log
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
-import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
-import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
+import com.android.internal.util.ScreenshotRequest
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.SCREENSHOT_WORK_PROFILE_POLICY
-import java.util.function.Consumer
-import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
+import java.util.function.Consumer
+import javax.inject.Inject
/**
* Processes a screenshot request sent from {@link ScreenshotHelper}.
*/
@SysUISingleton
class RequestProcessor @Inject constructor(
- private val capture: ImageCapture,
- private val policy: ScreenshotPolicy,
- private val flags: FeatureFlags,
- /** For the Java Async version, to invoke the callback. */
- @Application private val mainScope: CoroutineScope
+ private val capture: ImageCapture,
+ private val policy: ScreenshotPolicy,
+ private val flags: FeatureFlags,
+ /** For the Java Async version, to invoke the callback. */
+ @Application private val mainScope: CoroutineScope
) {
/**
* Inspects the incoming request, returning a potentially modified request depending on policy.
@@ -58,7 +57,7 @@
// regardless of the managed profile status.
if (request.type != TAKE_SCREENSHOT_PROVIDED_IMAGE &&
- flags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)
+ flags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)
) {
val info = policy.findPrimaryContent(policy.getDefaultDisplayId())
@@ -66,17 +65,21 @@
result = if (policy.isManagedProfile(info.user.identifier)) {
val image = capture.captureTask(info.taskId)
- ?: error("Task snapshot returned a null Bitmap!")
+ ?: error("Task snapshot returned a null Bitmap!")
// Provide the task snapshot as the screenshot
- ScreenshotRequest(
- TAKE_SCREENSHOT_PROVIDED_IMAGE, request.source,
- HardwareBitmapBundler.hardwareBitmapToBundle(image),
- info.bounds, Insets.NONE, info.taskId, info.user.identifier, info.component
- )
+ ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, request.source)
+ .setTopComponent(info.component)
+ .setTaskId(info.taskId)
+ .setUserId(info.user.identifier)
+ .setBitmap(image)
+ .setBoundsOnScreen(info.bounds)
+ .setInsets(Insets.NONE)
+ .build()
} else {
// Create a new request of the same type which includes the top component
- ScreenshotRequest(request.type, request.source, info.component)
+ ScreenshotRequest.Builder(request.type, request.source)
+ .setTopComponent(info.component).build()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index b21a485..6d87922 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -416,10 +416,11 @@
}
boolean showFlash = false;
- if (!aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
+ if (screenshotScreenBounds == null
+ || !aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
showFlash = true;
visibleInsets = Insets.NONE;
- screenshotScreenBounds.set(0, 0, screenshot.getWidth(), screenshot.getHeight());
+ screenshotScreenBounds = new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight());
}
mCurrentRequestCallback = requestCallback;
saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, topComponent,
@@ -553,6 +554,10 @@
Log.d(TAG, "adding OnComputeInternalInsetsListener");
}
mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener(mScreenshotView);
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "setContentView: " + mScreenshotView);
+ }
+ setContentView(mScreenshotView);
}
/**
@@ -634,6 +639,7 @@
// The window is focusable by default
setWindowFocusable(true);
+ mScreenshotView.requestFocus();
// Wait until this window is attached to request because it is
// the reference used to locate the target window (below).
@@ -691,10 +697,7 @@
mContext.getDrawable(R.drawable.overlay_badge_background), owner));
}
mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
- if (DEBUG_WINDOW) {
- Log.d(TAG, "setContentView: " + mScreenshotView);
- }
- setContentView(mScreenshotView);
+
// ignore system bar insets for the purpose of window layout
mWindow.getDecorView().setOnApplyWindowInsetsListener(
(v, insets) -> WindowInsets.CONSUMED);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 35e9f3e..7b271a8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -54,7 +54,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
-import com.android.internal.util.ScreenshotHelper;
+import com.android.internal.util.ScreenshotRequest;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.flags.FeatureFlags;
@@ -186,8 +186,7 @@
final Consumer<Uri> onSaved = (uri) -> reportUri(replyTo, uri);
RequestCallback callback = new RequestCallbackImpl(replyTo);
- ScreenshotHelper.ScreenshotRequest request =
- (ScreenshotHelper.ScreenshotRequest) msg.obj;
+ ScreenshotRequest request = (ScreenshotRequest) msg.obj;
handleRequest(request, onSaved, callback);
return true;
@@ -195,7 +194,7 @@
@MainThread
@VisibleForTesting
- void handleRequest(ScreenshotHelper.ScreenshotRequest request, Consumer<Uri> onSaved,
+ void handleRequest(ScreenshotRequest request, Consumer<Uri> onSaved,
RequestCallback callback) {
// If the storage for this user is locked, we have no place to store
// the screenshot, so skip taking it instead of showing a misleading
@@ -226,7 +225,7 @@
(r) -> dispatchToController(r, onSaved, callback));
}
- private void dispatchToController(ScreenshotHelper.ScreenshotRequest request,
+ private void dispatchToController(ScreenshotRequest request,
Consumer<Uri> uriConsumer, RequestCallback callback) {
ComponentName topComponent = request.getTopComponent();
@@ -244,8 +243,7 @@
if (DEBUG_SERVICE) {
Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_PROVIDED_IMAGE");
}
- Bitmap screenshot = ScreenshotHelper.HardwareBitmapBundler.bundleToHardwareBitmap(
- request.getBitmapBundle());
+ Bitmap screenshot = request.getBitmap();
Rect screenBounds = request.getBoundsInScreen();
Insets insets = request.getInsets();
int taskId = request.getTaskId();
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 5880003..2f6081b 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -218,7 +218,7 @@
automatic = Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
- UserHandle.USER_CURRENT);
+ mUserTracker.getUserId());
mAutomatic = automatic != Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index ce6fb14..392a851 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2097,7 +2097,17 @@
}
}
- public void expandWithoutQs() {
+ /**
+ * Expand shade so that notifications are visible.
+ * Non-split shade: just expanding shade or collapsing QS when they're expanded.
+ * Split shade: only expanding shade, notifications are always visible
+ *
+ * Called when `adb shell cmd statusbar expand-notifications` is executed.
+ */
+ public void expandShadeToNotifications() {
+ if (mSplitShadeEnabled && (isShadeFullyOpen() || isExpanding())) {
+ return;
+ }
if (isQsExpanded()) {
flingSettings(0 /* velocity */, FLING_COLLAPSE);
} else {
@@ -4186,9 +4196,7 @@
}
private void updateStatusBarIcons() {
- boolean showIconsWhenExpanded =
- (isPanelVisibleBecauseOfHeadsUp() || mIsFullWidth)
- && getExpandedHeight() < getOpeningHeight();
+ boolean showIconsWhenExpanded = getExpandedHeight() < getOpeningHeight();
if (showIconsWhenExpanded && isOnKeyguard()) {
showIconsWhenExpanded = false;
}
@@ -4255,7 +4263,7 @@
&& mHeadsUpAppearanceController.shouldBeVisible()) {
return false;
}
- return !mIsFullWidth || !mShowIconsWhenExpanded;
+ return !mShowIconsWhenExpanded;
}
private void onQsPanelScrollChanged(int scrollY) {
@@ -5538,7 +5546,7 @@
@Override
public void flingTopOverscroll(float velocity, boolean open) {
- // in split shade mode we want to expand/collapse QS only when touch happens within QS
+ // in split shade touches affect QS only when touch happens within QS
if (isSplitShadeAndTouchXOutsideQs(mInitialTouchX)) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
index db70065..b42bdaa 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
@@ -19,7 +19,6 @@
import android.hardware.display.AmbientDisplayConfiguration
import android.os.PowerManager
import android.os.SystemClock
-import android.os.UserHandle
import android.provider.Settings
import android.view.GestureDetector
import android.view.MotionEvent
@@ -29,6 +28,7 @@
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.FalsingManager.LOW_PENALTY
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
import com.android.systemui.tuner.TunerService
@@ -54,6 +54,7 @@
private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
private val statusBarStateController: StatusBarStateController,
private val shadeLogger: ShadeLogger,
+ userTracker: UserTracker,
tunerService: TunerService,
dumpManager: DumpManager
) : GestureDetector.SimpleOnGestureListener(), Dumpable {
@@ -65,10 +66,10 @@
when (key) {
Settings.Secure.DOZE_DOUBLE_TAP_GESTURE ->
doubleTapEnabled = ambientDisplayConfiguration.doubleTapGestureEnabled(
- UserHandle.USER_CURRENT)
+ userTracker.userId)
Settings.Secure.DOZE_TAP_SCREEN_GESTURE ->
singleTapEnabled = ambientDisplayConfiguration.tapGestureEnabled(
- UserHandle.USER_CURRENT)
+ userTracker.userId)
}
}
tunerService.addTunable(tunable,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 4e9f69c..1758379 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -53,7 +53,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.util.Pair;
-import android.util.Slog;
import android.util.SparseArray;
import android.view.InsetsState.InternalInsetsType;
import android.view.InsetsVisibilities;
@@ -1271,8 +1270,7 @@
public void showMediaOutputSwitcher(String packageName) {
int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
- Slog.e(TAG, "Call only allowed from system server.");
- return;
+ throw new SecurityException("Call only allowed from system server.");
}
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index 1e7fc93..197cf56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -54,7 +54,7 @@
* their respective views based on the progress of the animator. Interpolation differences TBD
*/
@SysUISingleton
-class SystemStatusAnimationScheduler @Inject constructor(
+open class SystemStatusAnimationScheduler @Inject constructor(
private val coordinator: SystemEventCoordinator,
private val chipAnimationController: SystemEventChipAnimationController,
private val statusBarWindowController: StatusBarWindowController,
@@ -66,7 +66,7 @@
companion object {
private const val PROPERTY_ENABLE_IMMERSIVE_INDICATOR = "enable_immersive_indicator"
}
- private fun isImmersiveIndicatorEnabled(): Boolean {
+ public fun isImmersiveIndicatorEnabled(): Boolean {
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
PROPERTY_ENABLE_IMMERSIVE_INDICATOR, true)
}
@@ -76,18 +76,22 @@
/** True if the persistent privacy dot should be active */
var hasPersistentDot = false
- private set
+ protected set
private var scheduledEvent: StatusEvent? = null
private var cancelExecutionRunnable: Runnable? = null
private val listeners = mutableSetOf<SystemStatusAnimationCallback>()
+ fun getListeners(): MutableSet<SystemStatusAnimationCallback> {
+ return listeners
+ }
+
init {
coordinator.attachScheduler(this)
dumpManager.registerDumpable(TAG, this)
}
- fun onStatusEvent(event: StatusEvent) {
+ open fun onStatusEvent(event: StatusEvent) {
// Ignore any updates until the system is up and running
if (isTooEarly() || !isImmersiveIndicatorEnabled()) {
return
@@ -139,7 +143,7 @@
}
}
- private fun isTooEarly(): Boolean {
+ public fun isTooEarly(): Boolean {
return systemClock.uptimeMillis() - Process.getStartUptimeMillis() < MIN_UPTIME
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
index 6115819..5ab3d7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
@@ -17,90 +17,25 @@
package com.android.systemui.statusbar.gesture
import android.content.Context
-import android.view.InputEvent
import android.view.MotionEvent
-import android.view.MotionEvent.ACTION_CANCEL
-import android.view.MotionEvent.ACTION_DOWN
-import android.view.MotionEvent.ACTION_MOVE
-import android.view.MotionEvent.ACTION_UP
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.window.StatusBarWindowController
import javax.inject.Inject
-/**
- * A class to detect when a user swipes away the status bar. To be notified when the swipe away
- * gesture is detected, add a callback via [addOnGestureDetectedCallback].
- */
+/** A class to detect when a user swipes away the status bar. */
@SysUISingleton
-open class SwipeStatusBarAwayGestureHandler @Inject constructor(
+class SwipeStatusBarAwayGestureHandler
+@Inject
+constructor(
context: Context,
+ logger: SwipeUpGestureLogger,
private val statusBarWindowController: StatusBarWindowController,
- private val logger: SwipeStatusBarAwayGestureLogger
-) : GenericGestureDetector(SwipeStatusBarAwayGestureHandler::class.simpleName!!) {
-
- private var startY: Float = 0f
- private var startTime: Long = 0L
- private var monitoringCurrentTouch: Boolean = false
-
- private var swipeDistanceThreshold: Int = context.resources.getDimensionPixelSize(
- com.android.internal.R.dimen.system_gestures_start_threshold
- )
-
- override fun onInputEvent(ev: InputEvent) {
- if (ev !is MotionEvent) {
- return
- }
-
- when (ev.actionMasked) {
- ACTION_DOWN -> {
- if (
- // Gesture starts just below the status bar
- ev.y >= statusBarWindowController.statusBarHeight
- && ev.y <= 3 * statusBarWindowController.statusBarHeight
- ) {
- logger.logGestureDetectionStarted(ev.y.toInt())
- startY = ev.y
- startTime = ev.eventTime
- monitoringCurrentTouch = true
- } else {
- monitoringCurrentTouch = false
- }
- }
- ACTION_MOVE -> {
- if (!monitoringCurrentTouch) {
- return
- }
- if (
- // Gesture is up
- ev.y < startY
- // Gesture went far enough
- && (startY - ev.y) >= swipeDistanceThreshold
- // Gesture completed quickly enough
- && (ev.eventTime - startTime) < SWIPE_TIMEOUT_MS
- ) {
- monitoringCurrentTouch = false
- logger.logGestureDetected(ev.y.toInt())
- onGestureDetected(ev)
- }
- }
- ACTION_CANCEL, ACTION_UP -> {
- if (monitoringCurrentTouch) {
- logger.logGestureDetectionEndedWithoutTriggering(ev.y.toInt())
- }
- monitoringCurrentTouch = false
- }
- }
- }
-
- override fun startGestureListening() {
- super.startGestureListening()
- logger.logInputListeningStarted()
- }
-
- override fun stopGestureListening() {
- super.stopGestureListening()
- logger.logInputListeningStopped()
+) : SwipeUpGestureHandler(context, logger, loggerTag = LOGGER_TAG) {
+ override fun startOfGestureIsWithinBounds(ev: MotionEvent): Boolean {
+ // Gesture starts just below the status bar
+ return ev.y >= statusBarWindowController.statusBarHeight &&
+ ev.y <= 3 * statusBarWindowController.statusBarHeight
}
}
-private const val SWIPE_TIMEOUT_MS: Long = 500
+private const val LOGGER_TAG = "SwipeStatusBarAway"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt
new file mode 100644
index 0000000..5ecc35c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 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.statusbar.gesture
+
+import android.content.Context
+import android.view.InputEvent
+import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_CANCEL
+import android.view.MotionEvent.ACTION_DOWN
+import android.view.MotionEvent.ACTION_MOVE
+import android.view.MotionEvent.ACTION_UP
+import com.android.systemui.dagger.SysUISingleton
+
+/**
+ * A class to detect a generic "swipe up" gesture. To be notified when the swipe up gesture is
+ * detected, add a callback via [addOnGestureDetectedCallback].
+ */
+@SysUISingleton
+abstract class SwipeUpGestureHandler(
+ context: Context,
+ private val logger: SwipeUpGestureLogger,
+ private val loggerTag: String,
+) : GenericGestureDetector(SwipeUpGestureHandler::class.simpleName!!) {
+
+ private var startY: Float = 0f
+ private var startTime: Long = 0L
+ private var monitoringCurrentTouch: Boolean = false
+
+ private var swipeDistanceThreshold: Int = context.resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.system_gestures_start_threshold
+ )
+
+ override fun onInputEvent(ev: InputEvent) {
+ if (ev !is MotionEvent) {
+ return
+ }
+
+ when (ev.actionMasked) {
+ ACTION_DOWN -> {
+ if (
+ startOfGestureIsWithinBounds(ev)
+ ) {
+ logger.logGestureDetectionStarted(loggerTag, ev.y.toInt())
+ startY = ev.y
+ startTime = ev.eventTime
+ monitoringCurrentTouch = true
+ } else {
+ monitoringCurrentTouch = false
+ }
+ }
+ ACTION_MOVE -> {
+ if (!monitoringCurrentTouch) {
+ return
+ }
+ if (
+ // Gesture is up
+ ev.y < startY &&
+ // Gesture went far enough
+ (startY - ev.y) >= swipeDistanceThreshold &&
+ // Gesture completed quickly enough
+ (ev.eventTime - startTime) < SWIPE_TIMEOUT_MS
+ ) {
+ monitoringCurrentTouch = false
+ logger.logGestureDetected(loggerTag, ev.y.toInt())
+ onGestureDetected(ev)
+ }
+ }
+ ACTION_CANCEL, ACTION_UP -> {
+ if (monitoringCurrentTouch) {
+ logger.logGestureDetectionEndedWithoutTriggering(loggerTag, ev.y.toInt())
+ }
+ monitoringCurrentTouch = false
+ }
+ }
+ }
+
+ /**
+ * Returns true if the [ACTION_DOWN] event falls within bounds for this specific swipe-up
+ * gesture.
+ *
+ * Implementations must override this method to specify what part(s) of the screen are valid
+ * locations for the swipe up gesture to start at.
+ */
+ abstract fun startOfGestureIsWithinBounds(ev: MotionEvent): Boolean
+
+ override fun startGestureListening() {
+ super.startGestureListening()
+ logger.logInputListeningStarted(loggerTag)
+ }
+
+ override fun stopGestureListening() {
+ super.stopGestureListening()
+ logger.logInputListeningStopped(loggerTag)
+ }
+}
+
+private const val SWIPE_TIMEOUT_MS: Long = 500
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt
similarity index 64%
rename from packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureLogger.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt
index 9bdff92..9ce6b02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt
@@ -16,49 +16,49 @@
package com.android.systemui.statusbar.gesture
-import com.android.systemui.log.dagger.SwipeStatusBarAwayLog
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.dagger.SwipeUpLog
import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogLevel
import javax.inject.Inject
-/** Log messages for [SwipeStatusBarAwayGestureHandler]. */
-class SwipeStatusBarAwayGestureLogger @Inject constructor(
- @SwipeStatusBarAwayLog private val buffer: LogBuffer
+/** Log messages for [SwipeUpGestureHandler]. */
+@SysUISingleton
+class SwipeUpGestureLogger @Inject constructor(
+ @SwipeUpLog private val buffer: LogBuffer,
) {
- fun logGestureDetectionStarted(y: Int) {
+ fun logGestureDetectionStarted(tag: String, y: Int) {
buffer.log(
- TAG,
+ tag,
LogLevel.DEBUG,
{ int1 = y },
{ "Beginning gesture detection. y=$int1" }
)
}
- fun logGestureDetectionEndedWithoutTriggering(y: Int) {
+ fun logGestureDetectionEndedWithoutTriggering(tag: String, y: Int) {
buffer.log(
- TAG,
+ tag,
LogLevel.DEBUG,
{ int1 = y },
{ "Gesture finished; no swipe up gesture detected. Final y=$int1" }
)
}
- fun logGestureDetected(y: Int) {
+ fun logGestureDetected(tag: String, y: Int) {
buffer.log(
- TAG,
+ tag,
LogLevel.INFO,
{ int1 = y },
{ "Gesture detected; notifying callbacks. y=$int1" }
)
}
- fun logInputListeningStarted() {
- buffer.log(TAG, LogLevel.VERBOSE, {}, { "Input listening started "})
+ fun logInputListeningStarted(tag: String) {
+ buffer.log(tag, LogLevel.VERBOSE, {}, { "Input listening started "})
}
- fun logInputListeningStopped() {
- buffer.log(TAG, LogLevel.VERBOSE, {}, { "Input listening stopped "})
+ fun logInputListeningStopped(tag: String) {
+ buffer.log(tag, LogLevel.VERBOSE, {}, { "Input listening stopped "})
}
}
-
-private const val TAG = "SwipeStatusBarAwayGestureHandler"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
index 05a9a42..635ed7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
@@ -25,6 +25,10 @@
val context: Context,
val featureFlags: FeatureFlags
) {
+ init {
+ featureFlags.addListener(Flags.DISABLE_FSI) { event -> event.requestNoRestart() }
+ }
+
fun isDevLoggingEnabled(): Boolean =
featureFlags.isEnabled(Flags.NOTIFICATION_PIPELINE_DEVELOPER_LOGGING)
@@ -35,6 +39,8 @@
fun fsiOnDNDUpdate(): Boolean = featureFlags.isEnabled(Flags.FSI_ON_DND_UPDATE)
+ fun disableFsi(): Boolean = featureFlags.isEnabled(Flags.DISABLE_FSI)
+
val shouldFilterUnseenNotifsOnKeyguard: Boolean by lazy {
featureFlags.isEnabled(Flags.FILTER_UNSEEN_NOTIFS_ON_KEYGUARD)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 76252d0..e996b78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -114,10 +114,10 @@
.onStart { emit(Unit) }
// for each change, lookup the new value
.map {
- secureSettings.getBoolForUser(
+ secureSettings.getIntForUser(
Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
UserHandle.USER_CURRENT,
- )
+ ) == 1
}
// perform lookups on the bg thread pool
.flowOn(bgDispatcher)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
index 7136cad..bc881ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -31,6 +31,10 @@
*/
enum FullScreenIntentDecision {
/**
+ * Full screen intents are disabled.
+ */
+ NO_FSI_DISABLED(false),
+ /**
* No full screen intent included, so there is nothing to show.
*/
NO_FULL_SCREEN_INTENT(false),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index d9dacfd..9bcf92d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -28,7 +28,6 @@
import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.IDreamManager;
import android.service.notification.StatusBarNotification;
@@ -40,6 +39,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -74,6 +74,7 @@
private final NotifPipelineFlags mFlags;
private final KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
private final UiEventLogger mUiEventLogger;
+ private final UserTracker mUserTracker;
@VisibleForTesting
protected boolean mUseHeadsUp = false;
@@ -114,7 +115,8 @@
@Main Handler mainHandler,
NotifPipelineFlags flags,
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ UserTracker userTracker) {
mContentResolver = contentResolver;
mPowerManager = powerManager;
mDreamManager = dreamManager;
@@ -127,6 +129,7 @@
mFlags = flags;
mKeyguardNotificationVisibilityProvider = keyguardNotificationVisibilityProvider;
mUiEventLogger = uiEventLogger;
+ mUserTracker = userTracker;
ContentObserver headsUpObserver = new ContentObserver(mainHandler) {
@Override
public void onChange(boolean selfChange) {
@@ -236,6 +239,9 @@
@Override
public FullScreenIntentDecision getFullScreenIntentDecision(NotificationEntry entry) {
+ if (mFlags.disableFsi()) {
+ return FullScreenIntentDecision.NO_FSI_DISABLED;
+ }
if (entry.getSbn().getNotification().fullScreenIntent == null) {
return FullScreenIntentDecision.NO_FULL_SCREEN_INTENT;
}
@@ -325,6 +331,9 @@
final int uid = entry.getSbn().getUid();
final String packageName = entry.getSbn().getPackageName();
switch (decision) {
+ case NO_FSI_DISABLED:
+ mLogger.logNoFullscreen(entry, "Disabled");
+ return;
case NO_FULL_SCREEN_INTENT:
return;
case NO_FSI_SUPPRESSED_BY_DND:
@@ -450,7 +459,7 @@
* @return true if the entry should ambient pulse, false otherwise
*/
private boolean shouldHeadsUpWhenDozing(NotificationEntry entry, boolean log) {
- if (!mAmbientDisplayConfiguration.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) {
+ if (!mAmbientDisplayConfiguration.pulseOnNotificationEnabled(mUserTracker.getUserId())) {
if (log) mLogger.logNoPulsingSettingDisabled(entry);
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt
index ffd931c..197ae1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt
@@ -18,9 +18,12 @@
package com.android.systemui.statusbar.notification.logging
import android.stats.sysui.NotificationEnums
+import android.util.Log
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
+import com.android.systemui.dump.DumpsysTableLogger
+import com.android.systemui.dump.Row
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import java.io.PrintWriter
import javax.inject.Inject
@@ -33,6 +36,7 @@
fun init() {
dumpManager.registerNormalDumpable(javaClass.simpleName, this)
+ Log.i("NotificationMemory", "Registered dumpable.")
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -45,27 +49,36 @@
/** Renders a table of notification object usage into passed [PrintWriter]. */
private fun dumpNotificationObjects(pw: PrintWriter, memoryUse: List<NotificationMemoryUsage>) {
- pw.println("Notification Object Usage")
- pw.println("-----------")
- pw.println(
- "Package".padEnd(35) +
- "\t\tSmall\tLarge\t${"Style".padEnd(15)}\t\tStyle\tBig\tExtend.\tExtras\tCustom"
- )
- pw.println("".padEnd(35) + "\t\tIcon\tIcon\t${"".padEnd(15)}\t\tIcon\tPicture\t \t \tView")
- pw.println()
-
- memoryUse.forEach { use ->
- pw.println(
- use.packageName.padEnd(35) +
- "\t\t" +
- "${use.objectUsage.smallIcon}\t${use.objectUsage.largeIcon}\t" +
- (styleEnumToString(use.objectUsage.style).take(15) ?: "").padEnd(15) +
- "\t\t${use.objectUsage.styleIcon}\t" +
- "${use.objectUsage.bigPicture}\t${use.objectUsage.extender}\t" +
- "${use.objectUsage.extras}\t${use.objectUsage.hasCustomView}\t" +
- use.notificationKey
+ val columns =
+ listOf(
+ "Package",
+ "Small Icon",
+ "Large Icon",
+ "Style",
+ "Style Icon",
+ "Big Picture",
+ "Extender",
+ "Extras",
+ "Custom View",
+ "Key"
)
- }
+ val rows: List<Row> =
+ memoryUse.map {
+ listOf(
+ it.packageName,
+ toKb(it.objectUsage.smallIcon),
+ toKb(it.objectUsage.largeIcon),
+ styleEnumToString(it.objectUsage.style),
+ toKb(it.objectUsage.styleIcon),
+ toKb(it.objectUsage.bigPicture),
+ toKb(it.objectUsage.extender),
+ toKb(it.objectUsage.extras),
+ it.objectUsage.hasCustomView.toString(),
+ // | is a field delimiter in the output format so we need to replace
+ // it to avoid breakage.
+ it.notificationKey.replace('|', '│')
+ )
+ }
// Calculate totals for easily glanceable summary.
data class Totals(
@@ -88,18 +101,23 @@
t
}
- pw.println()
- pw.println("TOTALS")
- pw.println(
- "".padEnd(35) +
- "\t\t" +
- "${toKb(totals.smallIcon)}\t${toKb(totals.largeIcon)}\t" +
- "".padEnd(15) +
- "\t\t${toKb(totals.styleIcon)}\t" +
- "${toKb(totals.bigPicture)}\t${toKb(totals.extender)}\t" +
- toKb(totals.extras)
- )
- pw.println()
+ val totalsRow: List<Row> =
+ listOf(
+ listOf(
+ "TOTALS",
+ toKb(totals.smallIcon),
+ toKb(totals.largeIcon),
+ "",
+ toKb(totals.styleIcon),
+ toKb(totals.bigPicture),
+ toKb(totals.extender),
+ toKb(totals.extras),
+ "",
+ ""
+ )
+ )
+ val tableLogger = DumpsysTableLogger("Notification Object Usage", columns, rows + totalsRow)
+ tableLogger.printTableData(pw)
}
/** Renders a table of notification view usage into passed [PrintWriter] */
@@ -116,40 +134,65 @@
var softwareBitmapsPenalty: Int = 0,
)
- val totals = Totals()
- pw.println("Notification View Usage")
- pw.println("-----------")
- pw.println("View Type".padEnd(24) + "\tSmall\tLarge\tStyle\tCustom\tSoftware")
- pw.println("".padEnd(24) + "\tIcon\tIcon\tUse\tView\tBitmaps")
- pw.println()
- memoryUse
- .filter { it.viewUsage.isNotEmpty() }
- .forEach { use ->
- pw.println(use.packageName + " " + use.notificationKey)
- use.viewUsage.forEach { view ->
- pw.println(
- " ${view.viewType.toString().padEnd(24)}\t${view.smallIcon}" +
- "\t${view.largeIcon}\t${view.style}" +
- "\t${view.customViews}\t${view.softwareBitmapsPenalty}"
- )
-
- if (view.viewType == ViewType.TOTAL) {
- totals.smallIcon += view.smallIcon
- totals.largeIcon += view.largeIcon
- totals.style += view.style
- totals.customViews += view.customViews
- totals.softwareBitmapsPenalty += view.softwareBitmapsPenalty
+ val columns =
+ listOf(
+ "Package",
+ "View Type",
+ "Small Icon",
+ "Large Icon",
+ "Style Use",
+ "Custom View",
+ "Software Bitmaps",
+ "Key"
+ )
+ val rows =
+ memoryUse
+ .filter { it.viewUsage.isNotEmpty() }
+ .flatMap { use ->
+ use.viewUsage.map { view ->
+ listOf(
+ use.packageName,
+ view.viewType.toString(),
+ toKb(view.smallIcon),
+ toKb(view.largeIcon),
+ toKb(view.style),
+ toKb(view.customViews),
+ toKb(view.softwareBitmapsPenalty),
+ // | is a field delimiter in the output format so we need to replace
+ // it to avoid breakage.
+ use.notificationKey.replace('|', '│')
+ )
}
}
+
+ val totals = Totals()
+ memoryUse
+ .filter { it.viewUsage.isNotEmpty() }
+ .map { it.viewUsage.firstOrNull { view -> view.viewType == ViewType.TOTAL } }
+ .filterNotNull()
+ .forEach { view ->
+ totals.smallIcon += view.smallIcon
+ totals.largeIcon += view.largeIcon
+ totals.style += view.style
+ totals.customViews += view.customViews
+ totals.softwareBitmapsPenalty += view.softwareBitmapsPenalty
}
- pw.println()
- pw.println("TOTALS")
- pw.println(
- " ${"".padEnd(24)}\t${toKb(totals.smallIcon)}" +
- "\t${toKb(totals.largeIcon)}\t${toKb(totals.style)}" +
- "\t${toKb(totals.customViews)}\t${toKb(totals.softwareBitmapsPenalty)}"
- )
- pw.println()
+
+ val totalsRow: List<Row> =
+ listOf(
+ listOf(
+ "TOTALS",
+ "",
+ toKb(totals.smallIcon),
+ toKb(totals.largeIcon),
+ toKb(totals.style),
+ toKb(totals.customViews),
+ toKb(totals.softwareBitmapsPenalty),
+ ""
+ )
+ )
+ val tableLogger = DumpsysTableLogger("Notification View Usage", columns, rows + totalsRow)
+ tableLogger.printTableData(pw)
}
private fun styleEnumToString(styleEnum: Int): String =
@@ -168,6 +211,10 @@
}
private fun toKb(bytes: Int): String {
- return (bytes / 1024).toString() + " KB"
+ if (bytes == 0) {
+ return "--"
+ }
+
+ return "%.2f KB".format(bytes / 1024f)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index fbe88df..7addc8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -24,6 +24,7 @@
import android.graphics.Point;
import android.util.AttributeSet;
import android.util.MathUtils;
+import android.view.Choreographer;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
@@ -492,12 +493,9 @@
if (animationListener != null) {
mAppearAnimator.addListener(animationListener);
}
- if (delay > 0) {
- // we need to apply the initial state already to avoid drawn frames in the wrong state
- updateAppearAnimationAlpha();
- updateAppearRect();
- mAppearAnimator.setStartDelay(delay);
- }
+ // we need to apply the initial state already to avoid drawn frames in the wrong state
+ updateAppearAnimationAlpha();
+ updateAppearRect();
mAppearAnimator.addListener(new AnimatorListenerAdapter() {
private boolean mWasCancelled;
@@ -528,7 +526,20 @@
mWasCancelled = true;
}
});
- mAppearAnimator.start();
+
+ // Cache the original animator so we can check if the animation should be started in the
+ // Choreographer callback. It's possible that the original animator (mAppearAnimator) is
+ // replaced with a new value before the callback is called.
+ ValueAnimator cachedAnimator = mAppearAnimator;
+ // Even when delay=0, starting the animation on the next frame is necessary to avoid jank.
+ // Not doing so will increase the chances our Animator will be forced to skip a value of
+ // the animation's progression, causing stutter.
+ Choreographer.getInstance().postFrameCallbackDelayed(
+ frameTimeNanos -> {
+ if (mAppearAnimator == cachedAnimator) {
+ mAppearAnimator.start();
+ }
+ }, delay);
}
private int getCujType(boolean isAppearing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 49dc655..21f4cb5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -16,15 +16,22 @@
package com.android.systemui.statusbar.notification.row;
+import android.annotation.ColorInt;
+import android.annotation.DrawableRes;
+import android.annotation.StringRes;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.IndentingPrintWriter;
import android.view.View;
+import android.widget.TextView;
import androidx.annotation.NonNull;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -41,6 +48,11 @@
private String mManageNotificationText;
private String mManageNotificationHistoryText;
+ // Footer label
+ private TextView mSeenNotifsFooterTextView;
+ private @StringRes int mSeenNotifsFilteredText;
+ private int mUnlockIconSize;
+
public FooterView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -73,10 +85,41 @@
super.onFinishInflate();
mClearAllButton = (FooterViewButton) findSecondaryView();
mManageButton = findViewById(R.id.manage_text);
+ mSeenNotifsFooterTextView = findViewById(R.id.unlock_prompt_footer);
updateResources();
updateText();
}
+ public void setFooterLabelTextAndIcon(@StringRes int text, @DrawableRes int icon) {
+ mSeenNotifsFilteredText = text;
+ if (mSeenNotifsFilteredText != 0) {
+ mSeenNotifsFooterTextView.setText(mSeenNotifsFilteredText);
+ } else {
+ mSeenNotifsFooterTextView.setText(null);
+ }
+ Drawable drawable;
+ if (icon == 0) {
+ drawable = null;
+ } else {
+ drawable = getResources().getDrawable(icon);
+ drawable.setBounds(0, 0, mUnlockIconSize, mUnlockIconSize);
+ }
+ mSeenNotifsFooterTextView.setCompoundDrawablesRelative(drawable, null, null, null);
+ updateFooterVisibilityMode();
+ }
+
+ private void updateFooterVisibilityMode() {
+ if (mSeenNotifsFilteredText != 0) {
+ mManageButton.setVisibility(View.GONE);
+ mClearAllButton.setVisibility(View.GONE);
+ mSeenNotifsFooterTextView.setVisibility(View.VISIBLE);
+ } else {
+ mManageButton.setVisibility(View.VISIBLE);
+ mClearAllButton.setVisibility(View.VISIBLE);
+ mSeenNotifsFooterTextView.setVisibility(View.GONE);
+ }
+ }
+
public void setManageButtonClickListener(OnClickListener listener) {
mManageButton.setOnClickListener(listener);
}
@@ -135,12 +178,19 @@
mClearAllButton.setTextColor(textColor);
mManageButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
mManageButton.setTextColor(textColor);
+ final @ColorInt int labelTextColor =
+ Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary);
+ mSeenNotifsFooterTextView.setTextColor(labelTextColor);
+ mSeenNotifsFooterTextView.setCompoundDrawableTintList(
+ ColorStateList.valueOf(labelTextColor));
}
private void updateResources() {
mManageNotificationText = getContext().getString(R.string.manage_notifications_text);
mManageNotificationHistoryText = getContext()
.getString(R.string.manage_notifications_history_text);
+ mUnlockIconSize = getResources()
+ .getDimensionPixelSize(R.dimen.notifications_unseen_footer_icon_size);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 4837075..aab36da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -538,6 +538,7 @@
private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
private final ScreenOffAnimationController mScreenOffAnimationController;
private boolean mShouldUseSplitNotificationShade;
+ private boolean mHasFilteredOutSeenNotifications;
private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener =
new ExpandableView.OnHeightChangedListener() {
@@ -684,6 +685,10 @@
updateFooter();
}
+ void setHasFilteredOutSeenNotifications(boolean hasFilteredOutSeenNotifications) {
+ mHasFilteredOutSeenNotifications = hasFilteredOutSeenNotifications;
+ }
+
@VisibleForTesting
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void updateFooter() {
@@ -4612,13 +4617,12 @@
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- void updateEmptyShadeView(
- boolean visible, boolean areNotificationsHiddenInShade, boolean areSeenNotifsFiltered) {
+ void updateEmptyShadeView(boolean visible, boolean areNotificationsHiddenInShade) {
mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
if (areNotificationsHiddenInShade) {
updateEmptyShadeView(R.string.dnd_suppressing_shade_text, 0, 0);
- } else if (areSeenNotifsFiltered) {
+ } else if (mHasFilteredOutSeenNotifications) {
updateEmptyShadeView(
R.string.no_unseen_notif_text,
R.string.unlock_to_see_notif_text,
@@ -4657,13 +4661,20 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void updateFooterView(boolean visible, boolean showDismissView, boolean showHistory) {
- if (mFooterView == null) {
+ if (mFooterView == null || mNotificationStackSizeCalculator == null) {
return;
}
boolean animate = mIsExpanded && mAnimationsEnabled;
mFooterView.setVisible(visible, animate);
mFooterView.setSecondaryVisible(showDismissView, animate);
mFooterView.showHistory(showHistory);
+ if (mHasFilteredOutSeenNotifications) {
+ mFooterView.setFooterLabelTextAndIcon(
+ R.string.unlock_to_see_notif_text,
+ R.drawable.ic_friction_lock_closed);
+ } else {
+ mFooterView.setFooterLabelTextAndIcon(0, 0);
+ }
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
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 5891948..42d122d 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
@@ -120,6 +120,7 @@
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.Compile;
+import com.android.systemui.util.settings.SecureSettings;
import java.util.ArrayList;
import java.util.List;
@@ -189,6 +190,7 @@
private final FeatureFlags mFeatureFlags;
private final boolean mUseRoundnessSourceTypes;
private final NotificationTargetsHelper mNotificationTargetsHelper;
+ private final SecureSettings mSecureSettings;
private View mLongPressedView;
@@ -667,7 +669,8 @@
NotificationStackScrollLogger logger,
NotificationStackSizeCalculator notificationStackSizeCalculator,
FeatureFlags featureFlags,
- NotificationTargetsHelper notificationTargetsHelper) {
+ NotificationTargetsHelper notificationTargetsHelper,
+ SecureSettings secureSettings) {
mStackStateLogger = stackLogger;
mLogger = logger;
mAllowLongPress = allowLongPress;
@@ -709,6 +712,7 @@
mFeatureFlags = featureFlags;
mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mNotificationTargetsHelper = notificationTargetsHelper;
+ mSecureSettings = secureSettings;
updateResources();
}
@@ -1015,8 +1019,7 @@
Log.wtf(TAG, "isHistoryEnabled failed to initialize its value");
return false;
}
- mHistoryEnabled = historyEnabled = Settings.Secure.getIntForUser(
- mView.getContext().getContentResolver(),
+ mHistoryEnabled = historyEnabled = mSecureSettings.getIntForUser(
Settings.Secure.NOTIFICATION_HISTORY_ENABLED,
0,
UserHandle.USER_CURRENT) == 1;
@@ -1242,11 +1245,7 @@
// For more details, see: b/228790482
&& !isInTransitionToKeyguard();
- mView.updateEmptyShadeView(
- shouldShow,
- mZenModeController.areNotificationsHiddenInShade(),
- mNotifPipelineFlags.getShouldFilterUnseenNotifsOnKeyguard()
- && mSeenNotificationsProvider.getHasFilteredOutSeenNotifications());
+ mView.updateEmptyShadeView(shouldShow, mZenModeController.areNotificationsHiddenInShade());
Trace.endSection();
}
@@ -1942,6 +1941,9 @@
@Override
public void setNotifStats(@NonNull NotifStats notifStats) {
mNotifStats = notifStats;
+ mView.setHasFilteredOutSeenNotifications(
+ mNotifPipelineFlags.getShouldFilterUnseenNotifsOnKeyguard()
+ && mSeenNotificationsProvider.getHasFilteredOutSeenNotifications());
updateFooter();
updateShowEmptyShadeView();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 2dad8e0..ccde3c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -31,7 +31,6 @@
import android.os.Bundle;
import android.os.PowerManager;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -55,6 +54,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.CameraLauncher;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeController;
@@ -99,6 +99,7 @@
private final Optional<Vibrator> mVibratorOptional;
private final DisableFlagsLogger mDisableFlagsLogger;
private final int mDisplayId;
+ private final UserTracker mUserTracker;
private final boolean mVibrateOnOpening;
private final VibrationEffect mCameraLaunchGestureVibrationEffect;
private final SystemBarAttributesListener mSystemBarAttributesListener;
@@ -133,7 +134,8 @@
DisableFlagsLogger disableFlagsLogger,
@DisplayId int displayId,
SystemBarAttributesListener systemBarAttributesListener,
- Lazy<CameraLauncher> cameraLauncherLazy) {
+ Lazy<CameraLauncher> cameraLauncherLazy,
+ UserTracker userTracker) {
mCentralSurfaces = centralSurfaces;
mContext = context;
mShadeController = shadeController;
@@ -157,6 +159,7 @@
mDisableFlagsLogger = disableFlagsLogger;
mDisplayId = displayId;
mCameraLauncherLazy = cameraLauncherLazy;
+ mUserTracker = userTracker;
mVibrateOnOpening = resources.getBoolean(R.bool.config_vibrateOnIconAnimation);
mCameraLaunchGestureVibrationEffect = getCameraGestureVibrationEffect(
@@ -215,7 +218,7 @@
return;
}
- mNotificationPanelViewController.expandWithoutQs();
+ mNotificationPanelViewController.expandShadeToNotifications();
}
@Override
@@ -375,7 +378,7 @@
mCentralSurfaces.startActivityDismissingKeyguard(cameraIntent,
false /* onlyProvisioned */, true /* dismissShade */,
true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
- null /* animationController */, UserHandle.CURRENT);
+ null /* animationController */, mUserTracker.getUserHandle());
} else {
if (!mCentralSurfaces.isDeviceInteractive()) {
// Avoid flickering of the scrim when we instant launch the camera and the bouncer
@@ -432,7 +435,7 @@
mCentralSurfaces.startActivityDismissingKeyguard(emergencyIntent,
false /* onlyProvisioned */, true /* dismissShade */,
true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
- null /* animationController */, UserHandle.CURRENT);
+ null /* animationController */, mUserTracker.getUserHandle());
return;
}
@@ -447,7 +450,7 @@
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.reset(true /* hide */);
}
- mContext.startActivityAsUser(emergencyIntent, UserHandle.CURRENT);
+ mContext.startActivityAsUser(emergencyIntent, mUserTracker.getUserHandle());
return;
}
// We need to defer the emergency action launch until the screen comes on, since otherwise
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 64c7ff7..dd75d35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -179,6 +179,7 @@
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.scrim.ScrimView;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.shade.CameraLauncher;
import com.android.systemui.shade.NotificationPanelViewController;
@@ -505,6 +506,7 @@
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private final MessageRouter mMessageRouter;
private final WallpaperManager mWallpaperManager;
+ private final UserTracker mUserTracker;
private CentralSurfacesComponent mCentralSurfacesComponent;
@@ -748,7 +750,8 @@
IDreamManager dreamManager,
Lazy<CameraLauncher> cameraLauncherLazy,
Lazy<LightRevealScrimViewModel> lightRevealScrimViewModelLazy,
- AlternateBouncerInteractor alternateBouncerInteractor
+ AlternateBouncerInteractor alternateBouncerInteractor,
+ UserTracker userTracker
) {
mContext = context;
mNotificationsController = notificationsController;
@@ -828,6 +831,7 @@
mJankMonitor = jankMonitor;
mCameraLauncherLazy = cameraLauncherLazy;
mAlternateBouncerInteractor = alternateBouncerInteractor;
+ mUserTracker = userTracker;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mStartingSurfaceOptional = startingSurfaceOptional;
@@ -865,6 +869,11 @@
wiredChargingRippleController.registerCallbacks();
mLightRevealScrimViewModelLazy = lightRevealScrimViewModelLazy;
+
+ // Based on teamfood flag, turn predictive back dispatch on at runtime.
+ if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) {
+ mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true);
+ }
}
@Override
@@ -979,11 +988,6 @@
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy.init();
- // Based on teamfood flag, turn predictive back dispatch on at runtime.
- if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) {
- mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true);
- }
-
mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onUnlockedChanged() {
@@ -1395,6 +1399,7 @@
// Things that mean we're not swiping to dismiss the keyguard, and should ignore this
// expansion:
// - Keyguard isn't even visible.
+ // - We're swiping on the bouncer, not the lockscreen.
// - Keyguard is occluded. Expansion changes here are the shade being expanded over the
// occluding activity.
// - Keyguard is visible, but can't be dismissed (swiping up will show PIN/password prompt).
@@ -1404,6 +1409,7 @@
// - QS is expanded and we're swiping - swiping up now will hide QS, not dismiss the
// keyguard.
if (!isKeyguardShowing()
+ || mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()
|| isOccluded()
|| !mKeyguardStateController.canDismissLockScreen()
|| mKeyguardViewMediator.isAnySimPinSecure()
@@ -4152,7 +4158,7 @@
Log.wtf(TAG, "WallpaperManager not supported");
return;
}
- WallpaperInfo info = mWallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
+ WallpaperInfo info = mWallpaperManager.getWallpaperInfo(mUserTracker.getUserId());
mWallpaperController.onWallpaperInfoUpdated(info);
final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
@@ -4376,6 +4382,6 @@
return new UserHandle(UserHandle.myUserId());
}
}
- return UserHandle.CURRENT;
+ return mUserTracker.getUserHandle();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 0446cef..c248a50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -45,6 +45,7 @@
import com.android.systemui.doze.DozeScreenState;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -84,6 +85,7 @@
private final ScreenOffAnimationController mScreenOffAnimationController;
private final FoldAodAnimationController mFoldAodAnimationController;
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+ private final UserTracker mUserTracker;
private final Set<Callback> mCallbacks = new HashSet<>();
@@ -128,7 +130,8 @@
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
ConfigurationController configurationController,
- StatusBarStateController statusBarStateController) {
+ StatusBarStateController statusBarStateController,
+ UserTracker userTracker) {
mResources = resources;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mAlwaysOnPolicy = alwaysOnDisplayPolicy;
@@ -140,6 +143,7 @@
mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation);
mScreenOffAnimationController = screenOffAnimationController;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
+ mUserTracker = userTracker;
keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
tunerService.addTunable(
@@ -169,7 +173,7 @@
private void updateQuickPickupEnabled() {
mIsQuickPickupEnabled =
- mAmbientDisplayConfiguration.quickPickupSensorEnabled(UserHandle.USER_CURRENT);
+ mAmbientDisplayConfiguration.quickPickupSensorEnabled(mUserTracker.getUserId());
}
public boolean getDisplayStateSupported() {
@@ -418,7 +422,7 @@
@Override
public void onTuningChanged(String key, String newValue) {
- mDozeAlwaysOn = mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT);
+ mDozeAlwaysOn = mAmbientDisplayConfiguration.alwaysOnEnabled(mUserTracker.getUserId());
if (key.equals(Settings.Secure.DOZE_ALWAYS_ON)) {
updateControlScreenOff();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 4ad3199..cba0897 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -357,7 +357,7 @@
mView.setOnApplyWindowInsetsListener(
(view, windowInsets) -> mView.updateWindowInsets(windowInsets, mInsetsProvider));
mSecureSettings.registerContentObserverForUser(
- Settings.Secure.getUriFor(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON),
+ Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
false,
mVolumeSettingObserver,
UserHandle.USER_ALL);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 48e58fc..6c532a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -35,7 +35,6 @@
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings.Global;
import android.service.notification.ZenModeConfig;
@@ -58,6 +57,7 @@
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.CastController;
@@ -136,6 +136,7 @@
private final UserInfoController mUserInfoController;
private final IActivityManager mIActivityManager;
private final UserManager mUserManager;
+ private final UserTracker mUserTracker;
private final DevicePolicyManager mDevicePolicyManager;
private final StatusBarIconController mIconController;
private final CommandQueue mCommandQueue;
@@ -176,7 +177,7 @@
KeyguardStateController keyguardStateController,
LocationController locationController,
SensorPrivacyController sensorPrivacyController, IActivityManager iActivityManager,
- AlarmManager alarmManager, UserManager userManager,
+ AlarmManager alarmManager, UserManager userManager, UserTracker userTracker,
DevicePolicyManager devicePolicyManager, RecordingController recordingController,
@Nullable TelecomManager telecomManager, @DisplayId int displayId,
@Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil,
@@ -196,6 +197,7 @@
mUserInfoController = userInfoController;
mIActivityManager = iActivityManager;
mUserManager = userManager;
+ mUserTracker = userTracker;
mDevicePolicyManager = devicePolicyManager;
mRotationLockController = rotationLockController;
mDataSaver = dataSaverController;
@@ -366,7 +368,7 @@
}
private void updateAlarm() {
- final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
+ final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(mUserTracker.getUserId());
final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
int zen = mZenController.getZen();
final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index f784723..80093a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -791,20 +791,28 @@
if (!mScreenOffAnimationController.shouldExpandNotifications()
&& !mAnimatingPanelExpansionOnUnlock
&& !occluding) {
- float behindFraction = getInterpolatedFraction();
- behindFraction = (float) Math.pow(behindFraction, 0.8f);
if (mClipsQsScrim) {
+ float behindFraction = getInterpolatedFraction();
+ behindFraction = (float) Math.pow(behindFraction, 0.8f);
mBehindAlpha = mTransparentScrimBackground ? 0 : 1;
mNotificationsAlpha =
mTransparentScrimBackground ? 0 : behindFraction * mDefaultScrimAlpha;
} else {
- mBehindAlpha =
- mTransparentScrimBackground ? 0 : behindFraction * mDefaultScrimAlpha;
- // Delay fade-in of notification scrim a bit further, to coincide with the
- // view fade in. Otherwise the empty panel can be quite jarring.
- mNotificationsAlpha = mTransparentScrimBackground
- ? 0 : MathUtils.constrainedMap(0f, 1f, 0.3f, 0.75f,
- mPanelExpansionFraction);
+ if (mTransparentScrimBackground) {
+ mBehindAlpha = 0;
+ mNotificationsAlpha = 0;
+ } else {
+ // Behind scrim will finish fading in at 30% expansion.
+ float behindFraction = MathUtils
+ .constrainedMap(0f, 1f, 0f, 0.3f, mPanelExpansionFraction);
+ mBehindAlpha = behindFraction * mDefaultScrimAlpha;
+ // Delay fade-in of notification scrim a bit further, to coincide with the
+ // behind scrim finishing fading in.
+ // Also to coincide with the view starting to fade in, otherwise the empty
+ // panel can be quite jarring.
+ mNotificationsAlpha = MathUtils
+ .constrainedMap(0f, 1f, 0.3f, 0.75f, mPanelExpansionFraction);
+ }
}
mBehindTint = mState.getBehindTint();
mInFrontAlpha = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index be06b05..8e22bf4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -1147,7 +1147,7 @@
if (hideImmediately) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
} else {
- mNotificationPanelViewController.expandWithoutQs();
+ mNotificationPanelViewController.expandShadeToNotifications();
}
}
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index be6e0cc..078a00d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -53,6 +53,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.NotificationClickNotifier;
@@ -118,6 +119,7 @@
private final NotificationPanelViewController mNotificationPanel;
private final ActivityLaunchAnimator mActivityLaunchAnimator;
private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
+ private final UserTracker mUserTracker;
private final OnUserInteractionCallback mOnUserInteractionCallback;
private boolean mIsCollapsingToShowActivityOverLockscreen;
@@ -153,7 +155,8 @@
ActivityLaunchAnimator activityLaunchAnimator,
NotificationLaunchAnimatorControllerProvider notificationAnimationProvider,
LaunchFullScreenIntentProvider launchFullScreenIntentProvider,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ UserTracker userTracker) {
mContext = context;
mMainThreadHandler = mainThreadHandler;
mUiBgExecutor = uiBgExecutor;
@@ -184,6 +187,7 @@
mNotificationPanel = panel;
mActivityLaunchAnimator = activityLaunchAnimator;
mNotificationAnimationProvider = notificationAnimationProvider;
+ mUserTracker = userTracker;
launchFullScreenIntentProvider.registerListener(entry -> launchFullScreenIntent(entry));
}
@@ -518,7 +522,7 @@
intent.getPackage(),
(adapter) -> tsb.startActivities(
getActivityOptions(mCentralSurfaces.getDisplayId(), adapter),
- UserHandle.CURRENT));
+ mUserTracker.getUserHandle()));
});
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 9f3fd72..be6d5ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -320,7 +320,7 @@
mAnimationScheduler.addCallback(this);
mSecureSettings.registerContentObserverForUser(
- Settings.Secure.getUriFor(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON),
+ Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
false,
mVolumeSettingObserver,
UserHandle.USER_ALL);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index f8c17e8..4866f73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -303,7 +303,8 @@
mEntry.mRemoteEditImeVisible = editTextRootWindowInsets != null
&& editTextRootWindowInsets.isVisible(WindowInsets.Type.ime());
if (!mEntry.mRemoteEditImeVisible && !mEditText.mShowImeOnInputConnection) {
- mController.removeRemoteInput(mEntry, mToken);
+ // Pass null to ensure all inputs are cleared for this entry b/227115380
+ mController.removeRemoteInput(mEntry, null);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index ad48e21..df8d161 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -31,6 +31,7 @@
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT
import androidx.annotation.CallSuper
+import androidx.annotation.VisibleForTesting
import com.android.systemui.CoreStartable
import com.android.systemui.Dumpable
import com.android.systemui.dagger.qualifiers.Main
@@ -108,9 +109,10 @@
* Whenever the current view disappears, the next-priority view will be displayed if it's still
* valid.
*/
+ @VisibleForTesting
internal val activeViews: MutableList<DisplayInfo> = mutableListOf()
- private fun getCurrentDisplayInfo(): DisplayInfo? {
+ internal fun getCurrentDisplayInfo(): DisplayInfo? {
return activeViews.getOrNull(0)
}
@@ -119,15 +121,26 @@
dumpManager.registerNormalDumpable(this)
}
+ private val listeners: MutableSet<Listener> = mutableSetOf()
+
+ /** Registers a listener. */
+ fun registerListener(listener: Listener) {
+ listeners.add(listener)
+ }
+
+ /** Unregisters a listener. */
+ fun unregisterListener(listener: Listener) {
+ listeners.remove(listener)
+ }
+
/**
* Displays the view with the provided [newInfo].
*
* This method handles inflating and attaching the view, then delegates to [updateView] to
* display the correct information in the view.
- * @param onViewTimeout a runnable that runs after the view timeout.
*/
@Synchronized
- fun displayView(newInfo: T, onViewTimeout: Runnable? = null) {
+ fun displayView(newInfo: T) {
val timeout = accessibilityManager.getRecommendedTimeoutMillis(
newInfo.timeoutMs,
// Not all views have controls so FLAG_CONTENT_CONTROLS might be superfluous, but
@@ -146,14 +159,13 @@
logger.logViewUpdate(newInfo)
currentDisplayInfo.info = newInfo
currentDisplayInfo.timeExpirationMillis = timeExpirationMillis
- updateTimeout(currentDisplayInfo, timeout, onViewTimeout)
+ updateTimeout(currentDisplayInfo, timeout)
updateView(newInfo, view)
return
}
val newDisplayInfo = DisplayInfo(
info = newInfo,
- onViewTimeout = onViewTimeout,
timeExpirationMillis = timeExpirationMillis,
// Null values will be updated to non-null if/when this view actually gets displayed
view = null,
@@ -196,7 +208,7 @@
private fun showNewView(newDisplayInfo: DisplayInfo, timeout: Int) {
logger.logViewAddition(newDisplayInfo.info)
createAndAcquireWakeLock(newDisplayInfo)
- updateTimeout(newDisplayInfo, timeout, newDisplayInfo.onViewTimeout)
+ updateTimeout(newDisplayInfo, timeout)
inflateAndUpdateView(newDisplayInfo)
}
@@ -227,19 +239,16 @@
/**
* Creates a runnable that will remove [displayInfo] in [timeout] ms from now.
*
- * @param onViewTimeout an optional runnable that will be run if the view times out.
* @return a runnable that, when run, will *cancel* the view's timeout.
*/
- private fun updateTimeout(displayInfo: DisplayInfo, timeout: Int, onViewTimeout: Runnable?) {
+ private fun updateTimeout(displayInfo: DisplayInfo, timeout: Int) {
val cancelViewTimeout = mainExecutor.executeDelayed(
{
removeView(displayInfo.info.id, REMOVAL_REASON_TIMEOUT)
- onViewTimeout?.run()
},
timeout.toLong()
)
- displayInfo.onViewTimeout = onViewTimeout
// Cancel old view timeout and re-set it.
displayInfo.cancelViewTimeout?.run()
displayInfo.cancelViewTimeout = cancelViewTimeout
@@ -317,6 +326,9 @@
// event comes in while this view is animating out, we still display the new view
// appropriately.
activeViews.remove(displayInfo)
+ listeners.forEach {
+ it.onInfoPermanentlyRemoved(id)
+ }
// No need to time the view out since it's already gone
displayInfo.cancelViewTimeout?.run()
@@ -380,6 +392,9 @@
invalidViews.forEach {
activeViews.remove(it)
logger.logViewExpiration(it.info)
+ listeners.forEach { listener ->
+ listener.onInfoPermanentlyRemoved(it.info.id)
+ }
}
}
@@ -436,6 +451,15 @@
onAnimationEnd.run()
}
+ /** A listener interface to be notified of various view events. */
+ fun interface Listener {
+ /**
+ * Called whenever a [DisplayInfo] with the given [id] has been removed and will never be
+ * displayed again (unless another call to [updateView] is made).
+ */
+ fun onInfoPermanentlyRemoved(id: String)
+ }
+
/** A container for all the display-related state objects. */
inner class DisplayInfo(
/**
@@ -461,11 +485,6 @@
var wakeLock: WakeLock?,
/**
- * See [displayView].
- */
- var onViewTimeout: Runnable?,
-
- /**
* A runnable that, when run, will cancel this view's timeout.
*
* Null if this info isn't currently being displayed.
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index 04b1a50..9e0bbb7 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -80,6 +80,7 @@
powerManager: PowerManager,
private val falsingManager: FalsingManager,
private val falsingCollector: FalsingCollector,
+ private val swipeChipbarAwayGestureHandler: SwipeChipbarAwayGestureHandler?,
private val viewUtil: ViewUtil,
private val vibratorHelper: VibratorHelper,
wakeLockBuilder: WakeLock.Builder,
@@ -105,6 +106,8 @@
commonWindowLayoutParams.apply { gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL) }
override fun updateView(newInfo: ChipbarInfo, currentView: ViewGroup) {
+ updateGestureListening()
+
logger.logViewUpdate(
newInfo.windowTitle,
newInfo.text.loadText(context),
@@ -228,6 +231,42 @@
includeMargins = true,
onAnimationEnd,
)
+
+ updateGestureListening()
+ }
+
+ private fun updateGestureListening() {
+ if (swipeChipbarAwayGestureHandler == null) {
+ return
+ }
+
+ val currentDisplayInfo = getCurrentDisplayInfo()
+ if (currentDisplayInfo != null && currentDisplayInfo.info.allowSwipeToDismiss) {
+ swipeChipbarAwayGestureHandler.setViewFetcher { currentDisplayInfo.view }
+ swipeChipbarAwayGestureHandler.addOnGestureDetectedCallback(TAG) {
+ onSwipeUpGestureDetected()
+ }
+ } else {
+ swipeChipbarAwayGestureHandler.resetViewFetcher()
+ swipeChipbarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
+ }
+ }
+
+ private fun onSwipeUpGestureDetected() {
+ val currentDisplayInfo = getCurrentDisplayInfo()
+ if (currentDisplayInfo == null) {
+ logger.logSwipeGestureError(id = null, errorMsg = "No info is being displayed")
+ return
+ }
+ if (!currentDisplayInfo.info.allowSwipeToDismiss) {
+ logger.logSwipeGestureError(
+ id = currentDisplayInfo.info.id,
+ errorMsg = "This view prohibits swipe-to-dismiss",
+ )
+ return
+ }
+ removeView(currentDisplayInfo.info.id, SWIPE_UP_GESTURE_REASON)
+ updateGestureListening()
}
private fun ViewGroup.getInnerView(): ViewGroup {
@@ -250,3 +289,5 @@
private const val ANIMATION_IN_DURATION = 500L
private const val ANIMATION_OUT_DURATION = 250L
@IdRes private val INFO_TAG = R.id.tag_chipbar_info
+private const val SWIPE_UP_GESTURE_REASON = "SWIPE_UP_GESTURE_DETECTED"
+private const val TAG = "ChipbarCoordinator"
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
index dd4bd26..fe46318 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
@@ -33,12 +33,14 @@
* @property endItem an optional end item to display at the end of the chipbar (on the right in LTR
* locales; on the left in RTL locales).
* @property vibrationEffect an optional vibration effect when the chipbar is displayed
+ * @property allowSwipeToDismiss true if users are allowed to swipe up to dismiss this chipbar.
*/
data class ChipbarInfo(
val startIcon: TintedIcon,
val text: Text,
val endItem: ChipbarEndItem?,
val vibrationEffect: VibrationEffect? = null,
+ val allowSwipeToDismiss: Boolean = false,
override val windowTitle: String,
override val wakeReason: String,
override val timeoutMs: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt
index fcfbe0a..f239428 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt
@@ -46,4 +46,16 @@
{ "Chipbar updated. window=$str1 text=$str2 endItem=$str3" }
)
}
+
+ fun logSwipeGestureError(id: String?, errorMsg: String) {
+ buffer.log(
+ tag,
+ LogLevel.WARNING,
+ {
+ str1 = id
+ str2 = errorMsg
+ },
+ { "Chipbar swipe gesture detected for incorrect state. id=$str1 error=$str2" }
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt
new file mode 100644
index 0000000..6e3cb48
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.temporarydisplay.chipbar
+
+import android.content.Context
+import android.view.MotionEvent
+import android.view.View
+import com.android.systemui.statusbar.gesture.SwipeUpGestureHandler
+import com.android.systemui.statusbar.gesture.SwipeUpGestureLogger
+import com.android.systemui.util.boundsOnScreen
+
+/**
+ * A class to detect when a user has swiped the chipbar away.
+ *
+ * Effectively [SysUISingleton]. But, this shouldn't be created if the gesture isn't enabled. See
+ * [TemporaryDisplayModule.provideSwipeChipbarAwayGestureHandler].
+ */
+class SwipeChipbarAwayGestureHandler(
+ context: Context,
+ logger: SwipeUpGestureLogger,
+) : SwipeUpGestureHandler(context, logger, loggerTag = LOGGER_TAG) {
+
+ private var viewFetcher: () -> View? = { null }
+
+ override fun startOfGestureIsWithinBounds(ev: MotionEvent): Boolean {
+ val view = viewFetcher.invoke() ?: return false
+ // Since chipbar is in its own window, we need to use [boundsOnScreen] to get an accurate
+ // bottom. ([view.bottom] would be relative to its window, which would be too small.)
+ val viewBottom = view.boundsOnScreen.bottom
+ // Allow the gesture to start a bit below the chipbar
+ return ev.y <= 1.5 * viewBottom
+ }
+
+ /**
+ * Sets a fetcher that returns the current chipbar view. The fetcher will be invoked whenever a
+ * gesture starts to determine if the gesture is near the chipbar.
+ */
+ fun setViewFetcher(fetcher: () -> View?) {
+ viewFetcher = fetcher
+ }
+
+ /** Removes the current view fetcher. */
+ fun resetViewFetcher() {
+ viewFetcher = { null }
+ }
+}
+
+private const val LOGGER_TAG = "SwipeChipbarAway"
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt
index cf0a183..933c060 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt
@@ -16,22 +16,38 @@
package com.android.systemui.temporarydisplay.dagger
+import android.content.Context
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.statusbar.gesture.SwipeUpGestureLogger
+import com.android.systemui.temporarydisplay.chipbar.SwipeChipbarAwayGestureHandler
import dagger.Module
import dagger.Provides
@Module
interface TemporaryDisplayModule {
- @Module
companion object {
- @JvmStatic
@Provides
@SysUISingleton
@ChipbarLog
fun provideChipbarLogBuffer(factory: LogBufferFactory): LogBuffer {
return factory.create("ChipbarLog", 40)
}
+
+ @Provides
+ @SysUISingleton
+ fun provideSwipeChipbarAwayGestureHandler(
+ mediaTttFlags: MediaTttFlags,
+ context: Context,
+ logger: SwipeUpGestureLogger,
+ ): SwipeChipbarAwayGestureHandler? {
+ return if (mediaTttFlags.isMediaTttDismissGestureEnabled()) {
+ SwipeChipbarAwayGestureHandler(context, logger)
+ } else {
+ null
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 3ecb15b..f29ca4d 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -26,7 +26,6 @@
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_SOURCE;
import static com.android.systemui.theme.ThemeOverlayApplier.TIMESTAMP_FIELD;
-import android.annotation.Nullable;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.app.WallpaperManager.OnColorsChangedListener;
@@ -70,6 +69,7 @@
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.monet.ColorScheme;
import com.android.systemui.monet.Style;
+import com.android.systemui.monet.TonalPalette;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -391,7 +391,7 @@
mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mMainExecutor,
UserHandle.ALL);
mSecureSettings.registerContentObserverForUser(
- Settings.Secure.getUriFor(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES),
+ Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
false,
new ContentObserver(mBgHandler) {
@Override
@@ -513,39 +513,42 @@
/**
* Given a color candidate, return an overlay definition.
*/
- protected @Nullable FabricatedOverlay getOverlay(int color, int type, Style style) {
+ protected FabricatedOverlay getOverlay(int color, int type, Style style) {
boolean nightMode = (mResources.getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
mColorScheme = new ColorScheme(color, nightMode, style);
- List<Integer> colorShades = type == ACCENT
- ? mColorScheme.getAllAccentColors() : mColorScheme.getAllNeutralColors();
String name = type == ACCENT ? "accent" : "neutral";
- int paletteSize = mColorScheme.getAccent1().size();
+
FabricatedOverlay.Builder overlay =
new FabricatedOverlay.Builder("com.android.systemui", name, "android");
- for (int i = 0; i < colorShades.size(); i++) {
- int luminosity = i % paletteSize;
- int paletteIndex = i / paletteSize + 1;
- String resourceName;
- switch (luminosity) {
- case 0:
- resourceName = "android:color/system_" + name + paletteIndex + "_10";
- break;
- case 1:
- resourceName = "android:color/system_" + name + paletteIndex + "_50";
- break;
- default:
- int l = luminosity - 1;
- resourceName = "android:color/system_" + name + paletteIndex + "_" + l + "00";
- }
- overlay.setResourceValue(resourceName, TypedValue.TYPE_INT_COLOR_ARGB8,
- ColorUtils.setAlphaComponent(colorShades.get(i), 0xFF));
+
+ if (type == ACCENT) {
+ assignTonalPaletteToOverlay("accent1", overlay, mColorScheme.getAccent1());
+ assignTonalPaletteToOverlay("accent2", overlay, mColorScheme.getAccent2());
+ assignTonalPaletteToOverlay("accent3", overlay, mColorScheme.getAccent3());
+ } else {
+ assignTonalPaletteToOverlay("neutral1", overlay, mColorScheme.getNeutral1());
+ assignTonalPaletteToOverlay("neutral2", overlay, mColorScheme.getNeutral2());
}
return overlay.build();
}
+ private void assignTonalPaletteToOverlay(String name,
+ FabricatedOverlay.Builder overlay, TonalPalette tonalPalette) {
+
+ String resourcePrefix = "android:color/system_" + name;
+ int colorDataType = TypedValue.TYPE_INT_COLOR_ARGB8;
+
+ tonalPalette.getAllShadesMapped().forEach((key, value) -> {
+ String resourceName = resourcePrefix + "_" + key;
+ int colorValue = ColorUtils.setAlphaComponent(value, 0xFF);
+ overlay.setResourceValue(resourceName, colorDataType,
+ colorValue);
+ });
+ }
+
/**
* Checks if the color scheme in mColorScheme matches the current system palettes.
* @param managedProfiles List of managed profiles for this user.
@@ -557,15 +560,15 @@
Resources res = userHandle.isSystem()
? mResources : mContext.createContextAsUser(userHandle, 0).getResources();
if (!(res.getColor(android.R.color.system_accent1_500, mContext.getTheme())
- == mColorScheme.getAccent1().get(6)
+ == mColorScheme.getAccent1().getS500()
&& res.getColor(android.R.color.system_accent2_500, mContext.getTheme())
- == mColorScheme.getAccent2().get(6)
+ == mColorScheme.getAccent2().getS500()
&& res.getColor(android.R.color.system_accent3_500, mContext.getTheme())
- == mColorScheme.getAccent3().get(6)
+ == mColorScheme.getAccent3().getS500()
&& res.getColor(android.R.color.system_neutral1_500, mContext.getTheme())
- == mColorScheme.getNeutral1().get(6)
+ == mColorScheme.getNeutral1().getS500()
&& res.getColor(android.R.color.system_neutral2_500, mContext.getTheme())
- == mColorScheme.getNeutral2().get(6))) {
+ == mColorScheme.getNeutral2().getS500())) {
return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 523cf68..0069bb5 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -36,6 +36,8 @@
import android.view.WindowManager
import android.view.WindowlessWindowManager
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.LinearLightRevealEffect
@@ -57,6 +59,7 @@
@Inject
constructor(
private val context: Context,
+ private val featureFlags: FeatureFlags,
private val deviceStateManager: DeviceStateManager,
private val contentResolver: ContentResolver,
private val displayManager: DisplayManager,
@@ -81,6 +84,7 @@
private var scrimView: LightRevealScrim? = null
private var isFolded: Boolean = false
private var isUnfoldHandled: Boolean = true
+ private var overlayAddReason: AddOverlayReason? = null
private var currentRotation: Int = context.display!!.rotation
@@ -158,6 +162,8 @@
ensureInBackground()
ensureOverlayRemoved()
+ overlayAddReason = reason
+
val newRoot = SurfaceControlViewHost(context, context.display!!, wwm)
val params = getLayoutParams()
val newView =
@@ -170,11 +176,7 @@
.apply {
revealEffect = createLightRevealEffect()
isScrimOpaqueChangedListener = Consumer {}
- revealAmount =
- when (reason) {
- FOLD -> TRANSPARENT
- UNFOLD -> BLACK
- }
+ revealAmount = calculateRevealAmount()
}
newRoot.setView(newView, params)
@@ -207,6 +209,31 @@
root = newRoot
}
+ private fun calculateRevealAmount(animationProgress: Float? = null): Float {
+ val overlayAddReason = overlayAddReason ?: UNFOLD
+
+ if (animationProgress == null) {
+ // Animation progress is unknown, calculate the initial value based on the overlay
+ // add reason
+ return when (overlayAddReason) {
+ FOLD -> TRANSPARENT
+ UNFOLD -> BLACK
+ }
+ }
+
+ val showVignetteWhenFolding =
+ featureFlags.isEnabled(Flags.ENABLE_DARK_VIGNETTE_WHEN_FOLDING)
+
+ return if (!showVignetteWhenFolding && overlayAddReason == FOLD) {
+ // Do not darken the content when SHOW_VIGNETTE_WHEN_FOLDING flag is off
+ // and we are folding the device. We still add the overlay to block touches
+ // while the animation is running but the overlay is transparent.
+ TRANSPARENT
+ } else {
+ animationProgress
+ }
+ }
+
private fun getLayoutParams(): WindowManager.LayoutParams {
val params: WindowManager.LayoutParams = WindowManager.LayoutParams()
@@ -259,7 +286,7 @@
private inner class TransitionListener : TransitionProgressListener {
override fun onTransitionProgress(progress: Float) {
- executeInBackground { scrimView?.revealAmount = progress }
+ executeInBackground { scrimView?.revealAmount = calculateRevealAmount(progress) }
}
override fun onTransitionFinished() {
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/AddUserDialog.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/AddUserDialog.kt
index a9d66de..5a25a4e 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/AddUserDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/AddUserDialog.kt
@@ -63,11 +63,10 @@
}
// Use broadcast instead of ShadeController, as this dialog may have started in
- // another
- // process where normal dagger bindings are not available.
+ // another process where normal dagger bindings are not available.
broadcastSender.sendBroadcastAsUser(
Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
- UserHandle.CURRENT
+ userHandle
)
context.startActivityAsUser(
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
index b61b2e6..47d505e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -16,14 +16,21 @@
package com.android.systemui.util.kotlin
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.util.time.SystemClockImpl
+import kotlinx.coroutines.CoroutineStart
import java.util.concurrent.atomic.AtomicReference
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
+import kotlin.math.max
/**
* Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
@@ -167,3 +174,89 @@
* Note that the returned Flow will not emit anything until [other] has emitted at least one value.
*/
fun <A> Flow<*>.sample(other: Flow<A>): Flow<A> = sample(other) { _, a -> a }
+
+/**
+ * Returns a flow that mirrors the original flow, but delays values following emitted values for the
+ * given [periodMs]. If the original flow emits more than one value during this period, only the
+ * latest value is emitted.
+ *
+ * Example:
+ *
+ * ```kotlin
+ * flow {
+ * emit(1) // t=0ms
+ * delay(90)
+ * emit(2) // t=90ms
+ * delay(90)
+ * emit(3) // t=180ms
+ * delay(1010)
+ * emit(4) // t=1190ms
+ * delay(1010)
+ * emit(5) // t=2200ms
+ * }.throttle(1000)
+ * ```
+ *
+ * produces the following emissions at the following times
+ *
+ * ```text
+ * 1 (t=0ms), 3 (t=1000ms), 4 (t=2000ms), 5 (t=3000ms)
+ * ```
+ */
+fun <T> Flow<T>.throttle(periodMs: Long): Flow<T> = this.throttle(periodMs, SystemClockImpl())
+
+/**
+ * Returns a flow that mirrors the original flow, but delays values following emitted values for the
+ * given [periodMs] as reported by the given [clock]. If the original flow emits more than one value
+ * during this period, only The latest value is emitted.
+ *
+ * Example:
+ *
+ * ```kotlin
+ * flow {
+ * emit(1) // t=0ms
+ * delay(90)
+ * emit(2) // t=90ms
+ * delay(90)
+ * emit(3) // t=180ms
+ * delay(1010)
+ * emit(4) // t=1190ms
+ * delay(1010)
+ * emit(5) // t=2200ms
+ * }.throttle(1000)
+ * ```
+ *
+ * produces the following emissions at the following times
+ *
+ * ```text
+ * 1 (t=0ms), 3 (t=1000ms), 4 (t=2000ms), 5 (t=3000ms)
+ * ```
+ */
+fun <T> Flow<T>.throttle(periodMs: Long, clock: SystemClock): Flow<T> = channelFlow {
+ coroutineScope {
+ var previousEmitTimeMs = 0L
+ var delayJob: Job? = null
+ var sendJob: Job? = null
+ val outerScope = this
+
+ collect {
+ delayJob?.cancel()
+ sendJob?.join()
+ val currentTimeMs = clock.elapsedRealtime()
+ val timeSinceLastEmit = currentTimeMs - previousEmitTimeMs
+ val timeUntilNextEmit = max(0L, periodMs - timeSinceLastEmit)
+ if (timeUntilNextEmit > 0L) {
+ // We create delayJob to allow cancellation during the delay period
+ delayJob = launch {
+ delay(timeUntilNextEmit)
+ sendJob = outerScope.launch(start = CoroutineStart.UNDISPATCHED) {
+ send(it)
+ previousEmitTimeMs = clock.elapsedRealtime()
+ }
+ }
+ } else {
+ send(it)
+ previousEmitTimeMs = currentTimeMs
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java b/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java
index 4351afe..a0d22f3 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java
@@ -29,12 +29,12 @@
import android.net.Uri;
import android.os.Debug;
import android.os.SystemProperties;
-import android.os.UserHandle;
import android.util.Log;
import androidx.core.content.FileProvider;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.settings.UserTracker;
import com.google.android.collect.Lists;
@@ -62,13 +62,15 @@
static final String LEAK_DUMP = "leak.dump";
private final Context mContext;
+ private final UserTracker mUserTracker;
private final LeakDetector mLeakDetector;
private final String mLeakReportEmail;
@Inject
- public LeakReporter(Context context, LeakDetector leakDetector,
+ public LeakReporter(Context context, UserTracker userTracker, LeakDetector leakDetector,
@Nullable @Named(LEAK_REPORT_EMAIL_NAME) String leakReportEmail) {
mContext = context;
+ mUserTracker = userTracker;
mLeakDetector = leakDetector;
mLeakReportEmail = leakReportEmail;
}
@@ -111,7 +113,7 @@
getIntent(hprofFile, dumpFile),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE,
null,
- UserHandle.CURRENT));
+ mUserTracker.getUserHandle()));
notiMan.notify(TAG, 0, builder.build());
} catch (IOException e) {
Log.e(TAG, "Couldn't dump heap for leak", e);
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
index 0b2f004..31f35fc 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
@@ -1,15 +1,17 @@
/*
* Copyright (C) 2017 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
+ * 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.
+ * 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.util.leak;
@@ -26,7 +28,27 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-public class RotationUtils {
+/**
+ * Utility class that provides device orientation.
+ *
+ * <p>Consider using {@link Surface.Rotation} or add a function that respects device aspect ratio
+ * and {@code android.internal.R.bool.config_reverseDefaultRotation}.
+ *
+ * <p>If you only care about the rotation, use {@link Surface.Rotation}, as it always gives the
+ * counter clock-wise rotation. (e.g. If you have a device that has a charging port at the bottom,
+ * rotating three times in counter clock direction will give you {@link Surface#ROTATION_270} while
+ * having the charging port on the left side of the device.)
+ *
+ * <p>If you need whether the device is in portrait or landscape (or their opposites), please add a
+ * function here that respects the device aspect ratio and
+ * {@code android.internal.R.bool.config_reverseDefaultRotation} together.
+ *
+ * <p>Note that {@code android.internal.R.bool.config_reverseDefaultRotation} does not change the
+ * winding order. In other words, the rotation order (counter clock-wise) will remain the same. It
+ * only flips the device orientation, such that portrait becomes upside down, landscape becomes
+ * seascape.
+ */
+public final class RotationUtils {
public static final int ROTATION_NONE = 0;
public static final int ROTATION_LANDSCAPE = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
index 1a30b0a..85fada2 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
@@ -20,14 +20,18 @@
import android.net.Uri;
import android.provider.Settings;
+import com.android.systemui.settings.UserTracker;
+
import javax.inject.Inject;
class GlobalSettingsImpl implements GlobalSettings {
private final ContentResolver mContentResolver;
+ private final UserTracker mUserTracker;
@Inject
- GlobalSettingsImpl(ContentResolver contentResolver) {
+ GlobalSettingsImpl(ContentResolver contentResolver, UserTracker userTracker) {
mContentResolver = contentResolver;
+ mUserTracker = userTracker;
}
@Override
@@ -36,13 +40,19 @@
}
@Override
+ public UserTracker getUserTracker() {
+ return mUserTracker;
+ }
+
+ @Override
public Uri getUriFor(String name) {
return Settings.Global.getUriFor(name);
}
@Override
public String getStringForUser(String name, int userHandle) {
- return Settings.Global.getStringForUser(mContentResolver, name, userHandle);
+ return Settings.Global.getStringForUser(mContentResolver, name,
+ getRealUserHandle(userHandle));
}
@Override
@@ -53,14 +63,16 @@
@Override
public boolean putStringForUser(String name, String value, int userHandle) {
- return Settings.Global.putStringForUser(mContentResolver, name, value, userHandle);
+ return Settings.Global.putStringForUser(mContentResolver, name, value,
+ getRealUserHandle(userHandle));
}
@Override
public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
int userHandle, boolean overrideableByRestore) {
return Settings.Global.putStringForUser(
- mContentResolver, name, value, tag, makeDefault, userHandle, overrideableByRestore);
+ mContentResolver, name, value, tag, makeDefault, getRealUserHandle(userHandle),
+ overrideableByRestore);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
index 020c234..f995436 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
@@ -20,14 +20,18 @@
import android.net.Uri;
import android.provider.Settings;
+import com.android.systemui.settings.UserTracker;
+
import javax.inject.Inject;
class SecureSettingsImpl implements SecureSettings {
private final ContentResolver mContentResolver;
+ private final UserTracker mUserTracker;
@Inject
- SecureSettingsImpl(ContentResolver contentResolver) {
+ SecureSettingsImpl(ContentResolver contentResolver, UserTracker userTracker) {
mContentResolver = contentResolver;
+ mUserTracker = userTracker;
}
@Override
@@ -36,13 +40,19 @@
}
@Override
+ public UserTracker getUserTracker() {
+ return mUserTracker;
+ }
+
+ @Override
public Uri getUriFor(String name) {
return Settings.Secure.getUriFor(name);
}
@Override
public String getStringForUser(String name, int userHandle) {
- return Settings.Secure.getStringForUser(mContentResolver, name, userHandle);
+ return Settings.Secure.getStringForUser(mContentResolver, name,
+ getRealUserHandle(userHandle));
}
@Override
@@ -52,14 +62,16 @@
@Override
public boolean putStringForUser(String name, String value, int userHandle) {
- return Settings.Secure.putStringForUser(mContentResolver, name, value, userHandle);
+ return Settings.Secure.putStringForUser(mContentResolver, name, value,
+ getRealUserHandle(userHandle));
}
@Override
public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
int userHandle, boolean overrideableByRestore) {
return Settings.Secure.putStringForUser(
- mContentResolver, name, value, tag, makeDefault, userHandle, overrideableByRestore);
+ mContentResolver, name, value, tag, makeDefault, getRealUserHandle(userHandle),
+ overrideableByRestore);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
index 1bf5f07..b6846a3 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
@@ -22,8 +22,11 @@
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
+import android.os.UserHandle;
import android.provider.Settings;
+import com.android.systemui.settings.UserTracker;
+
/**
* Used to interact with Settings.Secure, Settings.Global, and Settings.System.
*
@@ -46,6 +49,11 @@
ContentResolver getContentResolver();
/**
+ * Returns that {@link UserTracker} this instance was constructed with.
+ */
+ UserTracker getUserTracker();
+
+ /**
* Returns the user id for the associated {@link ContentResolver}.
*/
default int getUserId() {
@@ -53,6 +61,17 @@
}
/**
+ * Returns the actual current user handle when querying with the current user. Otherwise,
+ * returns the passed in user id.
+ */
+ default int getRealUserHandle(int userHandle) {
+ if (userHandle != UserHandle.USER_CURRENT) {
+ return userHandle;
+ }
+ return getUserTracker().getUserId();
+ }
+
+ /**
* Construct the content URI for a particular name/value pair,
* useful for monitoring changes with a ContentObserver.
* @param name to look up in the table
@@ -84,18 +103,18 @@
*
* Implicitly calls {@link #getUriFor(String)} on the passed in name.
*/
- default void registerContentObserver(String name, boolean notifyForDescendents,
+ default void registerContentObserver(String name, boolean notifyForDescendants,
ContentObserver settingsObserver) {
- registerContentObserver(getUriFor(name), notifyForDescendents, settingsObserver);
+ registerContentObserver(getUriFor(name), notifyForDescendants, settingsObserver);
}
/**
* Convenience wrapper around
* {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
*/
- default void registerContentObserver(Uri uri, boolean notifyForDescendents,
+ default void registerContentObserver(Uri uri, boolean notifyForDescendants,
ContentObserver settingsObserver) {
- registerContentObserverForUser(uri, notifyForDescendents, settingsObserver, getUserId());
+ registerContentObserverForUser(uri, notifyForDescendants, settingsObserver, getUserId());
}
/**
@@ -127,10 +146,10 @@
* Implicitly calls {@link #getUriFor(String)} on the passed in name.
*/
default void registerContentObserverForUser(
- String name, boolean notifyForDescendents, ContentObserver settingsObserver,
+ String name, boolean notifyForDescendants, ContentObserver settingsObserver,
int userHandle) {
registerContentObserverForUser(
- getUriFor(name), notifyForDescendents, settingsObserver, userHandle);
+ getUriFor(name), notifyForDescendants, settingsObserver, userHandle);
}
/**
@@ -138,10 +157,10 @@
* {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
*/
default void registerContentObserverForUser(
- Uri uri, boolean notifyForDescendents, ContentObserver settingsObserver,
+ Uri uri, boolean notifyForDescendants, ContentObserver settingsObserver,
int userHandle) {
getContentResolver().registerContentObserver(
- uri, notifyForDescendents, settingsObserver, userHandle);
+ uri, notifyForDescendants, settingsObserver, getRealUserHandle(userHandle));
}
/** See {@link ContentResolver#unregisterContentObserver(ContentObserver)}. */
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
index 0b8257d..561495e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
@@ -19,7 +19,6 @@
import android.annotation.UserIdInt
import android.database.ContentObserver
-import android.os.UserHandle
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -29,8 +28,8 @@
/** Returns a flow of [Unit] that is invoked each time that content is updated. */
fun SettingsProxy.observerFlow(
+ @UserIdInt userId: Int,
vararg names: String,
- @UserIdInt userId: Int = UserHandle.USER_CURRENT,
): Flow<Unit> {
return conflatedCallbackFlow {
val observer =
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
index 0dbb76f..fba7ddf 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
@@ -20,14 +20,18 @@
import android.net.Uri;
import android.provider.Settings;
+import com.android.systemui.settings.UserTracker;
+
import javax.inject.Inject;
class SystemSettingsImpl implements SystemSettings {
private final ContentResolver mContentResolver;
+ private final UserTracker mUserTracker;
@Inject
- SystemSettingsImpl(ContentResolver contentResolver) {
+ SystemSettingsImpl(ContentResolver contentResolver, UserTracker userTracker) {
mContentResolver = contentResolver;
+ mUserTracker = userTracker;
}
@Override
@@ -36,13 +40,19 @@
}
@Override
+ public UserTracker getUserTracker() {
+ return mUserTracker;
+ }
+
+ @Override
public Uri getUriFor(String name) {
return Settings.System.getUriFor(name);
}
@Override
public String getStringForUser(String name, int userHandle) {
- return Settings.System.getStringForUser(mContentResolver, name, userHandle);
+ return Settings.System.getStringForUser(mContentResolver, name,
+ getRealUserHandle(userHandle));
}
@Override
@@ -52,7 +62,8 @@
@Override
public boolean putStringForUser(String name, String value, int userHandle) {
- return Settings.System.putStringForUser(mContentResolver, name, value, userHandle);
+ return Settings.System.putStringForUser(mContentResolver, name, value,
+ getRealUserHandle(userHandle));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index f71d988..a453726 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -25,6 +25,7 @@
import android.provider.Settings;
import android.view.WindowManager.LayoutParams;
+import com.android.internal.R;
import com.android.settingslib.applications.InterestingConfigChanges;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoMode;
@@ -55,7 +56,7 @@
public static final String VOLUME_UP_SILENT = "sysui_volume_up_silent";
public static final String VOLUME_SILENT_DO_NOT_DISTURB = "sysui_do_not_disturb";
- public static final boolean DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT = false;
+ private final boolean mDefaultVolumeDownToEnterSilent;
public static final boolean DEFAULT_VOLUME_UP_TO_EXIT_SILENT = false;
public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = false;
@@ -72,12 +73,7 @@
private final KeyguardViewMediator mKeyguardViewMediator;
private final ActivityStarter mActivityStarter;
private VolumeDialog mDialog;
- private VolumePolicy mVolumePolicy = new VolumePolicy(
- DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT, // volumeDownToEnterSilent
- DEFAULT_VOLUME_UP_TO_EXIT_SILENT, // volumeUpToExitSilent
- DEFAULT_DO_NOT_DISTURB_WHEN_SILENT, // doNotDisturbWhenSilent
- 400 // vibrateToSilentDebounce
- );
+ private VolumePolicy mVolumePolicy;
@Inject
public VolumeDialogComponent(
@@ -107,7 +103,20 @@
mDialog = dialog;
mDialog.init(LayoutParams.TYPE_VOLUME_OVERLAY, mVolumeDialogCallback);
}).build();
+
+
+ mDefaultVolumeDownToEnterSilent = mContext.getResources()
+ .getBoolean(R.bool.config_volume_down_to_enter_silent);
+
+ mVolumePolicy = new VolumePolicy(
+ mDefaultVolumeDownToEnterSilent, // volumeDownToEnterSilent
+ DEFAULT_VOLUME_UP_TO_EXIT_SILENT, // volumeUpToExitSilent
+ DEFAULT_DO_NOT_DISTURB_WHEN_SILENT, // doNotDisturbWhenSilent
+ 400 // vibrateToSilentDebounce
+ );
+
applyConfiguration();
+
tunerService.addTunable(this, VOLUME_DOWN_SILENT, VOLUME_UP_SILENT,
VOLUME_SILENT_DO_NOT_DISTURB);
demoModeController.addCallback(this);
@@ -121,7 +130,7 @@
if (VOLUME_DOWN_SILENT.equals(key)) {
volumeDownToEnterSilent =
- TunerService.parseIntegerSwitch(newValue, DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT);
+ TunerService.parseIntegerSwitch(newValue, mDefaultVolumeDownToEnterSilent);
} else if (VOLUME_UP_SILENT.equals(key)) {
volumeUpToExitSilent =
TunerService.parseIntegerSwitch(newValue, DEFAULT_VOLUME_UP_TO_EXIT_SILENT);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 98d904e..89b66ee 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -46,7 +46,6 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.os.VibrationEffect;
import android.provider.Settings;
import android.service.notification.Condition;
@@ -69,6 +68,7 @@
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.qs.tiles.DndTile;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.RingerModeLiveData;
import com.android.systemui.util.RingerModeTracker;
@@ -133,6 +133,7 @@
private final CaptioningManager mCaptioningManager;
private final KeyguardManager mKeyguardManager;
private final ActivityManager mActivityManager;
+ private final UserTracker mUserTracker;
protected C mCallbacks = new C();
private final State mState = new State();
protected final MediaSessionsCallbacks mMediaSessionsCallbacksW;
@@ -180,6 +181,7 @@
CaptioningManager captioningManager,
KeyguardManager keyguardManager,
ActivityManager activityManager,
+ UserTracker userTracker,
DumpManager dumpManager
) {
mContext = context.getApplicationContext();
@@ -209,6 +211,7 @@
mCaptioningManager = captioningManager;
mKeyguardManager = keyguardManager;
mActivityManager = activityManager;
+ mUserTracker = userTracker;
dumpManager.registerDumpable("VolumeDialogControllerImpl", this);
boolean accessibilityVolumeStreamActive = accessibilityManager
@@ -371,7 +374,7 @@
if (System.currentTimeMillis() - mLastToggledRingerOn < TOUCH_FEEDBACK_TIMEOUT_MS) {
try {
mAudioService.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD,
- UserHandle.USER_CURRENT);
+ mUserTracker.getUserId());
} catch (RemoteException e) {
// ignore
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index 4cbc709..4da5d49 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -273,7 +273,7 @@
};
mSecureSettings.registerContentObserverForUser(
- Settings.Secure.getUriFor(Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT),
+ Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
false /* notifyForDescendants */,
mDefaultPaymentAppObserver,
UserHandle.USER_ALL);
@@ -293,7 +293,7 @@
};
mSecureSettings.registerContentObserverForUser(
- Settings.Secure.getUriFor(QuickAccessWalletClientImpl.SETTING_KEY),
+ QuickAccessWalletClientImpl.SETTING_KEY,
false /* notifyForDescendants */,
mWalletPreferenceObserver,
UserHandle.USER_ALL);
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt
new file mode 100644
index 0000000..7b8235a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt
@@ -0,0 +1,115 @@
+/*
+ * 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.wallet.controller
+
+import android.Manifest
+import android.content.Context
+import android.content.IntentFilter
+import android.service.quickaccesswallet.GetWalletCardsError
+import android.service.quickaccesswallet.GetWalletCardsResponse
+import android.service.quickaccesswallet.QuickAccessWalletClient
+import android.service.quickaccesswallet.WalletCard
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+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.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.shareIn
+
+@SysUISingleton
+class WalletContextualSuggestionsController
+@Inject
+constructor(
+ @Application private val applicationCoroutineScope: CoroutineScope,
+ private val walletController: QuickAccessWalletController,
+ broadcastDispatcher: BroadcastDispatcher,
+ featureFlags: FeatureFlags
+) {
+ private val allWalletCards: Flow<List<WalletCard>> =
+ if (featureFlags.isEnabled(Flags.ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS)) {
+ conflatedCallbackFlow {
+ val callback =
+ object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
+ override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
+ trySendWithFailureLogging(response.walletCards, TAG)
+ }
+
+ override fun onWalletCardRetrievalError(error: GetWalletCardsError) {
+ trySendWithFailureLogging(emptyList<WalletCard>(), TAG)
+ }
+ }
+
+ walletController.setupWalletChangeObservers(
+ callback,
+ QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE,
+ QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE
+ )
+ walletController.updateWalletPreference()
+ walletController.queryWalletCards(callback)
+
+ awaitClose {
+ walletController.unregisterWalletChangeObservers(
+ QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE,
+ QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE
+ )
+ }
+ }
+ } else {
+ emptyFlow()
+ }
+
+ private val contextualSuggestionsCardIds: Flow<Set<String>> =
+ if (featureFlags.isEnabled(Flags.ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS)) {
+ broadcastDispatcher.broadcastFlow(
+ filter = IntentFilter(ACTION_UPDATE_WALLET_CONTEXTUAL_SUGGESTIONS),
+ permission = Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE,
+ flags = Context.RECEIVER_EXPORTED
+ ) { intent, _ ->
+ if (intent.hasExtra(UPDATE_CARD_IDS_EXTRA)) {
+ intent.getStringArrayListExtra(UPDATE_CARD_IDS_EXTRA).toSet()
+ } else {
+ emptySet()
+ }
+ }
+ } else {
+ emptyFlow()
+ }
+
+ val contextualSuggestionCards: Flow<List<WalletCard>> =
+ combine(allWalletCards, contextualSuggestionsCardIds) { cards, ids ->
+ cards.filter { card -> ids.contains(card.cardId) }
+ }
+ .shareIn(applicationCoroutineScope, replay = 1, started = SharingStarted.Eagerly)
+
+ companion object {
+ private const val ACTION_UPDATE_WALLET_CONTEXTUAL_SUGGESTIONS =
+ "com.android.systemui.wallet.UPDATE_CONTEXTUAL_SUGGESTIONS"
+
+ private const val UPDATE_CARD_IDS_EXTRA = "cardIds"
+
+ private const val TAG = "WalletSuggestions"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index 1f1b32c..8b925b7 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -26,7 +26,6 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Trace;
-import android.os.UserHandle;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.Surface;
@@ -37,6 +36,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.concurrency.DelayableExecutor;
import java.io.FileDescriptor;
@@ -58,6 +58,8 @@
private volatile int mPages = 1;
private boolean mPagesComputed = false;
+ private final UserTracker mUserTracker;
+
// used for most tasks (call canvas.drawBitmap, load/unload the bitmap)
@Background
private final DelayableExecutor mBackgroundExecutor;
@@ -66,9 +68,11 @@
private static final int DELAY_UNLOAD_BITMAP = 2000;
@Inject
- public ImageWallpaper(@Background DelayableExecutor backgroundExecutor) {
+ public ImageWallpaper(@Background DelayableExecutor backgroundExecutor,
+ UserTracker userTracker) {
super();
mBackgroundExecutor = backgroundExecutor;
+ mUserTracker = userTracker;
}
@Override
@@ -288,7 +292,7 @@
boolean loadSuccess = false;
Bitmap bitmap;
try {
- bitmap = mWallpaperManager.getBitmapAsUser(UserHandle.USER_CURRENT, false);
+ bitmap = mWallpaperManager.getBitmapAsUser(mUserTracker.getUserId(), false);
if (bitmap != null
&& bitmap.getByteCount() > RecordingCanvas.MAX_BITMAP_SIZE) {
throw new RuntimeException("Wallpaper is too large to draw!");
@@ -300,9 +304,9 @@
// default wallpaper can't be loaded.
Log.w(TAG, "Unable to load wallpaper!", exception);
mWallpaperManager.clearWallpaper(
- WallpaperManager.FLAG_SYSTEM, UserHandle.USER_CURRENT);
+ WallpaperManager.FLAG_SYSTEM, mUserTracker.getUserId());
try {
- bitmap = mWallpaperManager.getBitmapAsUser(UserHandle.USER_CURRENT, false);
+ bitmap = mWallpaperManager.getBitmapAsUser(mUserTracker.getUserId(), false);
} catch (RuntimeException | OutOfMemoryError e) {
Log.w(TAG, "Unable to load default wallpaper!", e);
bitmap = null;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 9a9acf3..a4180fd 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -29,7 +29,6 @@
import android.content.res.Resources;
import android.database.ContentObserver;
-import android.net.Uri;
import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
@@ -275,7 +274,7 @@
ArgumentCaptor<ContentObserver> observerCaptor =
ArgumentCaptor.forClass(ContentObserver.class);
mController.init();
- verify(mSecureSettings).registerContentObserverForUser(any(Uri.class),
+ verify(mSecureSettings).registerContentObserverForUser(any(String.class),
anyBoolean(), observerCaptor.capture(), eq(UserHandle.USER_ALL));
ContentObserver observer = observerCaptor.getValue();
mExecutor.runAllReady();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonModeObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonModeObserverTest.java
index 7aa4763..4a5c1be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonModeObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonModeObserverTest.java
@@ -22,14 +22,16 @@
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.os.UserHandle;
+import android.app.ActivityManager;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.UserTracker;
import org.junit.Before;
import org.junit.Rule;
@@ -42,11 +44,14 @@
@RunWith(AndroidTestingRunner.class)
@SmallTest
public class AccessibilityButtonModeObserverTest extends SysuiTestCase {
+ private static final int MY_USER_ID = ActivityManager.getCurrentUser();
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@Mock
+ private UserTracker mUserTracker;
+ @Mock
private AccessibilityButtonModeObserver.ModeChangedListener mListener;
private AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
@@ -56,10 +61,12 @@
@Before
public void setUp() {
+ when(mUserTracker.getUserId()).thenReturn(MY_USER_ID);
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
- mAccessibilityButtonModeObserver = new AccessibilityButtonModeObserver(mContext);
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, MY_USER_ID);
+ mAccessibilityButtonModeObserver = new AccessibilityButtonModeObserver(mContext,
+ mUserTracker);
}
@Test
@@ -67,7 +74,7 @@
mAccessibilityButtonModeObserver.addListener(mListener);
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, TEST_A11Y_BTN_MODE_VALUE,
- UserHandle.USER_CURRENT);
+ MY_USER_ID);
mAccessibilityButtonModeObserver.mContentObserver.onChange(false);
@@ -80,7 +87,7 @@
mAccessibilityButtonModeObserver.removeListener(mListener);
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, TEST_A11Y_BTN_MODE_VALUE,
- UserHandle.USER_CURRENT);
+ MY_USER_ID);
mAccessibilityButtonModeObserver.mContentObserver.onChange(false);
@@ -91,7 +98,7 @@
public void getCurrentAccessibilityButtonMode_expectedValue() {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, TEST_A11Y_BTN_MODE_VALUE,
- UserHandle.USER_CURRENT);
+ MY_USER_ID);
final int actualValue =
mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java
index 4145437..a5a7a4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java
@@ -21,14 +21,16 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
-import android.os.UserHandle;
+import android.app.ActivityManager;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.UserTracker;
import org.junit.Before;
import org.junit.Rule;
@@ -42,11 +44,14 @@
@RunWith(AndroidTestingRunner.class)
@SmallTest
public class AccessibilityButtonTargetsObserverTest extends SysuiTestCase {
+ private static final int MY_USER_ID = ActivityManager.getCurrentUser();
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@Mock
+ private UserTracker mUserTracker;
+ @Mock
private AccessibilityButtonTargetsObserver.TargetsChangedListener mListener;
private AccessibilityButtonTargetsObserver mAccessibilityButtonTargetsObserver;
@@ -55,7 +60,9 @@
@Before
public void setUp() {
- mAccessibilityButtonTargetsObserver = new AccessibilityButtonTargetsObserver(mContext);
+ when(mUserTracker.getUserId()).thenReturn(MY_USER_ID);
+ mAccessibilityButtonTargetsObserver = new AccessibilityButtonTargetsObserver(mContext,
+ mUserTracker);
}
@Test
@@ -63,7 +70,7 @@
mAccessibilityButtonTargetsObserver.addListener(mListener);
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS,
- UserHandle.USER_CURRENT);
+ MY_USER_ID);
mAccessibilityButtonTargetsObserver.mContentObserver.onChange(false);
@@ -76,7 +83,7 @@
mAccessibilityButtonTargetsObserver.removeListener(mListener);
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS,
- UserHandle.USER_CURRENT);
+ MY_USER_ID);
mAccessibilityButtonTargetsObserver.mContentObserver.onChange(false);
@@ -87,7 +94,7 @@
public void getCurrentAccessibilityButtonTargets_expectedValue() {
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS,
- UserHandle.USER_CURRENT);
+ MY_USER_ID);
final String actualValue =
mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java
index 41fd2b3..9c601a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java
@@ -18,18 +18,20 @@
import static com.google.common.truth.Truth.assertThat;
+import android.app.ActivityManager;
import android.content.Context;
-import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.UserTracker;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
/** Test for {@link SecureSettingsContentObserver}. */
@RunWith(AndroidTestingRunner.class)
@@ -40,7 +42,9 @@
@Before
public void setUpObserver() {
- mTestObserver = new FakeSecureSettingsContentObserver(mContext,
+ UserTracker userTracker = Mockito.mock(UserTracker.class);
+ Mockito.when(userTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
+ mTestObserver = new FakeSecureSettingsContentObserver(mContext, userTracker,
Settings.Secure.ACCESSIBILITY_BUTTON_MODE);
}
@@ -57,7 +61,7 @@
@Test
public void checkValue() {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE, 1, UserHandle.USER_CURRENT);
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, 1, ActivityManager.getCurrentUser());
assertThat(mTestObserver.getSettingsValue()).isEqualTo("1");
}
@@ -66,9 +70,9 @@
private static class FakeSecureSettingsContentObserver extends
SecureSettingsContentObserver<Object> {
- protected FakeSecureSettingsContentObserver(Context context,
+ protected FakeSecureSettingsContentObserver(Context context, UserTracker userTracker,
String secureSettingsKey) {
- super(context, secureSettingsKey);
+ super(context, userTracker, secureSettingsKey);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index b7d3459..cdf3f65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -32,8 +32,11 @@
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.AdditionalAnswers.returnsSecondArg;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
@@ -76,6 +79,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
import com.android.systemui.util.leak.ReferenceTestUtils;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.utils.os.FakeHandler;
import org.junit.After;
@@ -111,6 +115,8 @@
IRemoteMagnificationAnimationCallback mAnimationCallback;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+ @Mock
+ private SecureSettings mSecureSettings;
private Handler mHandler;
private TestableWindowManager mWindowManager;
@@ -138,6 +144,10 @@
}).when(mSfVsyncFrameProvider).postFrameCallback(
any(FrameCallback.class));
mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class));
+ when(mSecureSettings.getIntForUser(anyString(), anyInt(), anyInt())).then(
+ returnsSecondArg());
+ when(mSecureSettings.getFloatForUser(anyString(), anyFloat(), anyInt())).then(
+ returnsSecondArg());
mResources = getContext().getOrCreateTestableResources().getResources();
mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index 8ca17b9..f34a36f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -25,6 +25,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.UserHandle;
@@ -40,6 +41,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
+import com.android.systemui.util.settings.SecureSettings;
import org.junit.After;
import org.junit.Before;
@@ -48,6 +50,8 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -71,8 +75,12 @@
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardCallbackCaptor;
private KeyguardUpdateMonitorCallback mKeyguardCallback;
+ @Mock
+ private SecureSettings mSecureSettings;
+
@Before
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
mContextWrapper = new ContextWrapper(mContext) {
@Override
public Context createContextAsUser(UserHandle user, int flags) {
@@ -128,7 +136,7 @@
public void onKeyguardVisibilityChanged_showing_destroyWidget() {
enableAccessibilityFloatingMenuConfig();
mController = setUpController();
- mController.mFloatingMenu = new AccessibilityFloatingMenu(mContextWrapper);
+ mController.mFloatingMenu = new AccessibilityFloatingMenu(mContextWrapper, mSecureSettings);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserUnlocked();
@@ -154,7 +162,7 @@
final int fakeUserId = 1;
enableAccessibilityFloatingMenuConfig();
mController = setUpController();
- mController.mFloatingMenu = new AccessibilityFloatingMenu(mContextWrapper);
+ mController.mFloatingMenu = new AccessibilityFloatingMenu(mContextWrapper, mSecureSettings);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserSwitching(fakeUserId);
@@ -167,7 +175,7 @@
final int fakeUserId = 1;
enableAccessibilityFloatingMenuConfig();
mController = setUpController();
- mController.mFloatingMenu = new AccessibilityFloatingMenu(mContextWrapper);
+ mController.mFloatingMenu = new AccessibilityFloatingMenu(mContextWrapper, mSecureSettings);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserUnlocked();
mKeyguardCallback.onKeyguardVisibilityChanged(true);
@@ -197,7 +205,7 @@
public void onAccessibilityButtonModeChanged_floatingModeAndHasButtonTargets_showWidget() {
Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS,
- UserHandle.USER_CURRENT);
+ ActivityManager.getCurrentUser());
mController = setUpController();
mController.onAccessibilityButtonModeChanged(ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
@@ -208,7 +216,7 @@
@Test
public void onAccessibilityButtonModeChanged_floatingModeAndNoButtonTargets_destroyWidget() {
Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "", UserHandle.USER_CURRENT);
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "", ActivityManager.getCurrentUser());
mController = setUpController();
mController.onAccessibilityButtonModeChanged(ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
@@ -220,7 +228,7 @@
public void onAccessibilityButtonModeChanged_navBarModeAndHasButtonTargets_destroyWidget() {
Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS,
- UserHandle.USER_CURRENT);
+ ActivityManager.getCurrentUser());
mController = setUpController();
mController.onAccessibilityButtonModeChanged(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
@@ -231,7 +239,7 @@
@Test
public void onAccessibilityButtonModeChanged_navBarModeAndNoButtonTargets_destroyWidget() {
Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "", UserHandle.USER_CURRENT);
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "", ActivityManager.getCurrentUser());
mController = setUpController();
mController.onAccessibilityButtonModeChanged(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
@@ -243,7 +251,7 @@
public void onAccessibilityButtonTargetsChanged_floatingModeAndHasButtonTargets_showWidget() {
Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU,
- UserHandle.USER_CURRENT);
+ ActivityManager.getCurrentUser());
mController = setUpController();
mController.onAccessibilityButtonTargetsChanged(TEST_A11Y_BTN_TARGETS);
@@ -255,7 +263,7 @@
public void onAccessibilityButtonTargetsChanged_floatingModeAndNoButtonTargets_destroyWidget() {
Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU,
- UserHandle.USER_CURRENT);
+ ActivityManager.getCurrentUser());
mController = setUpController();
mController.onAccessibilityButtonTargetsChanged("");
@@ -267,7 +275,7 @@
public void onAccessibilityButtonTargetsChanged_navBarModeAndHasButtonTargets_destroyWidget() {
Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
- ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, ActivityManager.getCurrentUser());
mController = setUpController();
mController.onAccessibilityButtonTargetsChanged(TEST_A11Y_BTN_TARGETS);
@@ -279,7 +287,7 @@
public void onAccessibilityButtonTargetsChanged_navBarModeAndNoButtonTargets_destroyWidget() {
Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
- ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, ActivityManager.getCurrentUser());
mController = setUpController();
mController.onAccessibilityButtonTargetsChanged("");
@@ -293,7 +301,7 @@
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
final AccessibilityFloatingMenuController controller =
new AccessibilityFloatingMenuController(mContextWrapper, mTargetsObserver,
- mModeObserver, mKeyguardUpdateMonitor);
+ mModeObserver, mKeyguardUpdateMonitor, mSecureSettings);
controller.init();
return controller;
@@ -302,10 +310,10 @@
private void enableAccessibilityFloatingMenuConfig() {
Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU,
- UserHandle.USER_CURRENT);
+ ActivityManager.getCurrentUser());
Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS,
- UserHandle.USER_CURRENT);
+ ActivityManager.getCurrentUser());
}
private void captureKeyguardUpdateMonitorCallback() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuTest.java
index 558261b..04345fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuTest.java
@@ -31,6 +31,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.settings.SecureSettings;
import org.junit.After;
import org.junit.Before;
@@ -55,6 +56,8 @@
@Mock
private AccessibilityManager mAccessibilityManager;
+ @Mock
+ private SecureSettings mSecureSettings;
private AccessibilityFloatingMenuView mMenuView;
private AccessibilityFloatingMenu mMenu;
@@ -69,7 +72,7 @@
final Position position = new Position(0, 0);
mMenuView = new AccessibilityFloatingMenuView(mContext, position);
- mMenu = new AccessibilityFloatingMenu(mContext, mMenuView);
+ mMenu = new AccessibilityFloatingMenu(mContext, mSecureSettings, mMenuView);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 1ef119d..0690d1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -53,6 +53,7 @@
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
@@ -99,6 +100,7 @@
@Mock private lateinit var unlockedScreenOffAnimationController:
UnlockedScreenOffAnimationController
@Mock private lateinit var udfpsDisplayMode: UdfpsDisplayModeProvider
+ @Mock private lateinit var secureSettings: SecureSettings
@Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback
@Mock private lateinit var udfpsController: UdfpsController
@Mock private lateinit var udfpsView: UdfpsView
@@ -139,8 +141,8 @@
statusBarStateController, shadeExpansionStateManager, statusBarKeyguardViewManager,
keyguardUpdateMonitor, dialogManager, dumpManager, transitionController,
configurationController, keyguardStateController,
- unlockedScreenOffAnimationController, udfpsDisplayMode, REQUEST_ID, reason,
- controllerCallback, onTouch, activityLaunchAnimator, featureFlags,
+ unlockedScreenOffAnimationController, udfpsDisplayMode, secureSettings, REQUEST_ID,
+ reason, controllerCallback, onTouch, activityLaunchAnimator, featureFlags,
primaryBouncerInteractor, alternateBouncerInteractor, isDebuggable,
)
block()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 0c34e54..232daad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -96,6 +96,7 @@
import com.android.systemui.util.concurrency.Execution;
import com.android.systemui.util.concurrency.FakeExecution;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.time.SystemClock;
@@ -205,6 +206,8 @@
private SinglePointerTouchProcessor mSinglePointerTouchProcessor;
@Mock
private AlternateBouncerInteractor mAlternateBouncerInteractor;
+ @Mock
+ private SecureSettings mSecureSettings;
// Capture listeners so that they can be used to send events
@Captor
@@ -296,7 +299,7 @@
mUnlockedScreenOffAnimationController, mSystemUIDialogManager, mLatencyTracker,
mActivityLaunchAnimator, alternateTouchProvider, mBiometricExecutor,
mPrimaryBouncerInteractor, mSinglePointerTouchProcessor,
- mAlternateBouncerInteractor);
+ mAlternateBouncerInteractor, mSecureSettings);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
index 262b4b8..80c3e5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -69,6 +70,8 @@
lateinit var cameraIntents: CameraIntentsWrapper
@Mock
lateinit var contentResolver: ContentResolver
+ @Mock
+ lateinit var userTracker: UserTracker
private lateinit var underTest: CameraGestureHelper
@@ -96,6 +99,7 @@
cameraIntents = cameraIntents,
contentResolver = contentResolver,
uiExecutor = MoreExecutors.directExecutor(),
+ userTracker = userTracker,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index d159714..d6cafcb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -16,18 +16,23 @@
package com.android.systemui.charging
+import android.graphics.Rect
import android.testing.AndroidTestingRunner
+import android.view.Surface
import android.view.View
import android.view.WindowManager
+import android.view.WindowMetrics
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.surfaceeffects.ripple.RippleView
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.surfaceeffects.ripple.RippleView
+import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import org.junit.Before
import org.junit.Test
@@ -35,12 +40,12 @@
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
import org.mockito.Mock
+import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.eq
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -54,6 +59,7 @@
@Mock private lateinit var rippleView: RippleView
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var uiEventLogger: UiEventLogger
+ @Mock private lateinit var windowMetrics: WindowMetrics
private val systemClock = FakeSystemClock()
@Before
@@ -66,6 +72,9 @@
rippleView.setupShader()
controller.rippleView = rippleView // Replace the real ripple view with a mock instance
controller.registerCallbacks()
+
+ `when`(windowMetrics.bounds).thenReturn(Rect(0, 0, 100, 100))
+ `when`(windowManager.currentWindowMetrics).thenReturn(windowMetrics)
}
@Test
@@ -164,4 +173,63 @@
verify(rippleView, never()).addOnAttachStateChangeListener(attachListenerCaptor.capture())
verify(windowManager, never()).addView(eq(rippleView), any<WindowManager.LayoutParams>())
}
+
+ @Test
+ fun testRipple_layoutsCorrectly() {
+ // Sets the correct ripple size.
+ val width = 100
+ val height = 200
+ whenever(windowMetrics.bounds).thenReturn(Rect(0, 0, width, height))
+
+ // Trigger ripple.
+ val captor = ArgumentCaptor
+ .forClass(BatteryController.BatteryStateChangeCallback::class.java)
+ verify(batteryController).addCallback(captor.capture())
+
+ captor.value.onBatteryLevelChanged(
+ /* unusedBatteryLevel= */ 0,
+ /* plugged in= */ true,
+ /* charging= */ false)
+
+ val attachListenerCaptor =
+ ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
+ verify(rippleView).addOnAttachStateChangeListener(attachListenerCaptor.capture())
+ verify(windowManager).addView(eq(rippleView), any<WindowManager.LayoutParams>())
+
+ val runnableCaptor =
+ ArgumentCaptor.forClass(Runnable::class.java)
+ attachListenerCaptor.value.onViewAttachedToWindow(rippleView)
+ verify(rippleView).startRipple(runnableCaptor.capture())
+
+ // Verify size and center position.
+ val maxSize = 400f // Double the max value between width and height.
+ verify(rippleView).setMaxSize(maxWidth = maxSize, maxHeight = maxSize)
+
+ val normalizedPortPosX =
+ context.resources.getFloat(R.dimen.physical_charger_port_location_normalized_x)
+ val normalizedPortPosY =
+ context.resources.getFloat(R.dimen.physical_charger_port_location_normalized_y)
+ val expectedCenterX: Float
+ val expectedCenterY: Float
+ when (context.display.rotation) {
+ Surface.ROTATION_90 -> {
+ expectedCenterX = width * normalizedPortPosY
+ expectedCenterY = height * (1 - normalizedPortPosX)
+ }
+ Surface.ROTATION_180 -> {
+ expectedCenterX = width * (1 - normalizedPortPosX)
+ expectedCenterY = height * (1 - normalizedPortPosY)
+ }
+ Surface.ROTATION_270 -> {
+ expectedCenterX = width * (1 - normalizedPortPosY)
+ expectedCenterY = height * normalizedPortPosX
+ }
+ else -> { // Surface.ROTATION_0
+ expectedCenterX = width * normalizedPortPosX
+ expectedCenterY = height * normalizedPortPosY
+ }
+ }
+
+ verify(rippleView).setCenter(expectedCenterX, expectedCenterY)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index 5c2b153..af027e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.hardware.display.AmbientDisplayConfiguration;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -36,6 +37,7 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.doze.DozeMachine.State;
+import com.android.systemui.settings.UserTracker;
import org.junit.Before;
import org.junit.Test;
@@ -48,6 +50,7 @@
@RunWithLooper
public class DozeDockHandlerTest extends SysuiTestCase {
@Mock private DozeMachine mMachine;
+ @Mock private UserTracker mUserTracker;
private AmbientDisplayConfiguration mConfig;
private DockManagerFake mDockManagerFake;
private DozeDockHandler mDockHandler;
@@ -57,9 +60,10 @@
MockitoAnnotations.initMocks(this);
mConfig = DozeConfigurationUtil.createMockConfig();
mDockManagerFake = spy(new DockManagerFake());
- mDockHandler = new DozeDockHandler(mConfig, mDockManagerFake);
+ mDockHandler = new DozeDockHandler(mConfig, mDockManagerFake, mUserTracker);
mDockHandler.setDozeMachine(mMachine);
+ when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
when(mMachine.getState()).thenReturn(State.DOZE_AOD);
doReturn(true).when(mConfig).alwaysOnEnabled(anyInt());
mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 5bbd810..a636b7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -45,6 +45,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.content.res.Configuration;
import android.hardware.display.AmbientDisplayConfiguration;
import android.testing.AndroidTestingRunner;
@@ -57,6 +58,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.wakelock.WakeLockFake;
@@ -85,6 +87,8 @@
private DozeMachine.Part mPartMock;
@Mock
private DozeMachine.Part mAnotherPartMock;
+ @Mock
+ private UserTracker mUserTracker;
private DozeServiceFake mServiceFake;
private WakeLockFake mWakeLockFake;
private AmbientDisplayConfiguration mAmbientDisplayConfigMock;
@@ -97,6 +101,7 @@
mAmbientDisplayConfigMock = mock(AmbientDisplayConfiguration.class);
when(mDockManager.isDocked()).thenReturn(false);
when(mDockManager.isHidden()).thenReturn(false);
+ when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
mMachine = new DozeMachine(mServiceFake,
mAmbientDisplayConfigMock,
@@ -105,7 +110,8 @@
mDozeLog,
mDockManager,
mHost,
- new DozeMachine.Part[]{mPartMock, mAnotherPartMock});
+ new DozeMachine.Part[]{mPartMock, mAnotherPartMock},
+ mUserTracker);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 03827da..3af444a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -34,6 +34,7 @@
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.reset;
@@ -57,6 +58,7 @@
import com.android.systemui.util.concurrency.FakeThreadFactory;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.FakeSensorManager;
+import com.android.systemui.util.settings.SystemSettings;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -94,6 +96,8 @@
DevicePostureController mDevicePostureController;
@Mock
DozeLog mDozeLog;
+ @Mock
+ SystemSettings mSystemSettings;
private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
private FakeThreadFactory mFakeThreadFactory = new FakeThreadFactory(mFakeExecutor);
@@ -102,9 +106,8 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- Settings.System.putIntForUser(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS, DEFAULT_BRIGHTNESS,
- UserHandle.USER_CURRENT);
+ when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS), anyInt(),
+ eq(UserHandle.USER_CURRENT))).thenReturn(DEFAULT_BRIGHTNESS);
doAnswer(invocation -> {
((Runnable) invocation.getArgument(0)).run();
return null;
@@ -131,7 +134,8 @@
mWakefulnessLifecycle,
mDozeParameters,
mDevicePostureController,
- mDozeLog);
+ mDozeLog,
+ mSystemSettings);
}
@Test
@@ -157,11 +161,10 @@
}
@Test
- public void testAod_usesLightSensorRespectingUserSetting() throws Exception {
+ public void testAod_usesLightSensorRespectingUserSetting() {
int maxBrightness = 3;
- Settings.System.putIntForUser(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS, maxBrightness,
- UserHandle.USER_CURRENT);
+ when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS), anyInt(),
+ eq(UserHandle.USER_CURRENT))).thenReturn(maxBrightness);
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
assertEquals(maxBrightness, mServiceFake.screenBrightness);
@@ -238,7 +241,8 @@
mWakefulnessLifecycle,
mDozeParameters,
mDevicePostureController,
- mDozeLog);
+ mDozeLog,
+ mSystemSettings);
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE);
reset(mDozeHost);
@@ -275,7 +279,8 @@
mWakefulnessLifecycle,
mDozeParameters,
mDevicePostureController,
- mDozeLog);
+ mDozeLog,
+ mSystemSettings);
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
@@ -306,7 +311,8 @@
mWakefulnessLifecycle,
mDozeParameters,
mDevicePostureController,
- mDozeLog);
+ mDozeLog,
+ mSystemSettings);
// GIVEN the device is in AOD
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
@@ -344,7 +350,8 @@
mWakefulnessLifecycle,
mDozeParameters,
mDevicePostureController,
- mDozeLog);
+ mDozeLog,
+ mSystemSettings);
// GIVEN device is in AOD
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
@@ -386,7 +393,8 @@
mWakefulnessLifecycle,
mDozeParameters,
mDevicePostureController,
- mDozeLog);
+ mDozeLog,
+ mSystemSettings);
verify(mDevicePostureController).addCallback(postureCallbackCaptor.capture());
// GIVEN device is in AOD
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index e87f104..986d6d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -35,11 +35,11 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -120,11 +120,13 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
+ when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
when(mAmbientDisplayConfiguration.tapSensorTypeMapping())
.thenReturn(new String[]{"tapSensor"});
when(mAmbientDisplayConfiguration.getWakeLockScreenDebounce()).thenReturn(5000L);
when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
- when(mAmbientDisplayConfiguration.enabled(UserHandle.USER_CURRENT)).thenReturn(true);
+ when(mAmbientDisplayConfiguration.enabled(ActivityManager.getCurrentUser())).thenReturn(
+ true);
doAnswer(invocation -> {
((Runnable) invocation.getArgument(0)).run();
return null;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java
index 32b9945..9064470 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java
@@ -36,6 +36,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.hardware.display.AmbientDisplayConfiguration;
import android.testing.AndroidTestingRunner;
import android.testing.UiThreadTest;
@@ -43,6 +44,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import org.junit.After;
@@ -73,6 +75,8 @@
private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
@Mock
private BiometricUnlockController mBiometricUnlockController;
+ @Mock
+ private UserTracker mUserTracker;
@Mock
private DozeMachine mDozeMachine;
@@ -89,12 +93,14 @@
when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
when(mBiometricUnlockController.hasPendingAuthentication()).thenReturn(false);
when(mDozeHost.isProvisioned()).thenReturn(true);
+ when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
mDozeSuppressor = new DozeSuppressor(
mDozeHost,
mConfig,
mDozeLog,
- mBiometricUnlockControllerLazy);
+ mBiometricUnlockControllerLazy,
+ mUserTracker);
mDozeSuppressor.setDozeMachine(mDozeMachine);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 4568d1e..3b8bb80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -234,6 +234,32 @@
}
@Test
+ public void testOnEndDream() throws RemoteException {
+ final IBinder proxy = mService.onBind(new Intent());
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+
+ // Inform the overlay service of dream starting.
+ overlay.startDream(mWindowParams, mDreamOverlayCallback,
+ LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
+ mMainExecutor.runAllReady();
+
+ // Verify view added.
+ verify(mWindowManager).addView(mViewCaptor.capture(), any());
+
+ // Service destroyed.
+ mService.onEndDream();
+ mMainExecutor.runAllReady();
+
+ // Verify view removed.
+ verify(mWindowManager).removeView(mViewCaptor.getValue());
+
+ // Verify state correctly set.
+ verify(mStateController).setOverlayActive(false);
+ verify(mStateController).setLowLightActive(false);
+ verify(mStateController).setEntryAnimationsFinished(false);
+ }
+
+ @Test
public void testDestroy() throws RemoteException {
final IBinder proxy = mService.onBind(new Intent());
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index 85c2819..596b903 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -31,6 +31,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.app.AlarmManager;
import android.content.Context;
import android.content.res.Resources;
@@ -47,6 +48,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -109,6 +111,8 @@
View mStatusBarItemView;
@Mock
DreamOverlayStateController mDreamOverlayStateController;
+ @Mock
+ UserTracker mUserTracker;
@Captor
private ArgumentCaptor<DreamOverlayStateController.Callback> mCallbackCaptor;
@@ -125,6 +129,7 @@
.thenReturn(NOTIFICATION_INDICATOR_FORMATTER_STRING);
doCallRealMethod().when(mView).setVisibility(anyInt());
doCallRealMethod().when(mView).getVisibility();
+ when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
mController = new DreamOverlayStatusBarViewController(
mView,
@@ -140,7 +145,8 @@
mZenModeController,
mStatusBarWindowStateController,
mDreamOverlayStatusBarItemsProvider,
- mDreamOverlayStateController);
+ mDreamOverlayStateController,
+ mUserTracker);
}
@Test
@@ -282,7 +288,8 @@
mZenModeController,
mStatusBarWindowStateController,
mDreamOverlayStatusBarItemsProvider,
- mDreamOverlayStateController);
+ mDreamOverlayStateController,
+ mUserTracker);
controller.onViewAttached();
verify(mView, never()).showIcon(
eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 0a03b2c..c0af0cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -173,6 +173,7 @@
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true)
set(Flags.REVAMPED_WALLPAPER_UI, true)
+ set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true)
},
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
index e9db8cc..b9cfc65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
@@ -23,6 +23,7 @@
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.Activity;
import android.app.ActivityManager;
@@ -35,12 +36,12 @@
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.UserHandle;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -71,6 +72,7 @@
private @Mock Context mContext;
private @Mock TaskStackChangeListeners mTaskStackChangeListeners;
private @Mock IActivityTaskManager mIActivityTaskManager;
+ private @Mock UserTracker mUserTracker;
private WorkLockActivityController mController;
private TaskStackChangeListener mTaskStackListener;
@@ -81,12 +83,13 @@
// Set a package name to use for checking ComponentName well-formedness in tests.
doReturn("com.example.test").when(mContext).getPackageName();
+ when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
// Construct controller. Save the TaskStackListener for injecting events.
final ArgumentCaptor<TaskStackChangeListener> listenerCaptor =
ArgumentCaptor.forClass(TaskStackChangeListener.class);
- mController = new WorkLockActivityController(mContext, mTaskStackChangeListeners,
- mIActivityTaskManager);
+ mController = new WorkLockActivityController(mContext, mUserTracker,
+ mTaskStackChangeListeners, mIActivityTaskManager);
verify(mTaskStackChangeListeners).registerTaskStackListener(listenerCaptor.capture());
mTaskStackListener = listenerCaptor.getValue();
@@ -135,7 +138,7 @@
anyInt(),
eq((ProfilerInfo) null),
argThat(hasOptions(taskId, taskOverlay)),
- eq(UserHandle.USER_CURRENT));
+ eq(ActivityManager.getCurrentUser()));
}
private void verifyStartActivity(int taskId, boolean taskOverlay) throws Exception {
@@ -151,7 +154,7 @@
anyInt(),
eq((ProfilerInfo) null),
argThat(hasOptions(taskId, taskOverlay)),
- eq(UserHandle.USER_CURRENT));
+ eq(ActivityManager.getCurrentUser()));
}
private static ArgumentMatcher<Intent> hasComponent(final Context context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
index 7205f30..8da4eae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
@@ -22,15 +22,21 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.camera.CameraGestureHelper
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.Mock
+import org.mockito.Mockito.anyInt
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
class CameraQuickAffordanceConfigTest : SysuiTestCase() {
@@ -62,4 +68,24 @@
.launchCamera(StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE)
assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
}
+
+ @Test
+ fun `getPickerScreenState - default when launchable`() = runTest {
+ setLaunchable(true)
+
+ Truth.assertThat(underTest.getPickerScreenState())
+ .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
+ }
+
+ @Test
+ fun `getPickerScreenState - unavailable when not launchable`() = runTest {
+ setLaunchable(false)
+
+ Truth.assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+ }
+
+ private fun setLaunchable(isLaunchable: Boolean) {
+ whenever(cameraGestureHelper.canCameraGestureBeLaunched(anyInt())).thenReturn(isLaunchable)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index ca44fa18..8f56b95 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -114,20 +114,8 @@
}
@Test
- fun `affordance - missing icon - model is none`() = runBlockingTest {
- setUpState(hasWalletIcon = false)
- var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
-
- val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
-
- assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
-
- job.cancel()
- }
-
- @Test
fun `affordance - no selected card - model is none`() = runBlockingTest {
- setUpState(hasWalletIcon = false)
+ setUpState(hasSelectedCard = false)
var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
@@ -165,7 +153,7 @@
@Test
fun `getPickerScreenState - unavailable`() = runTest {
setUpState(
- isWalletEnabled = false,
+ isWalletServiceAvailable = false,
)
assertThat(underTest.getPickerScreenState())
@@ -173,9 +161,9 @@
}
@Test
- fun `getPickerScreenState - disabled when there is no icon`() = runTest {
+ fun `getPickerScreenState - disabled when the feature is not enabled`() = runTest {
setUpState(
- hasWalletIcon = false,
+ isWalletEnabled = false,
)
assertThat(underTest.getPickerScreenState())
@@ -194,20 +182,16 @@
private fun setUpState(
isWalletEnabled: Boolean = true,
+ isWalletServiceAvailable: Boolean = true,
isWalletQuerySuccessful: Boolean = true,
- hasWalletIcon: Boolean = true,
hasSelectedCard: Boolean = true,
) {
whenever(walletController.isWalletEnabled).thenReturn(isWalletEnabled)
val walletClient: QuickAccessWalletClient = mock()
- val icon: Drawable? =
- if (hasWalletIcon) {
- ICON
- } else {
- null
- }
- whenever(walletClient.tileIcon).thenReturn(icon)
+ whenever(walletClient.tileIcon).thenReturn(ICON)
+ whenever(walletClient.isWalletServiceAvailable).thenReturn(isWalletServiceAvailable)
+
whenever(walletController.walletClient).thenReturn(walletClient)
whenever(walletController.queryWalletCards(any())).thenAnswer { invocation ->
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
new file mode 100644
index 0000000..805dcec
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
@@ -0,0 +1,115 @@
+/*
+ * 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.keyguard.data.quickaffordance
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.ActivityIntentHelper
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.camera.CameraIntentsWrapper
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.settings.FakeUserTracker
+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
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class VideoCameraQuickAffordanceConfigTest : SysuiTestCase() {
+
+ @Mock private lateinit var activityIntentHelper: ActivityIntentHelper
+
+ private lateinit var underTest: VideoCameraQuickAffordanceConfig
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest =
+ VideoCameraQuickAffordanceConfig(
+ context = context,
+ cameraIntents = CameraIntentsWrapper(context),
+ activityIntentHelper = activityIntentHelper,
+ userTracker = FakeUserTracker(),
+ )
+ }
+
+ @Test
+ fun `lockScreenState - visible when launchable`() = runTest {
+ setLaunchable(true)
+
+ val lockScreenState = collectLastValue(underTest.lockScreenState)
+
+ assertThat(lockScreenState())
+ .isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible::class.java)
+ }
+
+ @Test
+ fun `lockScreenState - hidden when not launchable`() = runTest {
+ setLaunchable(false)
+
+ val lockScreenState = collectLastValue(underTest.lockScreenState)
+
+ assertThat(lockScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ }
+
+ @Test
+ fun `getPickerScreenState - default when launchable`() = runTest {
+ setLaunchable(true)
+
+ assertThat(underTest.getPickerScreenState())
+ .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
+ }
+
+ @Test
+ fun `getPickerScreenState - unavailable when not launchable`() = runTest {
+ setLaunchable(false)
+
+ assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+ }
+
+ private fun setLaunchable(isLaunchable: Boolean) {
+ whenever(
+ activityIntentHelper.getTargetActivityInfo(
+ any(),
+ anyInt(),
+ anyBoolean(),
+ )
+ )
+ .thenReturn(
+ if (isLaunchable) {
+ mock()
+ } else {
+ null
+ }
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
new file mode 100644
index 0000000..c4ae2db
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.keyguard.data.repository
+
+import android.hardware.biometrics.BiometricSourceType
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Captor private lateinit var callbackCaptor: ArgumentCaptor<KeyguardUpdateMonitorCallback>
+
+ private lateinit var testScope: TestScope
+
+ private lateinit var underTest: DeviceEntryFingerprintAuthRepository
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testScope = TestScope()
+
+ underTest = DeviceEntryFingerprintAuthRepositoryImpl(keyguardUpdateMonitor)
+ }
+
+ @After
+ fun tearDown() {
+ verify(keyguardUpdateMonitor).removeCallback(callbackCaptor.value)
+ }
+
+ @Test
+ fun isLockedOut_whenFingerprintLockoutStateChanges_emitsNewValue() =
+ testScope.runTest {
+ val isLockedOutValue = collectLastValue(underTest.isLockedOut)
+ runCurrent()
+
+ verify(keyguardUpdateMonitor).registerCallback(callbackCaptor.capture())
+ val callback = callbackCaptor.value
+ whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true)
+
+ callback.onLockedOutStateChanged(BiometricSourceType.FACE)
+ assertThat(isLockedOutValue()).isFalse()
+
+ callback.onLockedOutStateChanged(BiometricSourceType.FINGERPRINT)
+ assertThat(isLockedOutValue()).isTrue()
+
+ whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
+ callback.onLockedOutStateChanged(BiometricSourceType.FINGERPRINT)
+ assertThat(isLockedOutValue()).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
new file mode 100644
index 0000000..4b06905
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
@@ -0,0 +1,163 @@
+/*
+ * 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.keyguard.data.repository
+
+import android.app.trust.TrustManager
+import android.content.pm.UserInfo
+import androidx.test.filters.SmallTest
+import com.android.keyguard.logging.TrustRepositoryLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogcatEchoTracker
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+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.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class TrustRepositoryTest : SysuiTestCase() {
+ @Mock private lateinit var trustManager: TrustManager
+ @Captor private lateinit var listenerCaptor: ArgumentCaptor<TrustManager.TrustListener>
+ private lateinit var userRepository: FakeUserRepository
+ private lateinit var testScope: TestScope
+ private val users = listOf(UserInfo(1, "user 1", 0), UserInfo(2, "user 1", 0))
+
+ private lateinit var underTest: TrustRepository
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testScope = TestScope()
+ userRepository = FakeUserRepository()
+ userRepository.setUserInfos(users)
+
+ val logger =
+ TrustRepositoryLogger(
+ LogBuffer("TestBuffer", 1, mock(LogcatEchoTracker::class.java), false)
+ )
+ underTest =
+ TrustRepositoryImpl(testScope.backgroundScope, userRepository, trustManager, logger)
+ }
+
+ @Test
+ fun isCurrentUserTrusted_whenTrustChanges_emitsLatestValue() =
+ testScope.runTest {
+ runCurrent()
+ verify(trustManager).registerTrustListener(listenerCaptor.capture())
+ val listener = listenerCaptor.value
+
+ val currentUserId = users[0].id
+ userRepository.setSelectedUserInfo(users[0])
+ val isCurrentUserTrusted = collectLastValue(underTest.isCurrentUserTrusted)
+
+ listener.onTrustChanged(true, false, currentUserId, 0, emptyList())
+ assertThat(isCurrentUserTrusted()).isTrue()
+
+ listener.onTrustChanged(false, false, currentUserId, 0, emptyList())
+
+ assertThat(isCurrentUserTrusted()).isFalse()
+ }
+
+ @Test
+ fun isCurrentUserTrusted_isFalse_byDefault() =
+ testScope.runTest {
+ runCurrent()
+
+ val isCurrentUserTrusted = collectLastValue(underTest.isCurrentUserTrusted)
+
+ assertThat(isCurrentUserTrusted()).isFalse()
+ }
+
+ @Test
+ fun isCurrentUserTrusted_whenTrustChangesForDifferentUser_noop() =
+ testScope.runTest {
+ runCurrent()
+ verify(trustManager).registerTrustListener(listenerCaptor.capture())
+ userRepository.setSelectedUserInfo(users[0])
+ val listener = listenerCaptor.value
+
+ val isCurrentUserTrusted = collectLastValue(underTest.isCurrentUserTrusted)
+ // current user is trusted.
+ listener.onTrustChanged(true, true, users[0].id, 0, emptyList())
+ // some other user is not trusted.
+ listener.onTrustChanged(false, false, users[1].id, 0, emptyList())
+
+ assertThat(isCurrentUserTrusted()).isTrue()
+ }
+
+ @Test
+ fun isCurrentUserTrusted_whenTrustChangesForCurrentUser_emitsNewValue() =
+ testScope.runTest {
+ runCurrent()
+ verify(trustManager).registerTrustListener(listenerCaptor.capture())
+ val listener = listenerCaptor.value
+ userRepository.setSelectedUserInfo(users[0])
+
+ val isCurrentUserTrusted = collectLastValue(underTest.isCurrentUserTrusted)
+ listener.onTrustChanged(true, true, users[0].id, 0, emptyList())
+ assertThat(isCurrentUserTrusted()).isTrue()
+
+ listener.onTrustChanged(false, true, users[0].id, 0, emptyList())
+ assertThat(isCurrentUserTrusted()).isFalse()
+ }
+
+ @Test
+ fun isCurrentUserTrusted_whenUserChangesWithoutRecentTrustChange_defaultsToFalse() =
+ testScope.runTest {
+ runCurrent()
+ verify(trustManager).registerTrustListener(listenerCaptor.capture())
+ val listener = listenerCaptor.value
+ userRepository.setSelectedUserInfo(users[0])
+ listener.onTrustChanged(true, true, users[0].id, 0, emptyList())
+
+ val isCurrentUserTrusted = collectLastValue(underTest.isCurrentUserTrusted)
+ userRepository.setSelectedUserInfo(users[1])
+
+ assertThat(isCurrentUserTrusted()).isFalse()
+ }
+
+ @Test
+ fun isCurrentUserTrusted_trustChangesFirstBeforeUserInfoChanges_emitsCorrectValue() =
+ testScope.runTest {
+ runCurrent()
+ verify(trustManager).registerTrustListener(listenerCaptor.capture())
+ val listener = listenerCaptor.value
+ val isCurrentUserTrusted = collectLastValue(underTest.isCurrentUserTrusted)
+
+ listener.onTrustChanged(true, true, users[0].id, 0, emptyList())
+ assertThat(isCurrentUserTrusted()).isFalse()
+
+ userRepository.setSelectedUserInfo(users[0])
+
+ assertThat(isCurrentUserTrusted()).isTrue()
+ }
+}
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 094d69a..9a0bd9e 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
@@ -243,6 +243,13 @@
}
@Test
+ public void dismissDialog_closesDialogByBroadcastSender() {
+ mMediaOutputBaseDialogImpl.dismissDialog();
+
+ verify(mBroadcastSender).closeSystemDialogs();
+ }
+
+ @Test
public void whenBroadcasting_verifyLeBroadcastServiceCallBackIsRegisteredAndUnregistered() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index f5432e2..117751c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -252,6 +252,13 @@
}
@Test
+ public void tryToLaunchMediaApplication_nullIntent_skip() {
+ mMediaOutputController.tryToLaunchMediaApplication();
+
+ verify(mCb, never()).dismissDialog();
+ }
+
+ @Test
public void onDevicesUpdated_unregistersNearbyDevicesCallback() throws RemoteException {
mMediaOutputController.start(mCb);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index f5b3959..54d4460 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -45,13 +45,18 @@
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
import com.android.systemui.temporarydisplay.chipbar.ChipbarLogger
import com.android.systemui.temporarydisplay.chipbar.FakeChipbarCoordinator
+import com.android.systemui.temporarydisplay.chipbar.SwipeChipbarAwayGestureHandler
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.view.ViewUtil
import com.android.systemui.util.wakelock.WakeLockFake
@@ -61,6 +66,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
+import org.mockito.Mockito.atLeast
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
@@ -93,6 +99,7 @@
@Mock private lateinit var viewUtil: ViewUtil
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var vibratorHelper: VibratorHelper
+ @Mock private lateinit var swipeHandler: SwipeChipbarAwayGestureHandler
private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
private lateinit var fakeWakeLock: WakeLockFake
private lateinit var chipbarCoordinator: ChipbarCoordinator
@@ -143,6 +150,7 @@
powerManager,
falsingManager,
falsingCollector,
+ swipeHandler,
viewUtil,
vibratorHelper,
fakeWakeLockBuilder,
@@ -161,9 +169,7 @@
)
underTest.start()
- val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
- verify(commandQueue).addCallback(callbackCaptor.capture())
- commandQueueCallback = callbackCaptor.value!!
+ setCommandQueueCallback()
}
@Test
@@ -920,6 +926,172 @@
verify(windowManager).removeView(any())
}
+ @Test
+ fun newState_viewListenerRegistered() {
+ val mockChipbarCoordinator = mock<ChipbarCoordinator>()
+ underTest =
+ MediaTttSenderCoordinator(
+ mockChipbarCoordinator,
+ commandQueue,
+ context,
+ logger,
+ mediaTttFlags,
+ uiEventLogger,
+ )
+ underTest.start()
+ // Re-set the command queue callback since we've created a new [MediaTttSenderCoordinator]
+ // with a new callback.
+ setCommandQueueCallback()
+
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+ routeInfo,
+ null,
+ )
+
+ verify(mockChipbarCoordinator).registerListener(any())
+ }
+
+ @Test
+ fun onInfoPermanentlyRemoved_viewListenerUnregistered() {
+ val mockChipbarCoordinator = mock<ChipbarCoordinator>()
+ underTest =
+ MediaTttSenderCoordinator(
+ mockChipbarCoordinator,
+ commandQueue,
+ context,
+ logger,
+ mediaTttFlags,
+ uiEventLogger,
+ )
+ underTest.start()
+ setCommandQueueCallback()
+
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+ routeInfo,
+ null,
+ )
+
+ val listenerCaptor = argumentCaptor<TemporaryViewDisplayController.Listener>()
+ verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
+
+ // WHEN the listener is notified that the view has been removed
+ listenerCaptor.value.onInfoPermanentlyRemoved(DEFAULT_ID)
+
+ // THEN the media coordinator unregisters the listener
+ verify(mockChipbarCoordinator).unregisterListener(listenerCaptor.value)
+ }
+
+ @Test
+ fun onInfoPermanentlyRemoved_wrongId_viewListenerNotUnregistered() {
+ val mockChipbarCoordinator = mock<ChipbarCoordinator>()
+ underTest =
+ MediaTttSenderCoordinator(
+ mockChipbarCoordinator,
+ commandQueue,
+ context,
+ logger,
+ mediaTttFlags,
+ uiEventLogger,
+ )
+ underTest.start()
+ setCommandQueueCallback()
+
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+ routeInfo,
+ null,
+ )
+
+ val listenerCaptor = argumentCaptor<TemporaryViewDisplayController.Listener>()
+ verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
+
+ // WHEN the listener is notified that a different view has been removed
+ listenerCaptor.value.onInfoPermanentlyRemoved("differentViewId")
+
+ // THEN the media coordinator doesn't unregister the listener
+ verify(mockChipbarCoordinator, never()).unregisterListener(listenerCaptor.value)
+ }
+
+ @Test
+ fun farFromReceiverState_viewListenerUnregistered() {
+ val mockChipbarCoordinator = mock<ChipbarCoordinator>()
+ underTest =
+ MediaTttSenderCoordinator(
+ mockChipbarCoordinator,
+ commandQueue,
+ context,
+ logger,
+ mediaTttFlags,
+ uiEventLogger,
+ )
+ underTest.start()
+ setCommandQueueCallback()
+
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+ routeInfo,
+ null,
+ )
+
+ val listenerCaptor = argumentCaptor<TemporaryViewDisplayController.Listener>()
+ verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
+
+ // WHEN we go to the FAR_FROM_RECEIVER state
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ routeInfo,
+ null
+ )
+
+ // THEN the media coordinator unregisters the listener
+ verify(mockChipbarCoordinator).unregisterListener(listenerCaptor.value)
+ }
+
+ @Test
+ fun statesWithDifferentIds_onInfoPermanentlyRemovedForOneId_viewListenerNotUnregistered() {
+ val mockChipbarCoordinator = mock<ChipbarCoordinator>()
+ underTest =
+ MediaTttSenderCoordinator(
+ mockChipbarCoordinator,
+ commandQueue,
+ context,
+ logger,
+ mediaTttFlags,
+ uiEventLogger,
+ )
+ underTest.start()
+ setCommandQueueCallback()
+
+ // WHEN there are two different media transfers with different IDs
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+ MediaRoute2Info.Builder("route1", OTHER_DEVICE_NAME)
+ .addFeature("feature")
+ .setClientPackageName(PACKAGE_NAME)
+ .build(),
+ null,
+ )
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+ MediaRoute2Info.Builder("route2", OTHER_DEVICE_NAME)
+ .addFeature("feature")
+ .setClientPackageName(PACKAGE_NAME)
+ .build(),
+ null,
+ )
+
+ val listenerCaptor = argumentCaptor<TemporaryViewDisplayController.Listener>()
+ verify(mockChipbarCoordinator, atLeast(1)).registerListener(capture(listenerCaptor))
+
+ // THEN one of them is removed
+ listenerCaptor.value.onInfoPermanentlyRemoved("route1")
+
+ // THEN the media coordinator doesn't unregister the listener (since route2 is still active)
+ verify(mockChipbarCoordinator, never()).unregisterListener(listenerCaptor.value)
+ }
+
private fun getChipbarView(): ViewGroup {
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
verify(windowManager).addView(viewCaptor.capture(), any())
@@ -960,8 +1132,16 @@
null
)
}
+
+ private fun setCommandQueueCallback() {
+ val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>()
+ verify(commandQueue).addCallback(capture(callbackCaptor))
+ commandQueueCallback = callbackCaptor.value
+ reset(commandQueue)
+ }
}
+private const val DEFAULT_ID = "defaultId"
private const val APP_NAME = "Fake app name"
private const val OTHER_DEVICE_NAME = "My Tablet"
private const val BLANK_DEVICE_NAME = " "
@@ -969,13 +1149,13 @@
private const val TIMEOUT = 10000
private val routeInfo =
- MediaRoute2Info.Builder("id", OTHER_DEVICE_NAME)
+ MediaRoute2Info.Builder(DEFAULT_ID, OTHER_DEVICE_NAME)
.addFeature("feature")
.setClientPackageName(PACKAGE_NAME)
.build()
private val routeInfoWithBlankDeviceName =
- MediaRoute2Info.Builder("id", BLANK_DEVICE_NAME)
+ MediaRoute2Info.Builder(DEFAULT_ID, BLANK_DEVICE_NAME)
.addFeature("feature")
.setClientPackageName(PACKAGE_NAME)
.build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
index 1bc4719..1a35502 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
@@ -31,10 +31,7 @@
import org.junit.runner.RunWith;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -69,7 +66,7 @@
// Expressive applies hue rotations to the theme color. The input theme color has hue
// 117, ensuring the hue changed significantly is a strong signal styles are being applied.
ColorScheme colorScheme = new ColorScheme(wallpaperColors, false, Style.EXPRESSIVE);
- Assert.assertEquals(357.77, Cam.fromInt(colorScheme.getAccent1().get(6)).getHue(), 0.1);
+ Assert.assertEquals(357.77, Cam.fromInt(colorScheme.getAccent1().getS500()).getHue(), 0.1);
}
@@ -111,7 +108,8 @@
public void testTertiaryHueWrapsProperly() {
int colorInt = 0xffB3588A; // H350 C50 T50
ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */);
- int tertiaryMid = colorScheme.getAccent3().get(colorScheme.getAccent3().size() / 2);
+ int tertiaryMid = colorScheme.getAccent3().getAllShades().get(
+ colorScheme.getShadeCount() / 2);
Cam cam = Cam.fromInt(tertiaryMid);
Assert.assertEquals(cam.getHue(), 50.0, 10.0);
}
@@ -121,7 +119,8 @@
int colorInt = 0xffB3588A; // H350 C50 T50
ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */,
Style.SPRITZ /* style */);
- int primaryMid = colorScheme.getAccent1().get(colorScheme.getAccent1().size() / 2);
+ int primaryMid = colorScheme.getAccent1().getAllShades().get(
+ colorScheme.getShadeCount() / 2);
Cam cam = Cam.fromInt(primaryMid);
Assert.assertEquals(cam.getChroma(), 12.0, 1.0);
}
@@ -131,7 +130,8 @@
int colorInt = 0xffB3588A; // H350 C50 T50
ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */,
Style.VIBRANT /* style */);
- int neutralMid = colorScheme.getNeutral1().get(colorScheme.getNeutral1().size() / 2);
+ int neutralMid = colorScheme.getNeutral1().getAllShades().get(
+ colorScheme.getShadeCount() / 2);
Cam cam = Cam.fromInt(neutralMid);
Assert.assertTrue("chroma was " + cam.getChroma(), Math.floor(cam.getChroma()) <= 12.0);
}
@@ -141,7 +141,8 @@
int colorInt = 0xffB3588A; // H350 C50 T50
ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */,
Style.EXPRESSIVE /* style */);
- int neutralMid = colorScheme.getNeutral1().get(colorScheme.getNeutral1().size() / 2);
+ int neutralMid = colorScheme.getNeutral1().getAllShades().get(
+ colorScheme.getShadeCount() / 2);
Cam cam = Cam.fromInt(neutralMid);
Assert.assertTrue(cam.getChroma() <= 8.0);
}
@@ -151,10 +152,11 @@
int colorInt = 0xffB3588A; // H350 C50 T50
ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */,
Style.MONOCHROMATIC /* style */);
- int neutralMid = colorScheme.getNeutral1().get(colorScheme.getNeutral1().size() / 2);
+ int neutralMid = colorScheme.getNeutral1().getAllShades().get(
+ colorScheme.getShadeCount() / 2);
Assert.assertTrue(
Color.red(neutralMid) == Color.green(neutralMid)
- && Color.green(neutralMid) == Color.blue(neutralMid)
+ && Color.green(neutralMid) == Color.blue(neutralMid)
);
}
@@ -190,15 +192,14 @@
xml.append(" <").append(styleName).append(">");
List<String> colors = new ArrayList<>();
- for (Stream<Integer> stream: Arrays.asList(colorScheme.getAccent1().stream(),
- colorScheme.getAccent2().stream(),
- colorScheme.getAccent3().stream(),
- colorScheme.getNeutral1().stream(),
- colorScheme.getNeutral2().stream())) {
+
+ colorScheme.getAllHues().forEach(schemeHue -> {
colors.add("ffffff");
- colors.addAll(stream.map(Integer::toHexString).map(s -> s.substring(2)).collect(
- Collectors.toList()));
- }
+ schemeHue.getAllShades().forEach(tone -> {
+ colors.add(Integer.toHexString(tone).substring(2));
+ });
+ });
+
xml.append(String.join(",", colors));
xml.append("</").append(styleName).append(">\n");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 8b0342e..8058b85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -55,8 +55,8 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
@@ -109,7 +109,8 @@
TaskStackChangeListeners.getTestInstance(),
Optional.of(mock(Pip.class)),
Optional.of(mock(BackAnimation.class)),
- mock(FeatureFlags.class)));
+ mock(FeatureFlags.class),
+ mock(SecureSettings.class)));
initializeNavigationBars();
mMockitoSession = mockitoSession().mockStatic(Utilities.class).startMocking();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
new file mode 100644
index 0000000..a1d42a0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.notetask.quickaffordance
+
+import android.test.suitebuilder.annotation.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
+import com.android.systemui.notetask.NoteTaskController
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+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.verify
+import org.mockito.MockitoAnnotations
+
+/**
+ * Tests for [NoteTaskQuickAffordanceConfig].
+ *
+ * Build/Install/Run:
+ * - atest SystemUITests:NoteTaskQuickAffordanceConfigTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
+
+ @Mock lateinit var noteTaskController: NoteTaskController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(noteTaskController.showNoteTask()).then {}
+ }
+
+ private fun createUnderTest(isEnabled: Boolean) =
+ NoteTaskQuickAffordanceConfig(
+ context = context,
+ noteTaskController = noteTaskController,
+ isEnabled = isEnabled,
+ )
+
+ @Test
+ fun lockScreenState_isNotEnabled_shouldEmitHidden() = runTest {
+ val underTest = createUnderTest(isEnabled = false)
+
+ val actual = collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual()).isEqualTo(LockScreenState.Hidden)
+ }
+
+ @Test
+ fun lockScreenState_isEnabled_shouldEmitVisible() = runTest {
+ val stringResult = "Notetaking"
+ val underTest = createUnderTest(isEnabled = true)
+
+ val actual = collectLastValue(underTest.lockScreenState)
+
+ val expected =
+ LockScreenState.Visible(
+ icon =
+ Icon.Resource(
+ res = R.drawable.ic_note_task_shortcut_keyguard,
+ contentDescription = ContentDescription.Loaded(stringResult),
+ )
+ )
+ assertThat(actual()).isEqualTo(expected)
+ }
+
+ @Test
+ fun onTriggered_shouldLaunchNoteTask() {
+ val underTest = createUnderTest(isEnabled = false)
+
+ underTest.onTriggered(expandable = null)
+
+ verify(noteTaskController).showNoteTask()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index a56990f..4a6158f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -30,6 +30,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
@@ -53,6 +54,7 @@
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.NotificationChannels;
@@ -82,6 +84,8 @@
@Mock
private UiEventLogger mUiEventLogger;
@Mock
+ private UserTracker mUserTracker;
+ @Mock
private View mView;
private BroadcastReceiver mReceiver;
@@ -103,8 +107,12 @@
mContext.addMockSystemService(NotificationManager.class, mMockNotificationManager);
ActivityStarter starter = mDependency.injectMockDependency(ActivityStarter.class);
BroadcastSender broadcastSender = mDependency.injectMockDependency(BroadcastSender.class);
+ when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
+ when(mUserTracker.getUserHandle()).thenReturn(
+ UserHandle.of(ActivityManager.getCurrentUser()));
mPowerNotificationWarnings = new PowerNotificationWarnings(wrapper, starter,
- broadcastSender, () -> mBatteryController, mDialogLaunchAnimator, mUiEventLogger);
+ broadcastSender, () -> mBatteryController, mDialogLaunchAnimator, mUiEventLogger,
+ mUserTracker);
BatteryStateSnapshot snapshot = new BatteryStateSnapshot(100, false, false, 1,
BatteryManager.BATTERY_HEALTH_GOOD, 5, 15);
mPowerNotificationWarnings.updateSnapshot(snapshot);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
index 46a502a..ed3f1a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
@@ -22,15 +22,12 @@
import android.graphics.Insets
import android.graphics.Rect
import android.hardware.HardwareBuffer
-import android.os.Bundle
import android.os.UserHandle
import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER
import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
-import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
-import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler.bundleToHardwareBitmap
-import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
+import com.android.internal.util.ScreenshotRequest
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo
@@ -49,7 +46,6 @@
private val bounds = Rect(25, 25, 75, 75)
private val scope = CoroutineScope(Dispatchers.Unconfined)
- private val dispatcher = Dispatchers.Unconfined
private val policy = FakeScreenshotPolicy()
private val flags = FakeFeatureFlags()
@@ -58,7 +54,8 @@
fun testProcessAsync() {
flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false)
- val request = ScreenshotRequest(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
+ val request =
+ ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD).build()
val processor = RequestProcessor(imageCapture, policy, flags, scope)
var result: ScreenshotRequest? = null
@@ -80,7 +77,8 @@
fun testFullScreenshot_workProfilePolicyDisabled() = runBlocking {
flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false)
- val request = ScreenshotRequest(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
+ val request =
+ ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD).build()
val processor = RequestProcessor(imageCapture, policy, flags, scope)
val processedRequest = processor.process(request)
@@ -97,9 +95,11 @@
policy.setManagedProfile(USER_ID, false)
policy.setDisplayContentInfo(
policy.getDefaultDisplayId(),
- DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID))
+ DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID)
+ )
- val request = ScreenshotRequest(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_OTHER)
+ val request =
+ ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_OTHER).build()
val processor = RequestProcessor(imageCapture, policy, flags, scope)
val processedRequest = processor.process(request)
@@ -120,17 +120,20 @@
// Indicate that the primary content belongs to a manged profile
policy.setManagedProfile(USER_ID, true)
- policy.setDisplayContentInfo(policy.getDefaultDisplayId(),
- DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID))
+ policy.setDisplayContentInfo(
+ policy.getDefaultDisplayId(),
+ DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID)
+ )
- val request = ScreenshotRequest(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
+ val request =
+ ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD).build()
val processor = RequestProcessor(imageCapture, policy, flags, scope)
val processedRequest = processor.process(request)
// Expect a task snapshot is taken, overriding the full screen mode
assertThat(processedRequest.type).isEqualTo(TAKE_SCREENSHOT_PROVIDED_IMAGE)
- assertThat(bitmap.equalsHardwareBitmapBundle(processedRequest.bitmapBundle)).isTrue()
+ assertThat(bitmap.equalsHardwareBitmap(processedRequest.bitmap)).isTrue()
assertThat(processedRequest.boundsInScreen).isEqualTo(bounds)
assertThat(processedRequest.insets).isEqualTo(Insets.NONE)
assertThat(processedRequest.taskId).isEqualTo(TASK_ID)
@@ -147,10 +150,16 @@
val processor = RequestProcessor(imageCapture, policy, flags, scope)
val bitmap = makeHardwareBitmap(100, 100)
- val bitmapBundle = HardwareBitmapBundler.hardwareBitmapToBundle(bitmap)
- val request = ScreenshotRequest(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER,
- bitmapBundle, bounds, Insets.NONE, TASK_ID, USER_ID, component)
+ val request =
+ ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER)
+ .setTopComponent(component)
+ .setTaskId(TASK_ID)
+ .setUserId(USER_ID)
+ .setBitmap(bitmap)
+ .setBoundsOnScreen(bounds)
+ .setInsets(Insets.NONE)
+ .build()
val processedRequest = processor.process(request)
@@ -168,10 +177,16 @@
policy.setManagedProfile(USER_ID, false)
val bitmap = makeHardwareBitmap(100, 100)
- val bitmapBundle = HardwareBitmapBundler.hardwareBitmapToBundle(bitmap)
- val request = ScreenshotRequest(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER,
- bitmapBundle, bounds, Insets.NONE, TASK_ID, USER_ID, component)
+ val request =
+ ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER)
+ .setTopComponent(component)
+ .setTaskId(TASK_ID)
+ .setUserId(USER_ID)
+ .setBitmap(bitmap)
+ .setBoundsOnScreen(bounds)
+ .setInsets(Insets.NONE)
+ .build()
val processedRequest = processor.process(request)
@@ -190,10 +205,16 @@
policy.setManagedProfile(USER_ID, true)
val bitmap = makeHardwareBitmap(100, 100)
- val bitmapBundle = HardwareBitmapBundler.hardwareBitmapToBundle(bitmap)
- val request = ScreenshotRequest(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER,
- bitmapBundle, bounds, Insets.NONE, TASK_ID, USER_ID, component)
+ val request =
+ ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER)
+ .setTopComponent(component)
+ .setTaskId(TASK_ID)
+ .setUserId(USER_ID)
+ .setBitmap(bitmap)
+ .setBoundsOnScreen(bounds)
+ .setInsets(Insets.NONE)
+ .build()
val processedRequest = processor.process(request)
@@ -202,14 +223,18 @@
}
private fun makeHardwareBitmap(width: Int, height: Int): Bitmap {
- val buffer = HardwareBuffer.create(width, height, HardwareBuffer.RGBA_8888, 1,
- HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)
+ val buffer =
+ HardwareBuffer.create(
+ width,
+ height,
+ HardwareBuffer.RGBA_8888,
+ 1,
+ HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
+ )
return Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB))!!
}
- private fun Bitmap.equalsHardwareBitmapBundle(bundle: Bundle): Boolean {
- val provided = bundleToHardwareBitmap(bundle)
- return provided.hardwareBuffer == this.hardwareBuffer &&
- provided.colorSpace == this.colorSpace
+ private fun Bitmap.equalsHardwareBitmap(bitmap: Bitmap): Boolean {
+ return bitmap.hardwareBuffer == this.hardwareBuffer && bitmap.colorSpace == this.colorSpace
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
index 99c79b0..f935019 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -35,8 +35,7 @@
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.internal.util.ScreenshotHelper
-import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
+import com.android.internal.util.ScreenshotRequest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.SCREENSHOT_WORK_PROFILE_POLICY
@@ -80,24 +79,39 @@
private val flags = FakeFeatureFlags()
private val topComponent = ComponentName(mContext, TakeScreenshotServiceTest::class.java)
- private val service = TakeScreenshotService(
- controller, userManager, devicePolicyManager, eventLogger,
- notificationsController, mContext, Runnable::run, flags, requestProcessor)
+ private val service =
+ TakeScreenshotService(
+ controller,
+ userManager,
+ devicePolicyManager,
+ eventLogger,
+ notificationsController,
+ mContext,
+ Runnable::run,
+ flags,
+ requestProcessor
+ )
@Before
fun setUp() {
whenever(devicePolicyManager.resources).thenReturn(devicePolicyResourcesManager)
- whenever(devicePolicyManager.getScreenCaptureDisabled(
- /* admin component (null: any admin) */ isNull(), eq(UserHandle.USER_ALL)))
+ whenever(
+ devicePolicyManager.getScreenCaptureDisabled(
+ /* admin component (null: any admin) */ isNull(),
+ eq(UserHandle.USER_ALL)
+ )
+ )
.thenReturn(false)
whenever(userManager.isUserUnlocked).thenReturn(true)
// Stub request processor as a synchronous no-op for tests with the flag enabled
doAnswer {
- val request: ScreenshotRequest = it.getArgument(0) as ScreenshotRequest
- val consumer: Consumer<ScreenshotRequest> = it.getArgument(1)
- consumer.accept(request)
- }.`when`(requestProcessor).processAsync(/* request= */ any(), /* callback= */ any())
+ val request: ScreenshotRequest = it.getArgument(0) as ScreenshotRequest
+ val consumer: Consumer<ScreenshotRequest> = it.getArgument(1)
+ consumer.accept(request)
+ }
+ .`when`(requestProcessor)
+ .processAsync(/* request= */ any(), /* callback= */ any())
// Flipped in selected test cases
flags.set(SCREENSHOT_WORK_PROFILE_POLICY, false)
@@ -108,7 +122,8 @@
/* className = */ null,
/* token = */ null,
application,
- /* activityManager = */ null)
+ /* activityManager = */ null
+ )
}
@Test
@@ -125,63 +140,89 @@
@Test
fun takeScreenshotFullscreen() {
- val request = ScreenshotRequest(
- TAKE_SCREENSHOT_FULLSCREEN,
- SCREENSHOT_KEY_CHORD,
- topComponent)
+ val request =
+ ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
+ .setTopComponent(topComponent)
+ .build()
- service.handleRequest(request, { /* onSaved */ }, callback)
+ service.handleRequest(request, { /* onSaved */}, callback)
- verify(controller, times(1)).takeScreenshotFullscreen(
- eq(topComponent),
- /* onSavedListener = */ any(),
- /* requestCallback = */ any())
+ verify(controller, times(1))
+ .takeScreenshotFullscreen(
+ eq(topComponent),
+ /* onSavedListener = */ any(),
+ /* requestCallback = */ any()
+ )
assertEquals("Expected one UiEvent", eventLogger.numLogs(), 1)
val logEvent = eventLogger.get(0)
- assertEquals("Expected SCREENSHOT_REQUESTED UiEvent",
- logEvent.eventId, SCREENSHOT_REQUESTED_KEY_CHORD.id)
- assertEquals("Expected supplied package name",
- topComponent.packageName, eventLogger.get(0).packageName)
+ assertEquals(
+ "Expected SCREENSHOT_REQUESTED UiEvent",
+ logEvent.eventId,
+ SCREENSHOT_REQUESTED_KEY_CHORD.id
+ )
+ assertEquals(
+ "Expected supplied package name",
+ topComponent.packageName,
+ eventLogger.get(0).packageName
+ )
}
@Test
fun takeScreenshotProvidedImage() {
val bounds = Rect(50, 50, 150, 150)
val bitmap = makeHardwareBitmap(100, 100)
- val bitmapBundle = ScreenshotHelper.HardwareBitmapBundler.hardwareBitmapToBundle(bitmap)
- val request = ScreenshotRequest(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OVERVIEW,
- bitmapBundle, bounds, Insets.NONE, TASK_ID, USER_ID, topComponent)
+ val request =
+ ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OVERVIEW)
+ .setTopComponent(topComponent)
+ .setTaskId(TASK_ID)
+ .setUserId(USER_ID)
+ .setBitmap(bitmap)
+ .setBoundsOnScreen(bounds)
+ .setInsets(Insets.NONE)
+ .build()
- service.handleRequest(request, { /* onSaved */ }, callback)
+ service.handleRequest(request, { /* onSaved */}, callback)
- verify(controller, times(1)).handleImageAsScreenshot(
- argThat { b -> b.equalsHardwareBitmap(bitmap) },
- eq(bounds),
- eq(Insets.NONE), eq(TASK_ID), eq(USER_ID), eq(topComponent),
- /* onSavedListener = */ any(), /* requestCallback = */ any())
+ verify(controller, times(1))
+ .handleImageAsScreenshot(
+ argThat { b -> b.equalsHardwareBitmap(bitmap) },
+ eq(bounds),
+ eq(Insets.NONE),
+ eq(TASK_ID),
+ eq(USER_ID),
+ eq(topComponent),
+ /* onSavedListener = */ any(),
+ /* requestCallback = */ any()
+ )
assertEquals("Expected one UiEvent", eventLogger.numLogs(), 1)
val logEvent = eventLogger.get(0)
- assertEquals("Expected SCREENSHOT_REQUESTED_* UiEvent",
- logEvent.eventId, SCREENSHOT_REQUESTED_OVERVIEW.id)
- assertEquals("Expected supplied package name",
- topComponent.packageName, eventLogger.get(0).packageName)
+ assertEquals(
+ "Expected SCREENSHOT_REQUESTED_* UiEvent",
+ logEvent.eventId,
+ SCREENSHOT_REQUESTED_OVERVIEW.id
+ )
+ assertEquals(
+ "Expected supplied package name",
+ topComponent.packageName,
+ eventLogger.get(0).packageName
+ )
}
@Test
fun takeScreenshotFullscreen_userLocked() {
whenever(userManager.isUserUnlocked).thenReturn(false)
- val request = ScreenshotRequest(
- TAKE_SCREENSHOT_FULLSCREEN,
- SCREENSHOT_KEY_CHORD,
- topComponent)
+ val request =
+ ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
+ .setTopComponent(topComponent)
+ .build()
- service.handleRequest(request, { /* onSaved */ }, callback)
+ service.handleRequest(request, { /* onSaved */}, callback)
verify(notificationsController, times(1)).notifyScreenshotError(anyInt())
verify(callback, times(1)).reportError()
@@ -190,21 +231,24 @@
@Test
fun takeScreenshotFullscreen_screenCaptureDisabled_allUsers() {
- whenever(devicePolicyManager.getScreenCaptureDisabled(
- isNull(), eq(UserHandle.USER_ALL))
- ).thenReturn(true)
+ whenever(devicePolicyManager.getScreenCaptureDisabled(isNull(), eq(UserHandle.USER_ALL)))
+ .thenReturn(true)
- whenever(devicePolicyResourcesManager.getString(
- eq(SCREENSHOT_BLOCKED_BY_ADMIN),
- /* Supplier<String> */ any(),
- )).thenReturn("SCREENSHOT_BLOCKED_BY_ADMIN")
+ whenever(
+ devicePolicyResourcesManager.getString(
+ eq(SCREENSHOT_BLOCKED_BY_ADMIN),
+ /* Supplier<String> */
+ any(),
+ )
+ )
+ .thenReturn("SCREENSHOT_BLOCKED_BY_ADMIN")
- val request = ScreenshotRequest(
- TAKE_SCREENSHOT_FULLSCREEN,
- SCREENSHOT_KEY_CHORD,
- topComponent)
+ val request =
+ ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
+ .setTopComponent(topComponent)
+ .build()
- service.handleRequest(request, { /* onSaved */ }, callback)
+ service.handleRequest(request, { /* onSaved */}, callback)
// error shown: Toast.makeText(...).show(), untestable
verify(callback, times(1)).reportError()
@@ -214,14 +258,20 @@
private fun Bitmap.equalsHardwareBitmap(other: Bitmap): Boolean {
return config == HARDWARE &&
- other.config == HARDWARE &&
- hardwareBuffer == other.hardwareBuffer &&
- colorSpace == other.colorSpace
+ other.config == HARDWARE &&
+ hardwareBuffer == other.hardwareBuffer &&
+ colorSpace == other.colorSpace
}
/** A hardware Bitmap is mandated by use of ScreenshotHelper.HardwareBitmapBundler */
private fun makeHardwareBitmap(width: Int, height: Int): Bitmap {
- val buffer = HardwareBuffer.create(width, height, HardwareBuffer.RGBA_8888, 1,
- HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)
+ val buffer =
+ HardwareBuffer.create(
+ width,
+ height,
+ HardwareBuffer.RGBA_8888,
+ 1,
+ HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
+ )
return Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB))!!
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
index 3e769e9..76aa08a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
@@ -29,6 +29,7 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.tuner.TunerService
import com.android.systemui.tuner.TunerService.Tunable
@@ -69,6 +70,8 @@
private lateinit var statusBarStateController: StatusBarStateController
@Mock
private lateinit var shadeLogger: ShadeLogger
+ @Mock
+ private lateinit var userTracker: UserTracker
private lateinit var tunableCaptor: ArgumentCaptor<Tunable>
private lateinit var underTest: PulsingGestureListener
@@ -85,6 +88,7 @@
ambientDisplayConfiguration,
statusBarStateController,
shadeLogger,
+ userTracker,
tunerService,
dumpManager
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index be6b1dc..2686238 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -321,7 +321,7 @@
val testDispatcher = UnconfinedTestDispatcher()
val testScope = TestScope(testDispatcher)
val fakeSettings = FakeSettings().apply {
- putBool(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, true)
+ putInt(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 1)
}
val seenNotificationsProvider = SeenNotificationsProviderImpl()
val keyguardCoordinator =
@@ -372,14 +372,14 @@
var showOnlyUnseenNotifsOnKeyguardSetting: Boolean
get() =
- fakeSettings.getBoolForUser(
+ fakeSettings.getIntForUser(
Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
UserHandle.USER_CURRENT,
- )
+ ) == 1
set(value) {
- fakeSettings.putBoolForUser(
+ fakeSettings.putIntForUser(
Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
- value,
+ if (value) 1 else 2,
UserHandle.USER_CURRENT,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index 601771d..03af527 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -41,6 +41,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.PendingIntent;
@@ -59,6 +60,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -107,6 +109,8 @@
UiEventLoggerFake mUiEventLoggerFake;
@Mock
PendingIntent mPendingIntent;
+ @Mock
+ UserTracker mUserTracker;
private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider;
@@ -114,6 +118,7 @@
public void setup() {
MockitoAnnotations.initMocks(this);
when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(false);
+ when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
mUiEventLoggerFake = new UiEventLoggerFake();
@@ -131,7 +136,8 @@
mMockHandler,
mFlags,
mKeyguardNotificationVisibilityProvider,
- mUiEventLoggerFake);
+ mUiEventLoggerFake,
+ mUserTracker);
mNotifInterruptionStateProvider.mUseHeadsUp = true;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
index 1f92b0a..819a75b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.row;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
@@ -98,5 +100,16 @@
mView.setSecondaryVisible(true /* visible */, true /* animate */);
}
+
+ @Test
+ public void testSetFooterLabelTextAndIcon() {
+ mView.setFooterLabelTextAndIcon(
+ R.string.unlock_to_see_notif_text,
+ R.drawable.ic_friction_lock_closed);
+ assertThat(mView.findViewById(R.id.manage_text).getVisibility()).isEqualTo(View.GONE);
+ assertThat(mView.findSecondaryView().getVisibility()).isEqualTo(View.GONE);
+ assertThat(mView.findViewById(R.id.unlock_prompt_footer).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
}
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 645052f..9f6f082 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
@@ -21,6 +21,7 @@
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
@@ -65,6 +66,7 @@
import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl;
import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifStats;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -78,6 +80,7 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.settings.SecureSettings;
import org.junit.Before;
import org.junit.Test;
@@ -135,10 +138,14 @@
@Mock private ShadeTransitionController mShadeTransitionController;
@Mock private FeatureFlags mFeatureFlags;
@Mock private NotificationTargetsHelper mNotificationTargetsHelper;
+ @Mock private SecureSettings mSecureSettings;
@Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
+ private final SeenNotificationsProviderImpl mSeenNotificationsProvider =
+ new SeenNotificationsProviderImpl();
+
private NotificationStackScrollLayoutController mController;
@Before
@@ -180,14 +187,15 @@
mUiEventLogger,
mRemoteInputManager,
mVisibilityLocationProviderDelegator,
- new SeenNotificationsProviderImpl(),
+ mSeenNotificationsProvider,
mShadeController,
mJankMonitor,
mStackLogger,
mLogger,
mNotificationStackSizeCalculator,
mFeatureFlags,
- mNotificationTargetsHelper
+ mNotificationTargetsHelper,
+ mSecureSettings
);
when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true);
@@ -233,16 +241,14 @@
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ true,
- /* notifVisibleInShade= */ true,
- /* areSeenNotifsFiltered= */false);
+ /* notifVisibleInShade= */ true);
setupShowEmptyShadeViewState(false);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ false,
- /* notifVisibleInShade= */ true,
- /* areSeenNotifsFiltered= */false);
+ /* notifVisibleInShade= */ true);
}
@Test
@@ -255,16 +261,14 @@
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ true,
- /* notifVisibleInShade= */ false,
- /* areSeenNotifsFiltered= */false);
+ /* notifVisibleInShade= */ false);
setupShowEmptyShadeViewState(false);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ false,
- /* notifVisibleInShade= */ false,
- /* areSeenNotifsFiltered= */false);
+ /* notifVisibleInShade= */ false);
}
@Test
@@ -283,16 +287,14 @@
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ true,
- /* notifVisibleInShade= */ false,
- /* areSeenNotifsFiltered= */false);
+ /* notifVisibleInShade= */ false);
mController.setQsFullScreen(true);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ true,
- /* notifVisibleInShade= */ false,
- /* areSeenNotifsFiltered= */false);
+ /* notifVisibleInShade= */ false);
}
@Test
@@ -400,6 +402,17 @@
verify(mNotificationStackScrollLayout).setIsRemoteInputActive(true);
}
+ @Test
+ public void testSetNotifStats_updatesHasFilteredOutSeenNotifications() {
+ when(mNotifPipelineFlags.getShouldFilterUnseenNotifsOnKeyguard()).thenReturn(true);
+ mSeenNotificationsProvider.setHasFilteredOutSeenNotifications(true);
+ mController.attach(mNotificationStackScrollLayout);
+ mController.getNotifStackController().setNotifStats(NotifStats.getEmpty());
+ verify(mNotificationStackScrollLayout).setHasFilteredOutSeenNotifications(true);
+ verify(mNotificationStackScrollLayout).updateFooter();
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(anyBoolean(), anyBoolean());
+ }
+
private LogMaker logMatcher(int category, int type) {
return argThat(new LogMatcher(category, type));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 7622549..dd7143a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -30,6 +30,7 @@
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
+import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -53,6 +54,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.TextView;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
@@ -328,7 +330,7 @@
public void updateEmptyView_dndSuppressing() {
when(mEmptyShadeView.willBeGone()).thenReturn(true);
- mStackScroller.updateEmptyShadeView(true, true, false);
+ mStackScroller.updateEmptyShadeView(true, true);
verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
}
@@ -338,7 +340,7 @@
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mEmptyShadeView.willBeGone()).thenReturn(true);
- mStackScroller.updateEmptyShadeView(true, false, false);
+ mStackScroller.updateEmptyShadeView(true, false);
verify(mEmptyShadeView).setText(R.string.empty_shade_text);
}
@@ -347,10 +349,10 @@
public void updateEmptyView_noNotificationsToDndSuppressing() {
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mEmptyShadeView.willBeGone()).thenReturn(true);
- mStackScroller.updateEmptyShadeView(true, false, false);
+ mStackScroller.updateEmptyShadeView(true, false);
verify(mEmptyShadeView).setText(R.string.empty_shade_text);
- mStackScroller.updateEmptyShadeView(true, true, false);
+ mStackScroller.updateEmptyShadeView(true, true);
verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
}
@@ -818,6 +820,29 @@
assertEquals(0f, mAmbientState.getStackY());
}
+ @Test
+ public void hasFilteredOutSeenNotifs_updateFooter() {
+ mStackScroller.setCurrentUserSetup(true);
+
+ // add footer
+ mStackScroller.inflateFooterView();
+ TextView footerLabel =
+ mStackScroller.mFooterView.requireViewById(R.id.unlock_prompt_footer);
+
+ mStackScroller.setHasFilteredOutSeenNotifications(true);
+ mStackScroller.updateFooter();
+
+ assertThat(footerLabel.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void hasFilteredOutSeenNotifs_updateEmptyShadeView() {
+ mStackScroller.setHasFilteredOutSeenNotifications(true);
+ mStackScroller.updateEmptyShadeView(true, false);
+
+ verify(mEmptyShadeView).setFooterText(not(0));
+ }
+
private void setBarStateForTest(int state) {
// Can't inject this through the listener or we end up on the actual implementation
// rather than the mock because the spy just coppied the anonymous inner /shruggie.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 3fccd37..b879cf2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -25,8 +25,10 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.app.StatusBarManager;
import android.os.PowerManager;
+import android.os.UserHandle;
import android.os.Vibrator;
import android.testing.AndroidTestingRunner;
import android.view.InsetsVisibilities;
@@ -41,6 +43,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.CameraLauncher;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeController;
@@ -88,6 +91,7 @@
@Mock private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
@Mock private SystemBarAttributesListener mSystemBarAttributesListener;
@Mock private Lazy<CameraLauncher> mCameraLauncherLazy;
+ @Mock private UserTracker mUserTracker;
CentralSurfacesCommandQueueCallbacks mSbcqCallbacks;
@@ -120,8 +124,11 @@
new DisableFlagsLogger(),
DEFAULT_DISPLAY,
mSystemBarAttributesListener,
- mCameraLauncherLazy);
+ mCameraLauncherLazy,
+ mUserTracker);
+ when(mUserTracker.getUserHandle()).thenReturn(
+ UserHandle.of(ActivityManager.getCurrentUser()));
when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
when(mRemoteInputQuickSettingsDisabler.adjustDisableFlags(anyInt()))
.thenAnswer((Answer<Integer>) invocation -> invocation.getArgument(0));
@@ -140,7 +147,7 @@
// Trying to open it does nothing.
mSbcqCallbacks.animateExpandNotificationsPanel();
- verify(mNotificationPanelViewController, never()).expandWithoutQs();
+ verify(mNotificationPanelViewController, never()).expandShadeToNotifications();
mSbcqCallbacks.animateExpandSettingsPanel(null);
verify(mNotificationPanelViewController, never()).expand(anyBoolean());
}
@@ -158,7 +165,7 @@
// Can now be opened.
mSbcqCallbacks.animateExpandNotificationsPanel();
- verify(mNotificationPanelViewController).expandWithoutQs();
+ verify(mNotificationPanelViewController).expandShadeToNotifications();
mSbcqCallbacks.animateExpandSettingsPanel(null);
verify(mNotificationPanelViewController).expandWithQs();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 4c1b219..b1363a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -44,6 +44,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.app.IWallpaperManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -118,6 +119,7 @@
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.ScreenPinningRequest;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.shade.CameraLauncher;
import com.android.systemui.shade.NotificationPanelView;
@@ -306,6 +308,7 @@
*/
@Mock private ViewRootImpl mViewRootImpl;
@Mock private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
+ @Mock private UserTracker mUserTracker;
@Captor private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
@Mock IPowerManager mPowerManagerService;
@@ -321,6 +324,10 @@
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
+ // CentralSurfacesImpl's runtime flag check fails if the flag is absent.
+ // This value is unused, because test manifest is opted in.
+ mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false);
+
IThermalService thermalService = mock(IThermalService.class);
mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService,
Handler.createAsync(Looper.myLooper()));
@@ -338,7 +345,8 @@
new Handler(TestableLooper.get(this).getLooper()),
mock(NotifPipelineFlags.class),
mock(KeyguardNotificationVisibilityProvider.class),
- mock(UiEventLogger.class));
+ mock(UiEventLogger.class),
+ mUserTracker);
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -419,6 +427,9 @@
when(mOperatorNameViewControllerFactory.create(any()))
.thenReturn(mOperatorNameViewController);
+ when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
+ when(mUserTracker.getUserHandle()).thenReturn(
+ UserHandle.of(ActivityManager.getCurrentUser()));
mCentralSurfaces = new CentralSurfacesImpl(
mContext,
@@ -508,7 +519,8 @@
mDreamManager,
mCameraLauncherLazy,
() -> mLightRevealScrimViewModel,
- mAlternateBouncerInteractor
+ mAlternateBouncerInteractor,
+ mUserTracker
) {
@Override
protected ViewRootImpl getViewRootImpl() {
@@ -1280,7 +1292,8 @@
Handler mainHandler,
NotifPipelineFlags flags,
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ UserTracker userTracker) {
super(
contentResolver,
powerManager,
@@ -1294,7 +1307,8 @@
mainHandler,
flags,
keyguardNotificationVisibilityProvider,
- uiEventLogger
+ uiEventLogger,
+ userTracker
);
mUseHeadsUp = true;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index c843850..eb5edbc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.content.res.Resources;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
@@ -44,6 +45,7 @@
import com.android.systemui.doze.DozeScreenState;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -82,6 +84,7 @@
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private ConfigurationController mConfigurationController;
+ @Mock private UserTracker mUserTracker;
@Captor private ArgumentCaptor<BatteryStateChangeCallback> mBatteryStateChangeCallback;
/**
@@ -107,6 +110,7 @@
when(mSysUIUnfoldComponent.getFoldAodAnimationController())
.thenReturn(mFoldAodAnimationController);
+ when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
mDozeParameters = new DozeParameters(
mContext,
@@ -123,7 +127,8 @@
mUnlockedScreenOffAnimationController,
mKeyguardUpdateMonitor,
mConfigurationController,
- mStatusBarStateController
+ mStatusBarStateController,
+ mUserTracker
);
verify(mBatteryController).addCallback(mBatteryStateChangeCallback.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
index 64dee95..305b9fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.screenrecord.RecordingController
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.BluetoothController
import com.android.systemui.statusbar.policy.CastController
@@ -71,61 +72,37 @@
private const val ALARM_SLOT = "alarm"
}
- @Mock
- private lateinit var iconController: StatusBarIconController
- @Mock
- private lateinit var commandQueue: CommandQueue
- @Mock
- private lateinit var broadcastDispatcher: BroadcastDispatcher
- @Mock
- private lateinit var castController: CastController
- @Mock
- private lateinit var hotspotController: HotspotController
- @Mock
- private lateinit var bluetoothController: BluetoothController
- @Mock
- private lateinit var nextAlarmController: NextAlarmController
- @Mock
- private lateinit var userInfoController: UserInfoController
- @Mock
- private lateinit var rotationLockController: RotationLockController
- @Mock
- private lateinit var dataSaverController: DataSaverController
- @Mock
- private lateinit var zenModeController: ZenModeController
- @Mock
- private lateinit var deviceProvisionedController: DeviceProvisionedController
- @Mock
- private lateinit var keyguardStateController: KeyguardStateController
- @Mock
- private lateinit var locationController: LocationController
- @Mock
- private lateinit var sensorPrivacyController: SensorPrivacyController
- @Mock
- private lateinit var iActivityManager: IActivityManager
- @Mock
- private lateinit var alarmManager: AlarmManager
- @Mock
- private lateinit var userManager: UserManager
- @Mock
- private lateinit var devicePolicyManager: DevicePolicyManager
- @Mock
- private lateinit var recordingController: RecordingController
- @Mock
- private lateinit var telecomManager: TelecomManager
- @Mock
- private lateinit var sharedPreferences: SharedPreferences
- @Mock
- private lateinit var dateFormatUtil: DateFormatUtil
+ @Mock private lateinit var iconController: StatusBarIconController
+ @Mock private lateinit var commandQueue: CommandQueue
+ @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock private lateinit var castController: CastController
+ @Mock private lateinit var hotspotController: HotspotController
+ @Mock private lateinit var bluetoothController: BluetoothController
+ @Mock private lateinit var nextAlarmController: NextAlarmController
+ @Mock private lateinit var userInfoController: UserInfoController
+ @Mock private lateinit var rotationLockController: RotationLockController
+ @Mock private lateinit var dataSaverController: DataSaverController
+ @Mock private lateinit var zenModeController: ZenModeController
+ @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var locationController: LocationController
+ @Mock private lateinit var sensorPrivacyController: SensorPrivacyController
+ @Mock private lateinit var iActivityManager: IActivityManager
+ @Mock private lateinit var alarmManager: AlarmManager
+ @Mock private lateinit var userManager: UserManager
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var devicePolicyManager: DevicePolicyManager
+ @Mock private lateinit var recordingController: RecordingController
+ @Mock private lateinit var telecomManager: TelecomManager
+ @Mock private lateinit var sharedPreferences: SharedPreferences
+ @Mock private lateinit var dateFormatUtil: DateFormatUtil
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private lateinit var ringerModeTracker: RingerModeTracker
- @Mock
- private lateinit var privacyItemController: PrivacyItemController
- @Mock
- private lateinit var privacyLogger: PrivacyLogger
+ @Mock private lateinit var privacyItemController: PrivacyItemController
+ @Mock private lateinit var privacyLogger: PrivacyLogger
@Captor
private lateinit var alarmCallbackCaptor:
- ArgumentCaptor<NextAlarmController.NextAlarmChangeCallback>
+ ArgumentCaptor<NextAlarmController.NextAlarmChangeCallback>
private lateinit var executor: FakeExecutor
private lateinit var statusBarPolicy: PhoneStatusBarPolicy
@@ -137,8 +114,8 @@
executor = FakeExecutor(FakeSystemClock())
testableLooper = TestableLooper.get(this)
context.orCreateTestableResources.addOverride(
- com.android.internal.R.string.status_bar_alarm_clock,
- ALARM_SLOT
+ com.android.internal.R.string.status_bar_alarm_clock,
+ ALARM_SLOT
)
statusBarPolicy = createStatusBarPolicy()
}
@@ -195,36 +172,37 @@
private fun createStatusBarPolicy(): PhoneStatusBarPolicy {
return PhoneStatusBarPolicy(
- iconController,
- commandQueue,
- broadcastDispatcher,
- executor,
- testableLooper.looper,
- context.resources,
- castController,
- hotspotController,
- bluetoothController,
- nextAlarmController,
- userInfoController,
- rotationLockController,
- dataSaverController,
- zenModeController,
- deviceProvisionedController,
- keyguardStateController,
- locationController,
- sensorPrivacyController,
- iActivityManager,
- alarmManager,
- userManager,
- devicePolicyManager,
- recordingController,
- telecomManager,
- /* displayId = */ 0,
- sharedPreferences,
- dateFormatUtil,
- ringerModeTracker,
- privacyItemController,
- privacyLogger
+ iconController,
+ commandQueue,
+ broadcastDispatcher,
+ executor,
+ testableLooper.looper,
+ context.resources,
+ castController,
+ hotspotController,
+ bluetoothController,
+ nextAlarmController,
+ userInfoController,
+ rotationLockController,
+ dataSaverController,
+ zenModeController,
+ deviceProvisionedController,
+ keyguardStateController,
+ locationController,
+ sensorPrivacyController,
+ iActivityManager,
+ alarmManager,
+ userManager,
+ userTracker,
+ devicePolicyManager,
+ recordingController,
+ telecomManager,
+ /* displayId = */ 0,
+ sharedPreferences,
+ dateFormatUtil,
+ ringerModeTracker,
+ privacyItemController,
+ privacyLogger
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index c7a0582..c0537a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -630,6 +630,7 @@
@Test
public void transitionToUnlocked() {
+ mScrimController.setClipsQsScrim(false);
mScrimController.setRawPanelExpansionFraction(0f);
mScrimController.transitionTo(ScrimState.UNLOCKED);
finishAnimationsImmediately();
@@ -645,14 +646,21 @@
));
// Back scrim should be visible after start dragging
- mScrimController.setRawPanelExpansionFraction(0.3f);
+ mScrimController.setRawPanelExpansionFraction(0.29f);
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
mNotificationsScrim, TRANSPARENT,
mScrimBehind, SEMI_TRANSPARENT));
+ // Back scrim should be opaque at 30%
+ mScrimController.setRawPanelExpansionFraction(0.3f);
+ assertScrimAlpha(Map.of(
+ mScrimInFront, TRANSPARENT,
+ mNotificationsScrim, TRANSPARENT,
+ mScrimBehind, OPAQUE));
+
// Then, notification scrim should fade in
- mScrimController.setRawPanelExpansionFraction(0.7f);
+ mScrimController.setRawPanelExpansionFraction(0.31f);
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
mNotificationsScrim, SEMI_TRANSPARENT,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 19658e6..ccc57ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -33,6 +33,7 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -59,6 +60,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shade.ShadeControllerImpl;
@@ -139,6 +141,8 @@
private ActivityLaunchAnimator mActivityLaunchAnimator;
@Mock
private InteractionJankMonitor mJankMonitor;
+ @Mock
+ private UserTracker mUserTracker;
private final FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private ExpandableNotificationRow mNotificationRow;
private ExpandableNotificationRow mBubbleNotificationRow;
@@ -183,6 +187,8 @@
when(mVisibilityProvider.obtain(any(NotificationEntry.class), anyBoolean()))
.thenAnswer(invocation -> NotificationVisibility.obtain(
invocation.<NotificationEntry>getArgument(0).getKey(), 0, 1, false));
+ when(mUserTracker.getUserHandle()).thenReturn(
+ UserHandle.of(ActivityManager.getCurrentUser()));
HeadsUpManagerPhone headsUpManager = mock(HeadsUpManagerPhone.class);
NotificationLaunchAnimatorControllerProvider notificationAnimationProvider =
@@ -222,7 +228,8 @@
mActivityLaunchAnimator,
notificationAnimationProvider,
mock(LaunchFullScreenIntentProvider.class),
- mock(FeatureFlags.class)
+ mock(FeatureFlags.class),
+ mUserTracker
);
// set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index d30222f..711e4ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -50,7 +50,9 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.*
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.ArgumentMatchers.nullable
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.eq
@@ -83,7 +85,8 @@
private lateinit var notifCollectionListener: NotifCollectionListener
@Mock private lateinit var mockOngoingCallFlags: OngoingCallFlags
- @Mock private lateinit var mockSwipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler
+ @Mock private lateinit var mockSwipeStatusBarAwayGestureHandler:
+ SwipeStatusBarAwayGestureHandler
@Mock private lateinit var mockOngoingCallListener: OngoingCallListener
@Mock private lateinit var mockActivityStarter: ActivityStarter
@Mock private lateinit var mockIActivityManager: IActivityManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index 99e2012..45f7df3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -159,7 +159,7 @@
underTest.displayView(getState())
assertThat(fakeWakeLock.isHeld).isTrue()
- underTest.removeView("id", "test reason")
+ underTest.removeView(DEFAULT_ID, "test reason")
assertThat(fakeWakeLock.isHeld).isFalse()
}
@@ -175,6 +175,8 @@
@Test
fun displayView_twiceWithDifferentIds_oldViewRemovedNewViewAdded() {
+ val listener = registerListener()
+
underTest.displayView(
ViewInfo(
name = "name",
@@ -199,10 +201,15 @@
assertThat(windowParamsCaptor.allValues[0].title).isEqualTo("First Fake Window Title")
assertThat(windowParamsCaptor.allValues[1].title).isEqualTo("Second Fake Window Title")
verify(windowManager).removeView(viewCaptor.allValues[0])
+ // Since the controller is still storing the older view in case it'll get re-displayed
+ // later, the listener shouldn't be notified
+ assertThat(listener.permanentlyRemovedIds).isEmpty()
}
@Test
fun displayView_viewDoesNotDisappearsBeforeTimeout() {
+ val listener = registerListener()
+
val state = getState()
underTest.displayView(state)
reset(windowManager)
@@ -210,10 +217,13 @@
fakeClock.advanceTime(TIMEOUT_MS - 1)
verify(windowManager, never()).removeView(any())
+ assertThat(listener.permanentlyRemovedIds).isEmpty()
}
@Test
fun displayView_viewDisappearsAfterTimeout() {
+ val listener = registerListener()
+
val state = getState()
underTest.displayView(state)
reset(windowManager)
@@ -221,10 +231,13 @@
fakeClock.advanceTime(TIMEOUT_MS + 1)
verify(windowManager).removeView(any())
+ assertThat(listener.permanentlyRemovedIds).containsExactly(DEFAULT_ID)
}
@Test
fun displayView_calledAgainBeforeTimeout_timeoutReset() {
+ val listener = registerListener()
+
// First, display the view
val state = getState()
underTest.displayView(state)
@@ -239,10 +252,13 @@
// Verify we didn't hide the view
verify(windowManager, never()).removeView(any())
+ assertThat(listener.permanentlyRemovedIds).isEmpty()
}
@Test
fun displayView_calledAgainBeforeTimeout_eventuallyTimesOut() {
+ val listener = registerListener()
+
// First, display the view
val state = getState()
underTest.displayView(state)
@@ -255,6 +271,7 @@
fakeClock.advanceTime(TIMEOUT_MS + 1)
verify(windowManager).removeView(any())
+ assertThat(listener.permanentlyRemovedIds).containsExactly(DEFAULT_ID)
}
@Test
@@ -271,25 +288,9 @@
}
@Test
- fun viewUpdatedWithNewOnViewTimeoutRunnable_newRunnableUsed() {
- var runnable1Run = false
- underTest.displayView(ViewInfo(name = "name", id = "id1", windowTitle = "1")) {
- runnable1Run = true
- }
-
- var runnable2Run = false
- underTest.displayView(ViewInfo(name = "name", id = "id1", windowTitle = "1")) {
- runnable2Run = true
- }
-
- fakeClock.advanceTime(TIMEOUT_MS + 1)
-
- assertThat(runnable1Run).isFalse()
- assertThat(runnable2Run).isTrue()
- }
-
- @Test
fun multipleViewsWithDifferentIds_moreRecentReplacesOlder() {
+ val listener = registerListener()
+
underTest.displayView(
ViewInfo(
name = "name",
@@ -315,10 +316,16 @@
assertThat(windowParamsCaptor.allValues[1].title).isEqualTo("Second Fake Window Title")
verify(windowManager).removeView(viewCaptor.allValues[0])
verify(configurationController, never()).removeCallback(any())
+
+ // Since the controller is still storing the older view in case it'll get re-displayed
+ // later, the listener shouldn't be notified
+ assertThat(listener.permanentlyRemovedIds).isEmpty()
}
@Test
- fun multipleViewsWithDifferentIds_recentActiveViewIsDisplayed() {
+ fun multipleViewsWithDifferentIds_newViewRemoved_previousViewIsDisplayed() {
+ val listener = registerListener()
+
underTest.displayView(ViewInfo("First name", id = "id1"))
verify(windowManager).addView(any(), any())
@@ -329,24 +336,35 @@
verify(windowManager).removeView(any())
verify(windowManager).addView(any(), any())
reset(windowManager)
+ assertThat(listener.permanentlyRemovedIds).isEmpty()
+ // WHEN the current view is removed
underTest.removeView("id2", "test reason")
+ // THEN it's correctly removed
verify(windowManager).removeView(any())
+ assertThat(listener.permanentlyRemovedIds).containsExactly("id2")
+
+ // And the previous view is correctly added
verify(windowManager).addView(any(), any())
assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id1")
assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("First name")
+ // WHEN the previous view times out
reset(windowManager)
fakeClock.advanceTime(TIMEOUT_MS + 1)
+ // THEN it is also removed
verify(windowManager).removeView(any())
assertThat(underTest.activeViews.size).isEqualTo(0)
verify(configurationController).removeCallback(any())
+ assertThat(listener.permanentlyRemovedIds).isEqualTo(listOf("id2", "id1"))
}
@Test
fun multipleViewsWithDifferentIds_oldViewRemoved_recentViewIsDisplayed() {
+ val listener = registerListener()
+
underTest.displayView(ViewInfo("First name", id = "id1"))
verify(windowManager).addView(any(), any())
@@ -361,7 +379,8 @@
// WHEN an old view is removed
underTest.removeView("id1", "test reason")
- // THEN we don't update anything
+ // THEN we don't update anything except the listener
+ assertThat(listener.permanentlyRemovedIds).containsExactly("id1")
verify(windowManager, never()).removeView(any())
assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id2")
assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name")
@@ -372,10 +391,13 @@
verify(windowManager).removeView(any())
assertThat(underTest.activeViews.size).isEqualTo(0)
verify(configurationController).removeCallback(any())
+ assertThat(listener.permanentlyRemovedIds).isEqualTo(listOf("id1", "id2"))
}
@Test
fun multipleViewsWithDifferentIds_threeDifferentViews_recentActiveViewIsDisplayed() {
+ val listener = registerListener()
+
underTest.displayView(ViewInfo("First name", id = "id1"))
underTest.displayView(ViewInfo("Second name", id = "id2"))
underTest.displayView(ViewInfo("Third name", id = "id3"))
@@ -387,6 +409,7 @@
underTest.removeView("id3", "test reason")
verify(windowManager).removeView(any())
+ assertThat(listener.permanentlyRemovedIds).isEqualTo(listOf("id3"))
assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id2")
assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name")
verify(configurationController, never()).removeCallback(any())
@@ -395,6 +418,7 @@
underTest.removeView("id2", "test reason")
verify(windowManager).removeView(any())
+ assertThat(listener.permanentlyRemovedIds).isEqualTo(listOf("id3", "id2"))
assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id1")
assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("First name")
verify(configurationController, never()).removeCallback(any())
@@ -403,6 +427,7 @@
fakeClock.advanceTime(TIMEOUT_MS + 1)
verify(windowManager).removeView(any())
+ assertThat(listener.permanentlyRemovedIds).isEqualTo(listOf("id3", "id2", "id1"))
assertThat(underTest.activeViews.size).isEqualTo(0)
verify(configurationController).removeCallback(any())
}
@@ -438,6 +463,8 @@
@Test
fun multipleViews_mostRecentViewRemoved_otherViewsTimedOutAndNotDisplayed() {
+ val listener = registerListener()
+
underTest.displayView(ViewInfo("First name", id = "id1", timeoutMs = 4000))
fakeClock.advanceTime(1000)
underTest.displayView(ViewInfo("Second name", id = "id2", timeoutMs = 4000))
@@ -451,10 +478,13 @@
verify(windowManager, never()).addView(any(), any())
assertThat(underTest.activeViews.size).isEqualTo(0)
verify(configurationController).removeCallback(any())
+ assertThat(listener.permanentlyRemovedIds).containsExactly("id1", "id2", "id3")
}
@Test
fun multipleViews_mostRecentViewRemoved_viewWithShortTimeLeftNotDisplayed() {
+ val listener = registerListener()
+
underTest.displayView(ViewInfo("First name", id = "id1", timeoutMs = 4000))
fakeClock.advanceTime(1000)
underTest.displayView(ViewInfo("Second name", id = "id2", timeoutMs = 2500))
@@ -467,10 +497,13 @@
verify(windowManager, never()).addView(any(), any())
assertThat(underTest.activeViews.size).isEqualTo(0)
verify(configurationController).removeCallback(any())
+ assertThat(listener.permanentlyRemovedIds).containsExactly("id1", "id2")
}
@Test
fun lowerThenHigherPriority_higherReplacesLower() {
+ val listener = registerListener()
+
underTest.displayView(
ViewInfo(
name = "normal",
@@ -499,10 +532,15 @@
verify(windowManager).addView(capture(viewCaptor), capture(windowParamsCaptor))
assertThat(windowParamsCaptor.value.title).isEqualTo("Critical Window Title")
verify(configurationController, never()).removeCallback(any())
+ // Since the controller is still storing the older view in case it'll get re-displayed
+ // later, the listener shouldn't be notified
+ assertThat(listener.permanentlyRemovedIds).isEmpty()
}
@Test
fun lowerThenHigherPriority_lowerPriorityRedisplayed() {
+ val listener = registerListener()
+
underTest.displayView(
ViewInfo(
name = "normal",
@@ -537,6 +575,7 @@
// THEN the normal view is re-displayed
verify(windowManager).removeView(viewCaptor.allValues[1])
+ assertThat(listener.permanentlyRemovedIds).containsExactly("critical")
verify(windowManager).addView(any(), capture(windowParamsCaptor))
assertThat(windowParamsCaptor.value.title).isEqualTo("Normal Window Title")
verify(configurationController, never()).removeCallback(any())
@@ -544,6 +583,8 @@
@Test
fun lowerThenHigherPriority_lowerPriorityNotRedisplayedBecauseTimedOut() {
+ val listener = registerListener()
+
underTest.displayView(
ViewInfo(
name = "normal",
@@ -573,6 +614,7 @@
verify(windowManager, never()).addView(any(), any())
assertThat(underTest.activeViews).isEmpty()
verify(configurationController).removeCallback(any())
+ assertThat(listener.permanentlyRemovedIds).containsExactly("critical", "normal")
}
@Test
@@ -609,6 +651,8 @@
@Test
fun higherThenLowerPriority_lowerEventuallyDisplayed() {
+ val listener = registerListener()
+
underTest.displayView(
ViewInfo(
name = "critical",
@@ -644,6 +688,7 @@
// THEN the second normal view is displayed
verify(windowManager).removeView(viewCaptor.value)
+ assertThat(listener.permanentlyRemovedIds).containsExactly("critical")
verify(windowManager).addView(capture(viewCaptor), capture(windowParamsCaptor))
assertThat(windowParamsCaptor.value.title).isEqualTo("Normal Window Title")
assertThat(underTest.activeViews.size).isEqualTo(1)
@@ -652,6 +697,8 @@
@Test
fun higherThenLowerPriority_lowerNotDisplayedBecauseTimedOut() {
+ val listener = registerListener()
+
underTest.displayView(
ViewInfo(
name = "critical",
@@ -691,10 +738,13 @@
verify(windowManager, never()).addView(any(), any())
assertThat(underTest.activeViews).isEmpty()
verify(configurationController).removeCallback(any())
+ assertThat(listener.permanentlyRemovedIds).containsExactly("critical", "normal")
}
@Test
fun criticalThenNewCritical_newCriticalDisplayed() {
+ val listener = registerListener()
+
underTest.displayView(
ViewInfo(
name = "critical 1",
@@ -724,10 +774,15 @@
assertThat(windowParamsCaptor.value.title).isEqualTo("Critical Window Title 2")
assertThat(underTest.activeViews.size).isEqualTo(2)
verify(configurationController, never()).removeCallback(any())
+ // Since the controller is still storing the older view in case it'll get re-displayed
+ // later, the listener shouldn't be notified
+ assertThat(listener.permanentlyRemovedIds).isEmpty()
}
@Test
fun normalThenNewNormal_newNormalDisplayed() {
+ val listener = registerListener()
+
underTest.displayView(
ViewInfo(
name = "normal 1",
@@ -757,6 +812,9 @@
assertThat(windowParamsCaptor.value.title).isEqualTo("Normal Window Title 2")
assertThat(underTest.activeViews.size).isEqualTo(2)
verify(configurationController, never()).removeCallback(any())
+ // Since the controller is still storing the older view in case it'll get re-displayed
+ // later, the listener shouldn't be notified
+ assertThat(listener.permanentlyRemovedIds).isEmpty()
}
@Test
@@ -957,25 +1015,103 @@
}
@Test
- fun removeView_viewRemovedAndRemovalLogged() {
+ fun removeView_viewRemovedAndRemovalLoggedAndListenerNotified() {
+ val listener = registerListener()
+
// First, add the view
underTest.displayView(getState())
// Then, remove it
val reason = "test reason"
- val deviceId = "id"
- underTest.removeView(deviceId, reason)
+ underTest.removeView(DEFAULT_ID, reason)
verify(windowManager).removeView(any())
- verify(logger).logViewRemoval(deviceId, reason)
+ verify(logger).logViewRemoval(DEFAULT_ID, reason)
verify(configurationController).removeCallback(any())
+ assertThat(listener.permanentlyRemovedIds).containsExactly(DEFAULT_ID)
}
@Test
- fun removeView_noAdd_viewNotRemoved() {
+ fun removeView_noAdd_viewNotRemovedAndListenerNotNotified() {
+ val listener = registerListener()
+
underTest.removeView("id", "reason")
verify(windowManager, never()).removeView(any())
+ assertThat(listener.permanentlyRemovedIds).isEmpty()
+ }
+
+ @Test
+ fun listenerRegistered_notifiedOnRemoval() {
+ val listener = registerListener()
+ underTest.displayView(getState())
+
+ underTest.removeView(DEFAULT_ID, "reason")
+
+ assertThat(listener.permanentlyRemovedIds).containsExactly(DEFAULT_ID)
+ }
+
+ @Test
+ fun listenerRegistered_notifiedOnTimedOutEvenWhenNotDisplayed() {
+ val listener = registerListener()
+ underTest.displayView(
+ ViewInfo(
+ id = "id1",
+ name = "name1",
+ timeoutMs = 3000,
+ ),
+ )
+
+ // Display a second view
+ underTest.displayView(
+ ViewInfo(
+ id = "id2",
+ name = "name2",
+ timeoutMs = 2500,
+ ),
+ )
+
+ // WHEN the second view times out
+ fakeClock.advanceTime(2501)
+
+ // THEN the listener is notified of both IDs, since id2 timed out and id1 doesn't have
+ // enough time left to be redisplayed
+ assertThat(listener.permanentlyRemovedIds).containsExactly("id1", "id2")
+ }
+
+ @Test
+ fun multipleListeners_allNotified() {
+ val listener1 = registerListener()
+ val listener2 = registerListener()
+ val listener3 = registerListener()
+
+ underTest.displayView(getState())
+
+ underTest.removeView(DEFAULT_ID, "reason")
+
+ assertThat(listener1.permanentlyRemovedIds).containsExactly(DEFAULT_ID)
+ assertThat(listener2.permanentlyRemovedIds).containsExactly(DEFAULT_ID)
+ assertThat(listener3.permanentlyRemovedIds).containsExactly(DEFAULT_ID)
+ }
+
+ @Test
+ fun sameListenerRegisteredMultipleTimes_onlyNotifiedOnce() {
+ val listener = registerListener()
+ underTest.registerListener(listener)
+ underTest.registerListener(listener)
+
+ underTest.displayView(getState())
+
+ underTest.removeView(DEFAULT_ID, "reason")
+
+ assertThat(listener.permanentlyRemovedIds).hasSize(1)
+ assertThat(listener.permanentlyRemovedIds).containsExactly(DEFAULT_ID)
+ }
+
+ private fun registerListener(): Listener {
+ return Listener().also {
+ underTest.registerListener(it)
+ }
}
private fun getState(name: String = "name") = ViewInfo(name)
@@ -1030,9 +1166,17 @@
override val windowTitle: String = "Window Title",
override val wakeReason: String = "WAKE_REASON",
override val timeoutMs: Int = TIMEOUT_MS.toInt(),
- override val id: String = "id",
+ override val id: String = DEFAULT_ID,
override val priority: ViewPriority = ViewPriority.NORMAL,
) : TemporaryViewInfo()
+
+ inner class Listener : TemporaryViewDisplayController.Listener {
+ val permanentlyRemovedIds = mutableListOf<String>()
+ override fun onInfoPermanentlyRemoved(id: String) {
+ permanentlyRemovedIds.add(id)
+ }
+ }
}
private const val TIMEOUT_MS = 10000L
+private const val DEFAULT_ID = "defaultId"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 90178c6..45eb1f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -20,6 +20,7 @@
import android.os.VibrationEffect
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
@@ -43,6 +44,8 @@
import com.android.systemui.temporarydisplay.ViewPriority
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.view.ViewUtil
@@ -54,6 +57,7 @@
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@@ -74,6 +78,7 @@
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var viewUtil: ViewUtil
@Mock private lateinit var vibratorHelper: VibratorHelper
+ @Mock private lateinit var swipeGestureHandler: SwipeChipbarAwayGestureHandler
private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
private lateinit var fakeWakeLock: WakeLockFake
private lateinit var fakeClock: FakeSystemClock
@@ -106,6 +111,7 @@
powerManager,
falsingManager,
falsingCollector,
+ swipeGestureHandler,
viewUtil,
vibratorHelper,
fakeWakeLockBuilder,
@@ -430,17 +436,101 @@
verify(logger).logViewUpdate(eq(WINDOW_TITLE), eq("new title text"), any())
}
+ @Test
+ fun swipeToDismiss_false_neverListensForGesture() {
+ underTest.displayView(
+ createChipbarInfo(
+ Icon.Resource(R.drawable.ic_cake, contentDescription = null),
+ Text.Loaded("title text"),
+ endItem = ChipbarEndItem.Loading,
+ allowSwipeToDismiss = false,
+ )
+ )
+
+ verify(swipeGestureHandler, never()).addOnGestureDetectedCallback(any(), any())
+ }
+
+ @Test
+ fun swipeToDismiss_true_listensForGesture() {
+ underTest.displayView(
+ createChipbarInfo(
+ Icon.Resource(R.drawable.ic_cake, contentDescription = null),
+ Text.Loaded("title text"),
+ endItem = ChipbarEndItem.Loading,
+ allowSwipeToDismiss = true,
+ )
+ )
+
+ verify(swipeGestureHandler).addOnGestureDetectedCallback(any(), any())
+ }
+
+ @Test
+ fun swipeToDismiss_swipeOccurs_viewDismissed() {
+ underTest.displayView(
+ createChipbarInfo(
+ Icon.Resource(R.drawable.ic_cake, contentDescription = null),
+ Text.Loaded("title text"),
+ endItem = ChipbarEndItem.Loading,
+ allowSwipeToDismiss = true,
+ )
+ )
+ val view = getChipbarView()
+
+ val callbackCaptor = argumentCaptor<(MotionEvent) -> Unit>()
+ verify(swipeGestureHandler).addOnGestureDetectedCallback(any(), capture(callbackCaptor))
+
+ callbackCaptor.value.invoke(MotionEvent.obtain(0L, 0L, 0, 0f, 0f, 0))
+
+ verify(windowManager).removeView(view)
+ }
+
+ @Test
+ fun swipeToDismiss_viewUpdatedToFalse_swipeOccurs_viewNotDismissed() {
+ underTest.displayView(
+ createChipbarInfo(
+ Icon.Resource(R.drawable.ic_cake, contentDescription = null),
+ Text.Loaded("title text"),
+ endItem = ChipbarEndItem.Loading,
+ allowSwipeToDismiss = true,
+ )
+ )
+ val view = getChipbarView()
+ val callbackCaptor = argumentCaptor<(MotionEvent) -> Unit>()
+ verify(swipeGestureHandler).addOnGestureDetectedCallback(any(), capture(callbackCaptor))
+
+ // WHEN the view is updated to not allow swipe-to-dismiss
+ underTest.displayView(
+ createChipbarInfo(
+ Icon.Resource(R.drawable.ic_cake, contentDescription = null),
+ Text.Loaded("title text"),
+ endItem = ChipbarEndItem.Loading,
+ allowSwipeToDismiss = false,
+ )
+ )
+
+ // THEN the callback is removed
+ verify(swipeGestureHandler).removeOnGestureDetectedCallback(any())
+
+ // And WHEN the old callback is invoked
+ callbackCaptor.value.invoke(MotionEvent.obtain(0L, 0L, 0, 0f, 0f, 0))
+
+ // THEN it is ignored and view isn't removed
+ verify(windowManager, never()).removeView(view)
+ }
+
private fun createChipbarInfo(
startIcon: Icon,
text: Text,
endItem: ChipbarEndItem?,
vibrationEffect: VibrationEffect? = null,
+ allowSwipeToDismiss: Boolean = false,
): ChipbarInfo {
return ChipbarInfo(
TintedIcon(startIcon, tintAttr = null),
text,
endItem,
vibrationEffect,
+ allowSwipeToDismiss,
windowTitle = WINDOW_TITLE,
wakeReason = WAKE_REASON,
timeoutMs = TIMEOUT,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
index 4ef4e6c..ffac8f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
@@ -43,6 +43,7 @@
powerManager: PowerManager,
falsingManager: FalsingManager,
falsingCollector: FalsingCollector,
+ swipeChipbarAwayGestureHandler: SwipeChipbarAwayGestureHandler,
viewUtil: ViewUtil,
vibratorHelper: VibratorHelper,
wakeLockBuilder: WakeLock.Builder,
@@ -59,6 +60,7 @@
powerManager,
falsingManager,
falsingCollector,
+ swipeChipbarAwayGestureHandler,
viewUtil,
vibratorHelper,
wakeLockBuilder,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt
new file mode 100644
index 0000000..a87a950
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.temporarydisplay.chipbar
+
+import android.graphics.Rect
+import android.view.MotionEvent
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer
+import com.android.systemui.SysuiTestCase
+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
+import org.junit.Before
+import org.junit.Test
+
+@SmallTest
+class SwipeChipbarAwayGestureHandlerTest : SysuiTestCase() {
+
+ private lateinit var underTest: SwipeChipbarAwayGestureHandler
+
+ @Before
+ fun setUp() {
+ underTest = SwipeChipbarAwayGestureHandler(context, mock())
+ }
+
+ @Test
+ fun startOfGestureIsWithinBounds_noViewFetcher_returnsFalse() {
+ assertThat(underTest.startOfGestureIsWithinBounds(createMotionEvent())).isFalse()
+ }
+
+ @Test
+ fun startOfGestureIsWithinBounds_usesViewFetcher_aboveBottom_returnsTrue() {
+ val view = createMockView()
+
+ underTest.setViewFetcher { view }
+
+ val motionEvent = createMotionEvent(y = VIEW_BOTTOM - 100f)
+ assertThat(underTest.startOfGestureIsWithinBounds(motionEvent)).isTrue()
+ }
+
+ @Test
+ fun startOfGestureIsWithinBounds_usesViewFetcher_slightlyBelowBottom_returnsTrue() {
+ val view = createMockView()
+
+ underTest.setViewFetcher { view }
+
+ val motionEvent = createMotionEvent(y = VIEW_BOTTOM + 20f)
+ assertThat(underTest.startOfGestureIsWithinBounds(motionEvent)).isTrue()
+ }
+
+ @Test
+ fun startOfGestureIsWithinBounds_usesViewFetcher_tooFarDown_returnsFalse() {
+ val view = createMockView()
+
+ underTest.setViewFetcher { view }
+
+ val motionEvent = createMotionEvent(y = VIEW_BOTTOM * 4f)
+ assertThat(underTest.startOfGestureIsWithinBounds(motionEvent)).isFalse()
+ }
+
+ @Test
+ fun startOfGestureIsWithinBounds_viewFetcherReset_returnsFalse() {
+ val view = createMockView()
+
+ underTest.setViewFetcher { view }
+
+ val motionEvent = createMotionEvent(y = VIEW_BOTTOM - 100f)
+ assertThat(underTest.startOfGestureIsWithinBounds(motionEvent)).isTrue()
+
+ underTest.resetViewFetcher()
+ assertThat(underTest.startOfGestureIsWithinBounds(motionEvent)).isFalse()
+ }
+
+ private fun createMotionEvent(y: Float = 0f): MotionEvent {
+ return MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0f, y, 0)
+ }
+
+ private fun createMockView(): View {
+ return mock<View>().also {
+ doAnswer { invocation ->
+ val out: Rect = invocation.getArgument(0)
+ out.set(0, 0, 0, VIEW_BOTTOM)
+ null
+ }
+ .whenever(it)
+ .getBoundsOnScreen(any())
+ }
+ }
+
+ private companion object {
+ const val VIEW_BOTTOM = 455
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 2a93fff..1710709 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -172,7 +172,7 @@
verify(mDumpManager).registerDumpable(any(), any());
verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
verify(mSecureSettings).registerContentObserverForUser(
- eq(Settings.Secure.getUriFor(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES)),
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES),
eq(false), mSettingsObserver.capture(), eq(UserHandle.USER_ALL)
);
}
@@ -790,15 +790,15 @@
reset(mResources);
when(mResources.getColor(eq(android.R.color.system_accent1_500), any()))
- .thenReturn(mThemeOverlayController.mColorScheme.getAccent1().get(6));
+ .thenReturn(mThemeOverlayController.mColorScheme.getAccent1().getS500());
when(mResources.getColor(eq(android.R.color.system_accent2_500), any()))
- .thenReturn(mThemeOverlayController.mColorScheme.getAccent2().get(6));
+ .thenReturn(mThemeOverlayController.mColorScheme.getAccent2().getS500());
when(mResources.getColor(eq(android.R.color.system_accent3_500), any()))
- .thenReturn(mThemeOverlayController.mColorScheme.getAccent3().get(6));
+ .thenReturn(mThemeOverlayController.mColorScheme.getAccent3().getS500());
when(mResources.getColor(eq(android.R.color.system_neutral1_500), any()))
- .thenReturn(mThemeOverlayController.mColorScheme.getNeutral1().get(6));
+ .thenReturn(mThemeOverlayController.mColorScheme.getNeutral1().getS500());
when(mResources.getColor(eq(android.R.color.system_neutral2_500), any()))
- .thenReturn(mThemeOverlayController.mColorScheme.getNeutral2().get(6));
+ .thenReturn(mThemeOverlayController.mColorScheme.getNeutral2().getS500());
// Defers event because we already have initial colors.
verify(mThemeOverlayApplier, never())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
index 6bfc2f1..7d0d57b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
@@ -19,9 +19,12 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -35,6 +38,10 @@
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.yield
import org.junit.Test
import org.junit.runner.RunWith
@@ -231,6 +238,170 @@
}
}
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ThrottleFlowTest : SysuiTestCase() {
+
+ @Test
+ fun doesNotAffectEmissions_whenDelayAtLeastEqualToPeriod() = runTest {
+ // Arrange
+ val choreographer = createChoreographer(this)
+ val output = mutableListOf<Int>()
+ val collectJob = backgroundScope.launch {
+ flow {
+ emit(1)
+ delay(1000)
+ emit(2)
+ }.throttle(1000, choreographer.fakeClock).toList(output)
+ }
+
+ // Act
+ choreographer.advanceAndRun(0)
+
+ // Assert
+ assertThat(output).containsExactly(1)
+
+ // Act
+ choreographer.advanceAndRun(999)
+
+ // Assert
+ assertThat(output).containsExactly(1)
+
+ // Act
+ choreographer.advanceAndRun(1)
+
+ // Assert
+ assertThat(output).containsExactly(1, 2)
+
+ // Cleanup
+ collectJob.cancel()
+ }
+
+ @Test
+ fun delaysEmissions_withShorterThanPeriodDelay_untilPeriodElapses() = runTest {
+ // Arrange
+ val choreographer = createChoreographer(this)
+ val output = mutableListOf<Int>()
+ val collectJob = backgroundScope.launch {
+ flow {
+ emit(1)
+ delay(500)
+ emit(2)
+ }.throttle(1000, choreographer.fakeClock).toList(output)
+ }
+
+ // Act
+ choreographer.advanceAndRun(0)
+
+ // Assert
+ assertThat(output).containsExactly(1)
+
+ // Act
+ choreographer.advanceAndRun(500)
+ choreographer.advanceAndRun(499)
+
+ // Assert
+ assertThat(output).containsExactly(1)
+
+ // Act
+ choreographer.advanceAndRun(1)
+
+ // Assert
+ assertThat(output).containsExactly(1, 2)
+
+ // Cleanup
+ collectJob.cancel()
+ }
+
+ @Test
+ fun filtersAllButLastEmission_whenMultipleEmissionsInPeriod() = runTest {
+ // Arrange
+ val choreographer = createChoreographer(this)
+ val output = mutableListOf<Int>()
+ val collectJob = backgroundScope.launch {
+ flow {
+ emit(1)
+ delay(500)
+ emit(2)
+ delay(500)
+ emit(3)
+ }.throttle(1000, choreographer.fakeClock).toList(output)
+ }
+
+ // Act
+ choreographer.advanceAndRun(0)
+
+ // Assert
+ assertThat(output).containsExactly(1)
+
+ // Act
+ choreographer.advanceAndRun(500)
+ choreographer.advanceAndRun(499)
+
+ // Assert
+ assertThat(output).containsExactly(1)
+
+ // Act
+ choreographer.advanceAndRun(1)
+
+ // Assert
+ assertThat(output).containsExactly(1, 3)
+
+ // Cleanup
+ collectJob.cancel()
+ }
+
+ @Test
+ fun filtersAllButLastEmission_andDelaysIt_whenMultipleEmissionsInShorterThanPeriod() = runTest {
+ // Arrange
+ val choreographer = createChoreographer(this)
+ val output = mutableListOf<Int>()
+ val collectJob = backgroundScope.launch {
+ flow {
+ emit(1)
+ delay(500)
+ emit(2)
+ delay(250)
+ emit(3)
+ }.throttle(1000, choreographer.fakeClock).toList(output)
+ }
+
+ // Act
+ choreographer.advanceAndRun(0)
+
+ // Assert
+ assertThat(output).containsExactly(1)
+
+ // Act
+ choreographer.advanceAndRun(500)
+ choreographer.advanceAndRun(250)
+ choreographer.advanceAndRun(249)
+
+ // Assert
+ assertThat(output).containsExactly(1)
+
+ // Act
+ choreographer.advanceAndRun(1)
+
+ // Assert
+ assertThat(output).containsExactly(1, 3)
+
+ // Cleanup
+ collectJob.cancel()
+ }
+
+ private fun createChoreographer(testScope: TestScope) = object {
+ val fakeClock = FakeSystemClock()
+
+ fun advanceAndRun(millis: Long) {
+ fakeClock.advanceTime(millis)
+ testScope.advanceTimeBy(millis)
+ testScope.runCurrent()
+ }
+ }
+}
+
private fun <T> assertThatFlow(flow: Flow<T>) =
object {
suspend fun emitsExactly(vararg emissions: T) =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakReporterTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakReporterTest.java
index d0420f7..729168a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakReporterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakReporterTest.java
@@ -22,13 +22,17 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.app.NotificationManager;
+import android.os.UserHandle;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.UserTracker;
import org.junit.After;
import org.junit.Before;
@@ -48,6 +52,7 @@
private File mLeakDir;
private File mLeakDump;
private File mLeakHprof;
+ private UserTracker mUserTracker;
private NotificationManager mNotificationManager;
@Before
@@ -56,6 +61,9 @@
mLeakDump = new File(mLeakDir, LeakReporter.LEAK_DUMP);
mLeakHprof = new File(mLeakDir, LeakReporter.LEAK_HPROF);
+ mUserTracker = mock(UserTracker.class);
+ when(mUserTracker.getUserHandle()).thenReturn(
+ UserHandle.of(ActivityManager.getCurrentUser()));
mNotificationManager = mock(NotificationManager.class);
mContext.addMockSystemService(NotificationManager.class, mNotificationManager);
@@ -65,7 +73,7 @@
return null;
}).when(mLeakDetector).dump(any(), any());
- mLeakReporter = new LeakReporter(mContext, mLeakDetector, "test@example.com");
+ mLeakReporter = new LeakReporter(mContext, mUserTracker, mLeakDetector, "test@example.com");
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index 915ea1a..0663004 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -48,6 +48,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.RingerModeLiveData;
import com.android.systemui.util.RingerModeTracker;
@@ -101,6 +102,8 @@
@Mock
private ActivityManager mActivityManager;
@Mock
+ private UserTracker mUserTracker;
+ @Mock
private DumpManager mDumpManager;
@@ -113,6 +116,7 @@
// Initial non-set value
when(mRingerModeLiveData.getValue()).thenReturn(-1);
when(mRingerModeInternalLiveData.getValue()).thenReturn(-1);
+ when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
// Enable group volume adjustments
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions,
@@ -124,7 +128,7 @@
mBroadcastDispatcher, mRingerModeTracker, mThreadFactory, mAudioManager,
mNotificationManager, mVibrator, mIAudioService, mAccessibilityManager,
mPackageManager, mWakefullnessLifcycle, mCaptioningManager, mKeyguardManager,
- mActivityManager, mDumpManager, mCallback);
+ mActivityManager, mUserTracker, mDumpManager, mCallback);
mVolumeController.setEnableDialogs(true, true);
}
@@ -233,12 +237,13 @@
CaptioningManager captioningManager,
KeyguardManager keyguardManager,
ActivityManager activityManager,
+ UserTracker userTracker,
DumpManager dumpManager,
C callback) {
super(context, broadcastDispatcher, ringerModeTracker, theadFactory, audioManager,
notificationManager, optionalVibrator, iAudioService, accessibilityManager,
packageManager, wakefulnessLifecycle, captioningManager, keyguardManager,
- activityManager, dumpManager);
+ activityManager, userTracker, dumpManager);
mCallbacks = callback;
ArgumentCaptor<WakefulnessLifecycle.Observer> observerCaptor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
new file mode 100644
index 0000000..b527861
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
@@ -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 com.android.systemui.wallet.controller
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.service.quickaccesswallet.GetWalletCardsResponse
+import android.service.quickaccesswallet.QuickAccessWalletClient
+import android.service.quickaccesswallet.WalletCard
+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.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import java.util.ArrayList
+import kotlinx.coroutines.CoroutineScope
+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.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.isNull
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class WalletContextualSuggestionsControllerTest : SysuiTestCase() {
+
+ @Mock private lateinit var walletController: QuickAccessWalletController
+ @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock private lateinit var featureFlags: FeatureFlags
+ @Mock private lateinit var mockContext: Context
+ @Captor private lateinit var broadcastReceiver: ArgumentCaptor<BroadcastReceiver>
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ whenever(
+ broadcastDispatcher.broadcastFlow<List<String>?>(
+ any(),
+ isNull(),
+ any(),
+ any(),
+ any()
+ )
+ )
+ .thenCallRealMethod()
+
+ whenever(featureFlags.isEnabled(eq(Flags.ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS)))
+ .thenReturn(true)
+
+ whenever(CARD_1.cardId).thenReturn(ID_1)
+ whenever(CARD_2.cardId).thenReturn(ID_2)
+ whenever(CARD_3.cardId).thenReturn(ID_3)
+ }
+
+ @Test
+ fun `state - has wallet cards - received contextual cards`() = runTest {
+ setUpWalletClient(listOf(CARD_1, CARD_2))
+ val latest =
+ collectLastValue(
+ createWalletContextualSuggestionsController(backgroundScope)
+ .contextualSuggestionCards,
+ )
+
+ runCurrent()
+ verifyRegistered()
+ broadcastReceiver.value.onReceive(
+ mockContext,
+ createContextualCardsIntent(listOf(ID_1, ID_2))
+ )
+
+ assertThat(latest()).containsExactly(CARD_1, CARD_2)
+ }
+
+ @Test
+ fun `state - no wallet cards - received contextual cards`() = runTest {
+ setUpWalletClient(emptyList())
+ val latest =
+ collectLastValue(
+ createWalletContextualSuggestionsController(backgroundScope)
+ .contextualSuggestionCards,
+ )
+
+ runCurrent()
+ verifyRegistered()
+ broadcastReceiver.value.onReceive(
+ mockContext,
+ createContextualCardsIntent(listOf(ID_1, ID_2))
+ )
+
+ assertThat(latest()).isEmpty()
+ }
+
+ @Test
+ fun `state - has wallet cards - no contextual cards`() = runTest {
+ setUpWalletClient(listOf(CARD_1, CARD_2))
+ val latest =
+ collectLastValue(
+ createWalletContextualSuggestionsController(backgroundScope)
+ .contextualSuggestionCards,
+ )
+
+ runCurrent()
+ verifyRegistered()
+ broadcastReceiver.value.onReceive(mockContext, createContextualCardsIntent(emptyList()))
+
+ assertThat(latest()).isEmpty()
+ }
+
+ @Test
+ fun `state - wallet cards error`() = runTest {
+ setUpWalletClient(shouldFail = true)
+ val latest =
+ collectLastValue(
+ createWalletContextualSuggestionsController(backgroundScope)
+ .contextualSuggestionCards,
+ )
+
+ runCurrent()
+ verifyRegistered()
+ broadcastReceiver.value.onReceive(
+ mockContext,
+ createContextualCardsIntent(listOf(ID_1, ID_2))
+ )
+
+ assertThat(latest()).isEmpty()
+ }
+
+ @Test
+ fun `state - no contextual cards extra`() = runTest {
+ setUpWalletClient(listOf(CARD_1, CARD_2))
+ val latest =
+ collectLastValue(
+ createWalletContextualSuggestionsController(backgroundScope)
+ .contextualSuggestionCards,
+ )
+
+ runCurrent()
+ verifyRegistered()
+ broadcastReceiver.value.onReceive(mockContext, Intent(INTENT_NAME))
+
+ assertThat(latest()).isEmpty()
+ }
+
+ @Test
+ fun `state - has wallet cards - received contextual cards - feature disabled`() = runTest {
+ whenever(featureFlags.isEnabled(eq(Flags.ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS)))
+ .thenReturn(false)
+ setUpWalletClient(listOf(CARD_1, CARD_2))
+ val latest =
+ collectLastValue(
+ createWalletContextualSuggestionsController(backgroundScope)
+ .contextualSuggestionCards,
+ )
+
+ runCurrent()
+ verify(broadcastDispatcher, never()).broadcastFlow(any(), isNull(), any(), any())
+ assertThat(latest()).isNull()
+ }
+
+ private fun createWalletContextualSuggestionsController(
+ scope: CoroutineScope
+ ): WalletContextualSuggestionsController {
+ return WalletContextualSuggestionsController(
+ scope,
+ walletController,
+ broadcastDispatcher,
+ featureFlags
+ )
+ }
+
+ private fun verifyRegistered() {
+ verify(broadcastDispatcher)
+ .registerReceiver(capture(broadcastReceiver), any(), isNull(), isNull(), any(), any())
+ }
+
+ private fun createContextualCardsIntent(
+ ids: List<String> = emptyList(),
+ ): Intent {
+ val intent = Intent(INTENT_NAME)
+ intent.putStringArrayListExtra("cardIds", ArrayList(ids))
+ return intent
+ }
+
+ private fun setUpWalletClient(
+ cards: List<WalletCard> = emptyList(),
+ shouldFail: Boolean = false
+ ) {
+ whenever(walletController.queryWalletCards(any())).thenAnswer { invocation ->
+ with(
+ invocation.arguments[0] as QuickAccessWalletClient.OnWalletCardsRetrievedCallback
+ ) {
+ if (shouldFail) {
+ onWalletCardRetrievalError(mock())
+ } else {
+ onWalletCardsRetrieved(GetWalletCardsResponse(cards, 0))
+ }
+ }
+ }
+ }
+
+ companion object {
+ private const val ID_1: String = "123"
+ private val CARD_1: WalletCard = mock()
+ private const val ID_2: String = "456"
+ private val CARD_2: WalletCard = mock()
+ private const val ID_3: String = "789"
+ private val CARD_3: WalletCard = mock()
+ private val INTENT_NAME: String = "WalletSuggestionsIntent"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
index 0fdcb95..31cce4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
@@ -32,13 +32,13 @@
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.intThat;
+import android.app.ActivityManager;
import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.ColorSpace;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
-import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Surface;
@@ -49,6 +49,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -81,6 +82,8 @@
private Surface mSurface;
@Mock
private Context mMockContext;
+ @Mock
+ private UserTracker mUserTracker;
@Mock
private Bitmap mWallpaperBitmap;
@@ -108,13 +111,16 @@
when(mWallpaperBitmap.getConfig()).thenReturn(Bitmap.Config.ARGB_8888);
// set up wallpaper manager
- when(mWallpaperManager.getBitmapAsUser(eq(UserHandle.USER_CURRENT), anyBoolean()))
+ when(mWallpaperManager.getBitmapAsUser(eq(ActivityManager.getCurrentUser()), anyBoolean()))
.thenReturn(mWallpaperBitmap);
when(mMockContext.getSystemService(WallpaperManager.class)).thenReturn(mWallpaperManager);
// set up surface
when(mSurfaceHolder.getSurface()).thenReturn(mSurface);
doNothing().when(mSurface).hwuiDestroy();
+
+ // set up UserTracker
+ when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
}
@Test
@@ -170,7 +176,7 @@
}
private ImageWallpaper createImageWallpaper() {
- return new ImageWallpaper(mFakeBackgroundExecutor) {
+ return new ImageWallpaper(mFakeBackgroundExecutor, mUserTracker) {
@Override
public Engine onCreateEngine() {
return new CanvasEngine() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index dec8080..68ccc30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -91,6 +91,7 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.ShadeController;
@@ -361,7 +362,8 @@
mock(Handler.class),
mock(NotifPipelineFlags.class),
mock(KeyguardNotificationVisibilityProvider.class),
- mock(UiEventLogger.class)
+ mock(UiEventLogger.class),
+ mock(UserTracker.class)
);
when(mShellTaskOrganizer.getExecutor()).thenReturn(syncExecutor);
mBubbleController = new TestableBubbleController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
index e5316bc8..ceee0bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
@@ -24,6 +24,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
@@ -48,7 +49,8 @@
Handler mainHandler,
NotifPipelineFlags flags,
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ UserTracker userTracker) {
super(contentResolver,
powerManager,
dreamManager,
@@ -61,7 +63,8 @@
mainHandler,
flags,
keyguardNotificationVisibilityProvider,
- uiEventLogger);
+ uiEventLogger,
+ userTracker);
mUseHeadsUp = true;
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
index e660e1f2..4b97316 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
@@ -23,6 +23,8 @@
import android.os.UserHandle;
import android.util.Pair;
+import com.android.systemui.settings.UserTracker;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -57,6 +59,11 @@
}
@Override
+ public UserTracker getUserTracker() {
+ return null;
+ }
+
+ @Override
public void registerContentObserverForUser(Uri uri, boolean notifyDescendents,
ContentObserver settingsObserver, int userHandle) {
List<ContentObserver> observers;
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index 3fa0ab6..e6abc4c 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -391,7 +391,7 @@
private boolean takeScreenshot() {
ScreenshotHelper screenshotHelper = (mScreenshotHelperSupplier != null)
? mScreenshotHelperSupplier.get() : new ScreenshotHelper(mContext);
- screenshotHelper.takeScreenshot(WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
+ screenshotHelper.takeScreenshot(
WindowManager.ScreenshotSource.SCREENSHOT_ACCESSIBILITY_ACTIONS,
new Handler(Looper.getMainLooper()), null);
return true;
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index c4333d9..8a42933 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3813,6 +3813,13 @@
final boolean includeSharedProfile =
(flags & StorageManager.FLAG_INCLUDE_SHARED_PROFILE) != 0;
+ // When the caller is the app actually hosting external storage, we
+ // should never attempt to augment the actual storage volume state,
+ // otherwise we risk confusing it with race conditions as users go
+ // through various unlocked states
+ final boolean callerIsMediaStore = UserHandle.isSameApp(callingUid,
+ mMediaStoreAuthorityAppId);
+
// Only Apps with MANAGE_EXTERNAL_STORAGE should call the API with includeSharedProfile
if (includeSharedProfile) {
try {
@@ -3825,8 +3832,13 @@
// Checking first entry in packagesFromUid is enough as using "sharedUserId"
// mechanism is rare and discouraged. Also, Apps that share same UID share the same
// permissions.
- if (!mStorageManagerInternal.hasExternalStorageAccess(callingUid,
- packagesFromUid[0])) {
+ // Allowing Media Provider is an exception, Media Provider process should be allowed
+ // to query users across profiles, even without MANAGE_EXTERNAL_STORAGE access.
+ // Note that ordinarily Media provider process has the above permission, but if they
+ // are revoked, Storage Volume(s) should still be returned.
+ if (!callerIsMediaStore
+ && !mStorageManagerInternal.hasExternalStorageAccess(callingUid,
+ packagesFromUid[0])) {
throw new SecurityException("Only File Manager Apps permitted");
}
} catch (RemoteException re) {
@@ -3839,13 +3851,6 @@
// point
final boolean systemUserUnlocked = isSystemUnlocked(UserHandle.USER_SYSTEM);
- // When the caller is the app actually hosting external storage, we
- // should never attempt to augment the actual storage volume state,
- // otherwise we risk confusing it with race conditions as users go
- // through various unlocked states
- final boolean callerIsMediaStore = UserHandle.isSameApp(callingUid,
- mMediaStoreAuthorityAppId);
-
final boolean userIsDemo;
final boolean userKeyUnlocked;
final boolean storagePermission;
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 8624ee0..bda60ff 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -379,16 +379,11 @@
resolvedType = key.requestResolvedType;
}
- // Apply any launch flags from the ActivityOptions. This is used only by SystemUI
- // to ensure that we can launch the pending intent with a consistent launch mode even
- // if the provided PendingIntent is immutable (ie. to force an activity to launch into
- // a new task, or to launch multiple instances if supported by the app)
+ // Apply any launch flags from the ActivityOptions. This is to ensure that the caller
+ // can specify a consistent launch mode even if the PendingIntent is immutable
final ActivityOptions opts = ActivityOptions.fromBundle(options);
if (opts != null) {
- // TODO(b/254490217): Move this check into SafeActivityOptions
- if (controller.mAtmInternal.isCallerRecents(Binder.getCallingUid())) {
- finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
- }
+ finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
}
// Extract options before clearing calling identity
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
index 4fd739ca..4b0ae1b 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
@@ -16,6 +16,9 @@
package com.android.server.app;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER;
+import static android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -33,7 +36,6 @@
import android.graphics.Insets;
import android.graphics.Rect;
import android.net.Uri;
-import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.games.CreateGameSessionRequest;
@@ -50,7 +52,6 @@
import android.util.Slog;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost.SurfacePackage;
-import android.view.WindowManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -59,6 +60,7 @@
import com.android.internal.infra.ServiceConnector.ServiceLifecycleCallbacks;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ScreenshotHelper;
+import com.android.internal.util.ScreenshotRequest;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener;
@@ -861,8 +863,6 @@
Slog.w(TAG, "Could not get bitmap for id: " + taskId);
callback.complete(GameScreenshotResult.createInternalErrorResult());
} else {
- final Bundle bundle = ScreenshotHelper.HardwareBitmapBundler.hardwareBitmapToBundle(
- bitmap);
final RunningTaskInfo runningTaskInfo =
mGameTaskInfoProvider.getRunningTaskInfo(taskId);
if (runningTaskInfo == null) {
@@ -877,11 +877,17 @@
callback.complete(GameScreenshotResult.createSuccessResult());
}
};
- mScreenshotHelper.provideScreenshot(bundle, crop, Insets.NONE, taskId,
- mUserHandle.getIdentifier(), gameSessionRecord.getComponentName(),
- WindowManager.ScreenshotSource.SCREENSHOT_OTHER,
- BackgroundThread.getHandler(),
- completionConsumer);
+ ScreenshotRequest request = new ScreenshotRequest.Builder(
+ TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER)
+ .setTopComponent(gameSessionRecord.getComponentName())
+ .setTaskId(taskId)
+ .setUserId(mUserHandle.getIdentifier())
+ .setBitmap(bitmap)
+ .setBoundsOnScreen(crop)
+ .setInsets(Insets.NONE)
+ .build();
+ mScreenshotHelper.takeScreenshot(
+ request, BackgroundThread.getHandler(), completionConsumer);
}
});
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 375e51c..1f7232a 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -159,6 +159,7 @@
private Layout mCurrentLayout = null;
private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
private int mPendingDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
+ private int mDeviceStateToBeAppliedAfterBoot = DeviceStateManager.INVALID_DEVICE_STATE;
private boolean mBootCompleted = false;
private boolean mInteractive;
@@ -353,6 +354,12 @@
ipw.println("mDeviceStatesOnWhichToWakeUp=" + mDeviceStatesOnWhichToWakeUp);
ipw.println("mDeviceStatesOnWhichToSleep=" + mDeviceStatesOnWhichToSleep);
ipw.println("mInteractive=" + mInteractive);
+ ipw.println("mBootCompleted=" + mBootCompleted);
+
+ ipw.println();
+ ipw.println("mDeviceState=" + mDeviceState);
+ ipw.println("mPendingDeviceState=" + mPendingDeviceState);
+ ipw.println("mDeviceStateToBeAppliedAfterBoot=" + mDeviceStateToBeAppliedAfterBoot);
final int logicalDisplayCount = mLogicalDisplays.size();
ipw.println();
@@ -370,10 +377,6 @@
}
void setDeviceStateLocked(int state, boolean isOverrideActive) {
- Slog.i(TAG, "Requesting Transition to state: " + state + ", from state=" + mDeviceState
- + ", interactive=" + mInteractive + ", mBootCompleted=" + mBootCompleted);
- mPendingDeviceState = state;
-
if (!mBootCompleted) {
// The boot animation might still be in progress, we do not want to switch states now
// as the boot animation would end up with an incorrect size.
@@ -381,14 +384,19 @@
Slog.d(TAG, "Postponing transition to state: " + mPendingDeviceState
+ " until boot is completed");
}
+ mDeviceStateToBeAppliedAfterBoot = state;
return;
}
+ Slog.i(TAG, "Requesting Transition to state: " + state + ", from state=" + mDeviceState
+ + ", interactive=" + mInteractive + ", mBootCompleted=" + mBootCompleted);
// As part of a state transition, we may need to turn off some displays temporarily so that
// the transition is smooth. Plus, on some devices, only one internal displays can be
// on at a time. We use LogicalDisplay.setIsInTransition to mark a display that needs to be
// temporarily turned off.
resetLayoutLocked(mDeviceState, state, /* isStateChangeStarting= */ true);
+ mPendingDeviceState = state;
+ mDeviceStateToBeAppliedAfterBoot = DeviceStateManager.INVALID_DEVICE_STATE;
final boolean wakeDevice = shouldDeviceBeWoken(mPendingDeviceState, mDeviceState,
mInteractive, mBootCompleted);
final boolean sleepDevice = shouldDeviceBePutToSleep(mPendingDeviceState, mDeviceState,
@@ -435,8 +443,9 @@
void onBootCompleted() {
synchronized (mSyncRoot) {
mBootCompleted = true;
- if (mPendingDeviceState != DeviceStateManager.INVALID_DEVICE_STATE) {
- setDeviceStateLocked(mPendingDeviceState, /* isOverrideActive= */ false);
+ if (mDeviceStateToBeAppliedAfterBoot != DeviceStateManager.INVALID_DEVICE_STATE) {
+ setDeviceStateLocked(mDeviceStateToBeAppliedAfterBoot,
+ /* isOverrideActive= */ false);
}
}
}
@@ -491,7 +500,8 @@
@VisibleForTesting
boolean shouldDeviceBePutToSleep(int pendingState, int currentState, boolean isOverrideActive,
boolean isInteractive, boolean isBootCompleted) {
- return mDeviceStatesOnWhichToSleep.get(pendingState)
+ return currentState != DeviceStateManager.INVALID_DEVICE_STATE
+ && mDeviceStatesOnWhichToSleep.get(pendingState)
&& !mDeviceStatesOnWhichToSleep.get(currentState)
&& !isOverrideActive
&& isInteractive && isBootCompleted;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b55b6dd..b380d84 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -566,6 +566,9 @@
// What we do when the user double-taps on home
private int mDoubleTapOnHomeBehavior;
+ // Whether to lock the device after the next app transition has finished.
+ private boolean mLockAfterAppTransitionFinished;
+
// Allowed theater mode wake actions
private boolean mAllowTheaterModeWakeFromKey;
private boolean mAllowTheaterModeWakeFromPowerKey;
@@ -709,7 +712,7 @@
handleRingerChordGesture();
break;
case MSG_SCREENSHOT_CHORD:
- handleScreenShot(msg.arg1, msg.arg2);
+ handleScreenShot(msg.arg1);
break;
}
}
@@ -1055,11 +1058,10 @@
return;
}
- // Make sure the device locks. Unfortunately, this has the side-effect of briefly revealing
- // the lock screen before the dream appears. Note that locking is a side-effect of the no
- // dream action that is executed if we early return above.
- // TODO(b/261662912): Find a better way to lock the device that doesn't result in jank.
- lockNow(null);
+ synchronized (mLock) {
+ // Lock the device after the dream transition has finished.
+ mLockAfterAppTransitionFinished = true;
+ }
dreamManagerInternal.requestDream();
}
@@ -1500,9 +1502,9 @@
|| mShortPressOnStemPrimaryBehavior != SHORT_PRESS_PRIMARY_NOTHING;
}
- private void interceptScreenshotChord(int type, int source, long pressDelay) {
+ private void interceptScreenshotChord(int source, long pressDelay) {
mHandler.removeMessages(MSG_SCREENSHOT_CHORD);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SCREENSHOT_CHORD, type, source),
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SCREENSHOT_CHORD, source),
pressDelay);
}
@@ -1572,9 +1574,8 @@
}
};
- private void handleScreenShot(@WindowManager.ScreenshotType int type,
- @WindowManager.ScreenshotSource int source) {
- mDefaultDisplayPolicy.takeScreenshot(type, source);
+ private void handleScreenShot(@WindowManager.ScreenshotSource int source) {
+ mDefaultDisplayPolicy.takeScreenshot(TAKE_SCREENSHOT_FULLSCREEN, source);
}
@Override
@@ -2139,6 +2140,22 @@
handleStartTransitionForKeyguardLw(
keyguardGoingAway, false /* keyguardOccludingStarted */,
0 /* duration */);
+
+ synchronized (mLock) {
+ mLockAfterAppTransitionFinished = false;
+ }
+ }
+
+ @Override
+ public void onAppTransitionFinishedLocked(IBinder token) {
+ synchronized (mLock) {
+ if (!mLockAfterAppTransitionFinished) {
+ return;
+ }
+ mLockAfterAppTransitionFinished = false;
+ }
+
+ lockNow(null);
}
});
@@ -2170,7 +2187,7 @@
@Override
void execute() {
mPowerKeyHandled = true;
- interceptScreenshotChord(TAKE_SCREENSHOT_FULLSCREEN,
+ interceptScreenshotChord(
SCREENSHOT_KEY_CHORD, getScreenshotChordLongPressDelay());
}
@Override
@@ -2864,8 +2881,7 @@
break;
case KeyEvent.KEYCODE_S:
if (down && event.isMetaPressed() && event.isCtrlPressed() && repeatCount == 0) {
- interceptScreenshotChord(
- TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
+ interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
return key_consumed;
}
break;
@@ -3239,8 +3255,7 @@
break;
case KeyEvent.KEYCODE_SYSRQ:
if (down && repeatCount == 0) {
- interceptScreenshotChord(
- TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
+ interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
}
return true;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 75d84ea..9af1b2b 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2974,7 +2974,7 @@
if (density == mInitialDisplayDensity) {
density = 0;
}
- mWmService.mDisplayWindowSettings.setForcedDensity(this, density, userId);
+ mWmService.mDisplayWindowSettings.setForcedDensity(getDisplayInfo(), density, userId);
}
/** @param mode {@link #FORCE_SCALING_MODE_AUTO} or {@link #FORCE_SCALING_MODE_DISABLED}. */
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 0bb4022..f8fd2b9 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -142,6 +142,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.statusbar.LetterboxDetails;
import com.android.internal.util.ScreenshotHelper;
+import com.android.internal.util.ScreenshotRequest;
import com.android.internal.util.function.TriConsumer;
import com.android.internal.view.AppearanceRegion;
import com.android.internal.widget.PointerLocationView;
@@ -2666,8 +2667,9 @@
*/
public void takeScreenshot(int screenshotType, int source) {
if (mScreenshotHelper != null) {
- mScreenshotHelper.takeScreenshot(screenshotType,
- source, mHandler, null /* completionConsumer */);
+ ScreenshotRequest request =
+ new ScreenshotRequest.Builder(screenshotType, source).build();
+ mScreenshotHelper.takeScreenshot(request, mHandler, null /* completionConsumer */);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index c1b6496..d87532a 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -77,14 +77,14 @@
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
- void setForcedDensity(DisplayContent displayContent, int density, int userId) {
- if (displayContent.isDefaultDisplay) {
+ void setForcedDensity(DisplayInfo info, int density, int userId) {
+ if (info.displayId == Display.DEFAULT_DISPLAY) {
final String densityString = density == 0 ? "" : Integer.toString(density);
Settings.Secure.putStringForUser(mService.mContext.getContentResolver(),
Settings.Secure.DISPLAY_DENSITY_FORCED, densityString, userId);
}
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final DisplayInfo displayInfo = info;
final SettingsProvider.SettingsEntry overrideSettings =
mSettingsProvider.getOverrideSettings(displayInfo);
overrideSettings.mForcedDensity = density;
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index f916ee4..800fe09 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -197,6 +197,12 @@
// Whether using split screen aspect ratio as a default aspect ratio for unresizable apps.
private boolean mIsSplitScreenAspectRatioForUnresizableAppsEnabled;
+ // Whether using display aspect ratio as a default aspect ratio for all letterboxed apps.
+ // mIsSplitScreenAspectRatioForUnresizableAppsEnabled and
+ // config_letterboxDefaultMinAspectRatioForUnresizableApps take priority over this for
+ // unresizable apps
+ private boolean mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox;
+
// Whether letterboxing strategy is enabled for translucent activities. If {@value false}
// all the feature is disabled
private boolean mTranslucentLetterboxingEnabled;
@@ -288,6 +294,9 @@
R.dimen.config_letterboxDefaultMinAspectRatioForUnresizableApps));
mIsSplitScreenAspectRatioForUnresizableAppsEnabled = mContext.getResources().getBoolean(
R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled);
+ mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox = mContext.getResources()
+ .getBoolean(R.bool
+ .config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled);
mTranslucentLetterboxingEnabled = mContext.getResources().getBoolean(
R.bool.config_letterboxIsEnabledForTranslucentActivities);
mIsCameraCompatTreatmentEnabled = mContext.getResources().getBoolean(
@@ -943,6 +952,13 @@
}
/**
+ * Whether using display aspect ratio as a default aspect ratio for all letterboxed apps.
+ */
+ boolean getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox() {
+ return mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox;
+ }
+
+ /**
* Overrides whether using split screen aspect ratio as a default aspect ratio for unresizable
* apps.
*/
@@ -951,6 +967,14 @@
}
/**
+ * Overrides whether using display aspect ratio as a default aspect ratio for all letterboxed
+ * apps.
+ */
+ void setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(boolean enabled) {
+ mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox = enabled;
+ }
+
+ /**
* Resets whether using split screen aspect ratio as a default aspect ratio for unresizable
* apps {@link R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled}.
*/
@@ -959,6 +983,16 @@
R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled);
}
+ /**
+ * Resets whether using display aspect ratio as a default aspect ratio for all letterboxed
+ * apps {@link R.bool.config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled}.
+ */
+ void resetIsDisplayAspectRatioEnabledForFixedOrientationLetterbox() {
+ mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox = mContext.getResources()
+ .getBoolean(R.bool
+ .config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled);
+ }
+
boolean isTranslucentLetterboxingEnabled() {
return mTranslucentLetterboxingOverrideEnabled || (mTranslucentLetterboxingEnabled
&& isTranslucentLetterboxingAllowed());
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 9c43c1d..67e188f 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -584,7 +584,7 @@
? getSplitScreenAspectRatio()
: mActivityRecord.shouldCreateCompatDisplayInsets()
? getDefaultMinAspectRatioForUnresizableApps()
- : mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
+ : getDefaultMinAspectRatio();
}
private float getDefaultMinAspectRatioForUnresizableApps() {
@@ -593,7 +593,7 @@
return mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()
> MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
? mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()
- : mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
+ : getDefaultMinAspectRatio();
}
return getSplitScreenAspectRatio();
@@ -621,6 +621,16 @@
return computeAspectRatio(bounds);
}
+ private float getDefaultMinAspectRatio() {
+ final DisplayContent displayContent = mActivityRecord.getDisplayContent();
+ if (displayContent == null
+ || !mLetterboxConfiguration
+ .getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox()) {
+ return mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
+ }
+ return computeAspectRatio(new Rect(displayContent.getBounds()));
+ }
+
Resources getResources() {
return mActivityRecord.mWmService.mContext.getResources();
}
@@ -1014,6 +1024,9 @@
+ mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps());
pw.println(prefix + " isSplitScreenAspectRatioForUnresizableAppsEnabled="
+ mLetterboxConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled());
+ pw.println(prefix + " isDisplayAspectRatioEnabledForFixedOrientationLetterbox="
+ + mLetterboxConfiguration
+ .getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox());
}
/**
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index beaac05..3aa80c4 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3327,12 +3327,17 @@
// We intend to let organizer manage task visibility but it doesn't
// have enough information until we finish shell transitions.
// In the mean time we do an easy fix here.
- final boolean show = isVisible() || isAnimating(TRANSITION | PARENTS | CHILDREN);
+ final boolean visible = isVisible();
+ final boolean show = visible || isAnimating(TRANSITION | PARENTS | CHILDREN);
if (mSurfaceControl != null) {
if (show != mLastSurfaceShowing) {
t.setVisibility(mSurfaceControl, show);
}
}
+ // Only show the overlay if the task has other visible children
+ if (mOverlayHost != null) {
+ mOverlayHost.setVisibility(t, visible);
+ }
mLastSurfaceShowing = show;
}
diff --git a/services/core/java/com/android/server/wm/TrustedOverlayHost.java b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
index 975b21c..88c410b 100644
--- a/services/core/java/com/android/server/wm/TrustedOverlayHost.java
+++ b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
@@ -80,6 +80,12 @@
}
}
+ void setVisibility(SurfaceControl.Transaction t, boolean visible) {
+ if (mSurfaceControl != null) {
+ t.setVisibility(mSurfaceControl, visible);
+ }
+ }
+
void addOverlay(SurfaceControlViewHost.SurfacePackage p, SurfaceControl currentParent) {
requireOverlaySurfaceControl();
mOverlays.add(p);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4e32a7c..5065014 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -167,7 +167,6 @@
import android.app.IAssistDataReceiver;
import android.app.WindowConfiguration;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -177,7 +176,6 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.TestUtilityService;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.Bitmap;
@@ -5839,6 +5837,11 @@
if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
return displayContent.mInitialDisplayDensity;
}
+
+ DisplayInfo info = mDisplayManagerInternal.getDisplayInfo(displayId);
+ if (info != null && info.hasAccess(Binder.getCallingUid())) {
+ return info.logicalDensityDpi;
+ }
}
return -1;
}
@@ -5870,6 +5873,11 @@
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null) {
displayContent.setForcedDensity(density, targetUserId);
+ } else {
+ DisplayInfo info = mDisplayManagerInternal.getDisplayInfo(displayId);
+ if (info != null) {
+ mDisplayWindowSettings.setForcedDensity(info, density, userId);
+ }
}
}
} finally {
@@ -5894,6 +5902,12 @@
if (displayContent != null) {
displayContent.setForcedDensity(displayContent.mInitialDisplayDensity,
callingUserId);
+ } else {
+ DisplayInfo info = mDisplayManagerInternal.getDisplayInfo(displayId);
+ if (info != null) {
+ mDisplayWindowSettings.setForcedDensity(info, info.logicalDensityDpi,
+ userId);
+ }
}
}
} finally {
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index aef6d1d..a06d84c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -955,6 +955,10 @@
runSetBooleanFlag(pw, mLetterboxConfiguration
::setIsSplitScreenAspectRatioForUnresizableAppsEnabled);
break;
+ case "--isDisplayAspectRatioEnabledForFixedOrientationLetterbox":
+ runSetBooleanFlag(pw, mLetterboxConfiguration
+ ::setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox);
+ break;
case "--isTranslucentLetterboxingEnabled":
runSetBooleanFlag(pw, mLetterboxConfiguration
::setTranslucentLetterboxingOverrideEnabled);
@@ -1030,6 +1034,10 @@
mLetterboxConfiguration
.resetIsSplitScreenAspectRatioForUnresizableAppsEnabled();
break;
+ case "IsDisplayAspectRatioEnabledForFixedOrientationLetterbox":
+ mLetterboxConfiguration
+ .resetIsDisplayAspectRatioEnabledForFixedOrientationLetterbox();
+ break;
case "isTranslucentLetterboxingEnabled":
mLetterboxConfiguration.resetTranslucentLetterboxingEnabled();
break;
@@ -1140,6 +1148,7 @@
mLetterboxConfiguration.resetDefaultPositionForVerticalReachability();
mLetterboxConfiguration.resetIsEducationEnabled();
mLetterboxConfiguration.resetIsSplitScreenAspectRatioForUnresizableAppsEnabled();
+ mLetterboxConfiguration.resetIsDisplayAspectRatioEnabledForFixedOrientationLetterbox();
mLetterboxConfiguration.resetTranslucentLetterboxingEnabled();
mLetterboxConfiguration.resetCameraCompatRefreshEnabled();
mLetterboxConfiguration.resetCameraCompatRefreshCycleThroughStopEnabled();
@@ -1187,7 +1196,9 @@
pw.println("Is using split screen aspect ratio as aspect ratio for unresizable apps: "
+ mLetterboxConfiguration
.getIsSplitScreenAspectRatioForUnresizableAppsEnabled());
-
+ pw.println("Is using display aspect ratio as aspect ratio for all letterboxed apps: "
+ + mLetterboxConfiguration
+ .getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox());
pw.println(" Is activity \"refresh\" in camera compatibility treatment enabled: "
+ mLetterboxConfiguration.isCameraCompatRefreshEnabled());
pw.println(" Refresh using \"stopped -> resumed\" cycle: "
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index bd0a814..eff9e8d 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -1162,9 +1162,9 @@
private final int mUserId;
- // Conversation package name + shortcut ID -> Number of active notifications
+ // Conversation package name + shortcut ID -> Keys of active notifications
@GuardedBy("this")
- private final Map<Pair<String, String>, Integer> mActiveNotifCounts = new ArrayMap<>();
+ private final Map<Pair<String, String>, Set<String>> mActiveNotifKeys = new ArrayMap<>();
private NotificationListener(int userId) {
mUserId = userId;
@@ -1178,8 +1178,10 @@
String shortcutId = sbn.getNotification().getShortcutId();
PackageData packageData = getPackageIfConversationExists(sbn, conversationInfo -> {
synchronized (this) {
- mActiveNotifCounts.merge(
- Pair.create(sbn.getPackageName(), shortcutId), 1, Integer::sum);
+ Set<String> notificationKeys = mActiveNotifKeys.computeIfAbsent(
+ Pair.create(sbn.getPackageName(), shortcutId),
+ (unusedKey) -> new HashSet<>());
+ notificationKeys.add(sbn.getKey());
}
});
@@ -1218,12 +1220,12 @@
Pair<String, String> conversationKey =
Pair.create(sbn.getPackageName(), shortcutId);
synchronized (this) {
- int count = mActiveNotifCounts.getOrDefault(conversationKey, 0) - 1;
- if (count <= 0) {
- mActiveNotifCounts.remove(conversationKey);
+ Set<String> notificationKeys = mActiveNotifKeys.computeIfAbsent(
+ conversationKey, (unusedKey) -> new HashSet<>());
+ notificationKeys.remove(sbn.getKey());
+ if (notificationKeys.isEmpty()) {
+ mActiveNotifKeys.remove(conversationKey);
cleanupCachedShortcuts(mUserId, MAX_CACHED_RECENT_SHORTCUTS);
- } else {
- mActiveNotifCounts.put(conversationKey, count);
}
}
});
@@ -1289,7 +1291,7 @@
}
synchronized boolean hasActiveNotifications(String packageName, String shortcutId) {
- return mActiveNotifCounts.containsKey(Pair.create(packageName, shortcutId));
+ return mActiveNotifKeys.containsKey(Pair.create(packageName, shortcutId));
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index e4f9eaf..9b23f8b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -1089,8 +1089,7 @@
Consumer<Uri> consumer = invocation.getArgument(invocation.getArguments().length - 1);
consumer.accept(Uri.parse("a/b.png"));
return null;
- }).when(mMockScreenshotHelper).provideScreenshot(
- any(), any(), any(), anyInt(), anyInt(), any(), anyInt(), any(), any());
+ }).when(mMockScreenshotHelper).takeScreenshot(any(), any(), any());
mGameServiceProviderInstance.start();
startTask(taskId, GAME_A_MAIN_ACTIVITY);
mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
index c15f6a9..5792ecb 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
@@ -17,7 +17,6 @@
package com.android.server.accessibility;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_ACCESSIBILITY_ACTIONS;
-import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
@@ -301,9 +300,7 @@
mSystemActionPerformer.performSystemAction(
AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT);
verify(mMockScreenshotHelper).takeScreenshot(
- eq(TAKE_SCREENSHOT_FULLSCREEN),
- eq(SCREENSHOT_ACCESSIBILITY_ACTIONS),
- any(Handler.class), any());
+ eq(SCREENSHOT_ACCESSIBILITY_ACTIONS), any(Handler.class), any());
}
// PendingIntent is a final class and cannot be mocked. So we are using this
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 6790ad9..650eef0 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -591,6 +591,7 @@
// 2) Mark the displays as STATE_OFF so that it can continue with transition
// 3) Send DISPLAY_DEVICE_EVENT_CHANGE to inform the mapper of the new display state
// 4) Dispatch handler events.
+ mLogicalDisplayMapper.onBootCompleted();
mLogicalDisplayMapper.setDeviceStateLocked(0, false);
mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
mLooper.moveTimeForward(1000);
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 4d177ac..a27602d 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -500,6 +500,7 @@
// The cached conversations are above the limit because every conversation has active
// notifications. To uncache one of them, the notifications for that conversation need to
// be dismissed.
+ String notificationKey = "";
for (int i = 0; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) {
String shortcutId = TEST_SHORTCUT_ID + i;
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
@@ -507,11 +508,13 @@
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.addOrUpdateConversationInfo(shortcut);
when(mNotification.getShortcutId()).thenReturn(shortcutId);
- sendGenericNotification();
+ notificationKey = String.format("notification-key-%d", i);
+ sendGenericNotificationWithKey(notificationKey);
}
// Post another notification for the last conversation.
- sendGenericNotification();
+ String otherNotificationKey = "other-notification-key";
+ sendGenericNotificationWithKey(otherNotificationKey);
// Removing one of the two notifications does not un-cache the shortcut.
listenerService.onNotificationRemoved(mGenericSbn, null,
@@ -520,6 +523,7 @@
anyInt(), any(), anyString(), any(), anyInt(), anyInt());
// Removing the second notification un-caches the shortcut.
+ when(mGenericSbn.getKey()).thenReturn(notificationKey);
listenerService.onNotificationRemoved(mGenericSbn, null,
NotificationListenerService.REASON_CANCEL_ALL);
verify(mShortcutServiceInternal).uncacheShortcuts(
@@ -687,6 +691,35 @@
}
@Test
+ public void testGetConversation_trackActiveConversations() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID)).isNull();
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_PINNED);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+ assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID)).isNotNull();
+
+ sendGenericNotification();
+ sendGenericNotification();
+ ConversationChannel result = mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID);
+ assertTrue(result.hasActiveNotifications());
+
+ // Both generic notifications have the same notification key, so a single dismiss will
+ // remove both of them.
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ listenerService.onNotificationRemoved(mGenericSbn, null,
+ NotificationListenerService.REASON_CANCEL);
+ ConversationChannel resultTwo = mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID);
+ assertFalse(resultTwo.hasActiveNotifications());
+ }
+
+ @Test
public void testGetConversation_unsyncedShortcut() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
@@ -1322,7 +1355,7 @@
sendGenericNotification();
- mDataManager.getRecentConversations(USER_ID_PRIMARY);
+ mDataManager.getRecentConversations(USER_ID_PRIMARY);
verify(mShortcutServiceInternal).getShortcuts(
anyInt(), anyString(), anyLong(), anyString(), anyList(), any(), any(),
@@ -1693,6 +1726,12 @@
// "Sends" a notification to a non-customized notification channel - the notification channel
// is something generic like "messages" and the notification has a shortcut id
private void sendGenericNotification() {
+ sendGenericNotificationWithKey(GENERIC_KEY);
+ }
+
+ // "Sends" a notification to a non-customized notification channel with the specified key.
+ private void sendGenericNotificationWithKey(String key) {
+ when(mGenericSbn.getKey()).thenReturn(key);
when(mNotification.getChannelId()).thenReturn(PARENT_NOTIFICATION_CHANNEL_ID);
doAnswer(invocationOnMock -> {
NotificationListenerService.Ranking ranking = (NotificationListenerService.Ranking)
@@ -1708,7 +1747,7 @@
mParentNotificationChannel, null, null, true, 0, false, -1, false, null, null,
false, false, false, null, 0, false, 0);
return true;
- }).when(mRankingMap).getRanking(eq(GENERIC_KEY),
+ }).when(mRankingMap).getRanking(eq(key),
any(NotificationListenerService.Ranking.class));
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 98e68ca..b0639bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -2064,7 +2064,8 @@
// Update the forced size and density in settings and the unique id to simualate a display
// remap.
dc.mWmService.mDisplayWindowSettings.setForcedSize(dc, forcedWidth, forcedHeight);
- dc.mWmService.mDisplayWindowSettings.setForcedDensity(dc, forcedDensity, 0 /* userId */);
+ dc.mWmService.mDisplayWindowSettings.setForcedDensity(displayInfo, forcedDensity,
+ 0 /* userId */);
dc.mCurrentUniqueDisplayId = mDisplayInfo.uniqueId + "-test";
// Trigger display changed.
dc.onDisplayChanged();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index c398a0a..fb4f2ee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -236,8 +236,8 @@
@Test
public void testSetForcedDensity() {
- mDisplayWindowSettings.setForcedDensity(mSecondaryDisplay, 600 /* density */,
- 0 /* userId */);
+ mDisplayWindowSettings.setForcedDensity(mSecondaryDisplay.getDisplayInfo(),
+ 600 /* density */, 0 /* userId */);
mDisplayWindowSettings.applySettingsToDisplayLocked(mSecondaryDisplay);
assertEquals(600 /* density */, mSecondaryDisplay.mBaseDisplayDensity);
@@ -439,8 +439,9 @@
public void testDisplayWindowSettingsAppliedOnDisplayReady() {
// Set forced densities for two displays in DisplayWindowSettings
final DisplayContent dc = createMockSimulatedDisplay();
- mDisplayWindowSettings.setForcedDensity(mPrimaryDisplay, 123, 0 /* userId */);
- mDisplayWindowSettings.setForcedDensity(dc, 456, 0 /* userId */);
+ mDisplayWindowSettings.setForcedDensity(mPrimaryDisplay.getDisplayInfo(), 123,
+ 0 /* userId */);
+ mDisplayWindowSettings.setForcedDensity(dc.getDisplayInfo(), 456, 0 /* userId */);
// Apply settings to displays - the settings will be stored, but config will not be
// recalculated immediately.
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 995932c..e5ff91f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -1912,6 +1912,132 @@
}
@Test
+ public void testDisplayAspectRatioForResizablePortraitApps() {
+ // Set up a display in portrait and ignoring orientation request.
+ int displayWidth = 1400;
+ int displayHeight = 1600;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mWm.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(2f);
+
+ // Enable display aspect ratio to take precedence before
+ // fixedOrientationLetterboxAspectRatio
+ mWm.mLetterboxConfiguration
+ .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
+
+ // Set up resizable app in portrait
+ prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, false /* isUnresizable */);
+
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+ // Move activity to split screen which takes half of the screen.
+ mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+ organizer.mPrimary.setBounds(0, 0, displayWidth, getExpectedSplitSize(displayHeight));
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+
+ // App should launch in fixed orientation letterbox.
+ assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ // Checking that there is no size compat mode.
+ assertFitted();
+ // Check that the display aspect ratio is used by the app.
+ final float targetMinAspectRatio = 1f * displayHeight / displayWidth;
+ final float delta = 0.01f;
+ assertEquals(targetMinAspectRatio, ActivityRecord
+ .computeAspectRatio(mActivity.getBounds()), delta);
+ }
+
+ @Test
+ public void testDisplayAspectRatioForResizableLandscapeApps() {
+ // Set up a display in landscape and ignoring orientation request.
+ int displayWidth = 1600;
+ int displayHeight = 1400;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mWm.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(2f);
+
+ // Enable display aspect ratio to take precedence before
+ // fixedOrientationLetterboxAspectRatio
+ mWm.mLetterboxConfiguration
+ .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
+
+ // Set up resizable app in landscape
+ prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_LANDSCAPE, false /* isUnresizable */);
+
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+ // Move activity to split screen which takes half of the screen.
+ mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+ organizer.mPrimary.setBounds(0, 0, getExpectedSplitSize(displayWidth), displayHeight);
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+
+ // App should launch in fixed orientation letterbox.
+ assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ // Checking that there is no size compat mode.
+ assertFitted();
+ // Check that the display aspect ratio is used by the app.
+ final float targetMinAspectRatio = 1f * displayWidth / displayHeight;
+ final float delta = 0.01f;
+ assertEquals(targetMinAspectRatio, ActivityRecord
+ .computeAspectRatio(mActivity.getBounds()), delta);
+ }
+
+ @Test
+ public void testDisplayAspectRatioForUnresizableLandscapeApps() {
+ // Set up a display in portrait and ignoring orientation request.
+ int displayWidth = 1400;
+ int displayHeight = 1600;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f);
+ // Enable display aspect ratio to take precedence before
+ // fixedOrientationLetterboxAspectRatio
+ mWm.mLetterboxConfiguration
+ .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+ // App should launch in fixed orientation letterbox.
+ assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ // Checking that there is no size compat mode.
+ assertFitted();
+ // Check that the display aspect ratio is used by the app.
+ final float targetMinAspectRatio = 1f * displayHeight / displayWidth;
+ final float delta = 0.01f;
+ assertEquals(targetMinAspectRatio, ActivityRecord
+ .computeAspectRatio(mActivity.getBounds()), delta);
+ }
+
+ @Test
+ public void testDisplayAspectRatioForUnresizablePortraitApps() {
+ // Set up a display in landscape and ignoring orientation request.
+ int displayWidth = 1600;
+ int displayHeight = 1400;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f);
+ // Enable display aspect ratio to take precedence before
+ // fixedOrientationLetterboxAspectRatio
+ mWm.mLetterboxConfiguration
+ .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ // App should launch in fixed orientation letterbox.
+ assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ // Checking that there is no size compat mode.
+ assertFitted();
+ // Check that the display aspect ratio is used by the app.
+ final float targetMinAspectRatio = 1f * displayWidth / displayHeight;
+ final float delta = 0.01f;
+ assertEquals(targetMinAspectRatio, ActivityRecord
+ .computeAspectRatio(mActivity.getBounds()), delta);
+ }
+
+ @Test
public void
testDisplayIgnoreOrientationRequest_orientationLetterboxBecameSizeCompatAfterRotate() {
// Set up a display in landscape and ignoring orientation request.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index bf1d1fa..d31ae6a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -203,6 +203,7 @@
}
final int displayId = SystemServicesTestRule.sNextDisplayId++;
+ mInfo.displayId = displayId;
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
final TestDisplayContent newDisplay = createInternal(display);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 268aa3e..f8b8094 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -253,6 +253,12 @@
// device form factors.
mAtm.mWindowManager.mLetterboxConfiguration
.setIsSplitScreenAspectRatioForUnresizableAppsEnabled(false);
+ // Ensure aspect ratio for al apps isn't overridden on any device target.
+ // {@link com.android.internal.R.bool
+ // .config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled}, may be set on
+ // some device form factors.
+ mAtm.mWindowManager.mLetterboxConfiguration
+ .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(false);
checkDeviceSpecificOverridesNotApplied();
}
@@ -267,6 +273,8 @@
mAtm.mWindowManager.mLetterboxConfiguration.resetIsVerticalReachabilityEnabled();
mAtm.mWindowManager.mLetterboxConfiguration
.resetIsSplitScreenAspectRatioForUnresizableAppsEnabled();
+ mAtm.mWindowManager.mLetterboxConfiguration
+ .resetIsDisplayAspectRatioEnabledForFixedOrientationLetterbox();
}
/**