Merge "Remove action chips from the create interface."
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index f9fb0d0..92716f4 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -1094,27 +1094,41 @@
private void updateNetworkBytesLocked() {
mTotalNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes();
+ if (mTotalNetworkDownloadBytes < 0) {
+ // Legacy apps may have provided invalid negative values. Ignore invalid values.
+ mTotalNetworkDownloadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
+ }
mTotalNetworkUploadBytes = job.getEstimatedNetworkUploadBytes();
+ if (mTotalNetworkUploadBytes < 0) {
+ // Legacy apps may have provided invalid negative values. Ignore invalid values.
+ mTotalNetworkUploadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
+ }
+ // Minimum network chunk bytes has had data validation since its introduction, so no
+ // need to do validation again.
mMinimumNetworkChunkBytes = job.getMinimumNetworkChunkBytes();
if (pendingWork != null) {
for (int i = 0; i < pendingWork.size(); i++) {
- if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
- // If any component of the job has unknown usage, we don't have a
- // complete picture of what data will be used, and we have to treat the
- // entire up/download as unknown.
- long downloadBytes = pendingWork.get(i).getEstimatedNetworkDownloadBytes();
- if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
+ long downloadBytes = pendingWork.get(i).getEstimatedNetworkDownloadBytes();
+ if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN && downloadBytes > 0) {
+ // If any component of the job has unknown usage, we won't have a
+ // complete picture of what data will be used. However, we use what we are given
+ // to get us as close to the complete picture as possible.
+ if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
mTotalNetworkDownloadBytes += downloadBytes;
+ } else {
+ mTotalNetworkDownloadBytes = downloadBytes;
}
}
- if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
- // If any component of the job has unknown usage, we don't have a
- // complete picture of what data will be used, and we have to treat the
- // entire up/download as unknown.
- long uploadBytes = pendingWork.get(i).getEstimatedNetworkUploadBytes();
- if (uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
+ long uploadBytes = pendingWork.get(i).getEstimatedNetworkUploadBytes();
+ if (uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN && uploadBytes > 0) {
+ // If any component of the job has unknown usage, we won't have a
+ // complete picture of what data will be used. However, we use what we are given
+ // to get us as close to the complete picture as possible.
+ if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
mTotalNetworkUploadBytes += uploadBytes;
+ } else {
+ mTotalNetworkUploadBytes = uploadBytes;
}
}
final long chunkBytes = pendingWork.get(i).getMinimumNetworkChunkBytes();
diff --git a/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java b/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java
index 3b14be7..24727c5 100644
--- a/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java
+++ b/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java
@@ -107,7 +107,7 @@
DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
int rotation = display.getRotation();
Point size = new Point();
- display.getSize(size);
+ display.getRealSize(size);
AccessibilityNodeInfoDumper.dumpWindowToFile(info, dumpFile, rotation, size.x,
size.y);
}
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
index ab198b3..488292d 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
@@ -139,7 +139,7 @@
serializer.attribute("", "id", Integer.toString(displayId));
int rotation = display.getRotation();
Point size = new Point();
- display.getSize(size);
+ display.getRealSize(size);
for (int i = 0, n = windows.size(); i < n; ++i) {
dumpWindowRec(windows.get(i), serializer, i, size.x, size.y, rotation);
}
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
index 6fd2bf2..1bcd343e 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
@@ -767,7 +767,7 @@
if(root != null) {
Display display = getAutomatorBridge().getDefaultDisplay();
Point size = new Point();
- display.getSize(size);
+ display.getRealSize(size);
AccessibilityNodeInfoDumper.dumpWindowToFile(root,
new File(new File(Environment.getDataDirectory(), "local/tmp"), fileName),
display.getRotation(), size.x, size.y);
diff --git a/core/java/android/credentials/ui/RequestInfo.java b/core/java/android/credentials/ui/RequestInfo.java
index 59d5118..c3937b6 100644
--- a/core/java/android/credentials/ui/RequestInfo.java
+++ b/core/java/android/credentials/ui/RequestInfo.java
@@ -69,25 +69,24 @@
private final boolean mIsFirstUsage;
- // TODO: change to package name
@NonNull
- private final String mAppDisplayName;
+ private final String mAppPackageName;
/** Creates new {@code RequestInfo} for a create-credential flow. */
public static RequestInfo newCreateRequestInfo(
@NonNull IBinder token, @NonNull CreateCredentialRequest createCredentialRequest,
- boolean isFirstUsage, @NonNull String appDisplayName) {
+ boolean isFirstUsage, @NonNull String appPackageName) {
return new RequestInfo(
- token, TYPE_CREATE, isFirstUsage, appDisplayName,
+ token, TYPE_CREATE, isFirstUsage, appPackageName,
createCredentialRequest, null);
}
/** Creates new {@code RequestInfo} for a get-credential flow. */
public static RequestInfo newGetRequestInfo(
@NonNull IBinder token, @NonNull GetCredentialRequest getCredentialRequest,
- boolean isFirstUsage, @NonNull String appDisplayName) {
+ boolean isFirstUsage, @NonNull String appPackageName) {
return new RequestInfo(
- token, TYPE_GET, isFirstUsage, appDisplayName,
+ token, TYPE_GET, isFirstUsage, appPackageName,
null, getCredentialRequest);
}
@@ -116,8 +115,8 @@
/** Returns the display name of the app that made this request. */
@NonNull
- public String getAppDisplayName() {
- return mAppDisplayName;
+ public String getAppPackageName() {
+ return mAppPackageName;
}
/**
@@ -139,13 +138,13 @@
}
private RequestInfo(@NonNull IBinder token, @NonNull @RequestType String type,
- boolean isFirstUsage, @NonNull String appDisplayName,
+ boolean isFirstUsage, @NonNull String appPackageName,
@Nullable CreateCredentialRequest createCredentialRequest,
@Nullable GetCredentialRequest getCredentialRequest) {
mToken = token;
mType = type;
mIsFirstUsage = isFirstUsage;
- mAppDisplayName = appDisplayName;
+ mAppPackageName = appPackageName;
mCreateCredentialRequest = createCredentialRequest;
mGetCredentialRequest = getCredentialRequest;
}
@@ -154,7 +153,7 @@
IBinder token = in.readStrongBinder();
String type = in.readString8();
boolean isFirstUsage = in.readBoolean();
- String appDisplayName = in.readString8();
+ String appPackageName = in.readString8();
CreateCredentialRequest createCredentialRequest =
in.readTypedObject(CreateCredentialRequest.CREATOR);
GetCredentialRequest getCredentialRequest =
@@ -165,8 +164,8 @@
mType = type;
AnnotationValidations.validate(NonNull.class, null, mType);
mIsFirstUsage = isFirstUsage;
- mAppDisplayName = appDisplayName;
- AnnotationValidations.validate(NonNull.class, null, mAppDisplayName);
+ mAppPackageName = appPackageName;
+ AnnotationValidations.validate(NonNull.class, null, mAppPackageName);
mCreateCredentialRequest = createCredentialRequest;
mGetCredentialRequest = getCredentialRequest;
}
@@ -176,7 +175,7 @@
dest.writeStrongBinder(mToken);
dest.writeString8(mType);
dest.writeBoolean(mIsFirstUsage);
- dest.writeString8(mAppDisplayName);
+ dest.writeString8(mAppPackageName);
dest.writeTypedObject(mCreateCredentialRequest, flags);
dest.writeTypedObject(mGetCredentialRequest, flags);
}
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 3bf9ca0..b117a9a 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -16,7 +16,9 @@
package android.preference;
+import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.app.NotificationManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
@@ -35,6 +37,7 @@
import android.os.HandlerThread;
import android.os.Message;
import android.preference.VolumePreference.VolumeStore;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.Settings.System;
@@ -44,6 +47,7 @@
import android.widget.SeekBar.OnSeekBarChangeListener;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.os.SomeArgs;
import java.util.concurrent.TimeUnit;
@@ -115,7 +119,6 @@
private final int mMaxStreamVolume;
private boolean mAffectedByRingerMode;
private boolean mNotificationOrRing;
- private final boolean mNotifAliasRing;
private final Receiver mReceiver = new Receiver();
private Handler mHandler;
@@ -158,6 +161,7 @@
this(context, streamType, defaultUri, callback, true /* playSample */);
}
+ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
public SeekBarVolumizer(
Context context,
int streamType,
@@ -180,8 +184,6 @@
if (mNotificationOrRing) {
mRingerMode = mAudioManager.getRingerModeInternal();
}
- mNotifAliasRing = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_alias_ring_notif_stream_types);
mZenMode = mNotificationManager.getZenMode();
if (hasAudioProductStrategies()) {
@@ -288,7 +290,9 @@
* so that when user attempts to slide the notification seekbar out of vibrate the
* seekbar doesn't wrongly snap back to 0 when the streams aren't aliased
*/
- if (mNotifAliasRing || mStreamType == AudioManager.STREAM_RING
+ if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false)
+ || mStreamType == AudioManager.STREAM_RING
|| (mStreamType == AudioManager.STREAM_NOTIFICATION && mMuted)) {
mSeekBar.setProgress(0, true);
}
@@ -365,7 +369,9 @@
// set the time of stop volume
if ((mStreamType == AudioManager.STREAM_VOICE_CALL
|| mStreamType == AudioManager.STREAM_RING
- || (!mNotifAliasRing && mStreamType == AudioManager.STREAM_NOTIFICATION)
+ || (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false)
+ && mStreamType == AudioManager.STREAM_NOTIFICATION)
|| mStreamType == AudioManager.STREAM_ALARM)) {
sStopVolumeTime = java.lang.System.currentTimeMillis();
}
@@ -644,8 +650,10 @@
}
private void updateVolumeSlider(int streamType, int streamValue) {
- final boolean streamMatch = mNotifAliasRing && mNotificationOrRing
- ? isNotificationOrRing(streamType) : streamType == mStreamType;
+ final boolean streamMatch = !DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false)
+ && mNotificationOrRing ? isNotificationOrRing(streamType) :
+ streamType == mStreamType;
if (mSeekBar != null && streamMatch && streamValue != -1) {
final boolean muted = mAudioManager.isStreamMute(mStreamType)
|| streamValue == 0;
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 44997b4..0f64f6d 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -556,6 +556,11 @@
"show_stop_button_for_user_allowlisted_apps";
/**
+ * (boolean) Whether to show notification volume control slider separate from ring.
+ */
+ public static final String VOLUME_SEPARATE_NOTIFICATION = "volume_separate_notification";
+
+ /**
* (boolean) Whether the clipboard overlay is enabled.
*/
public static final String CLIPBOARD_OVERLAY_ENABLED = "clipboard_overlay_enabled";
diff --git a/core/java/com/android/internal/expresslog/Counter.java b/core/java/com/android/internal/expresslog/Counter.java
new file mode 100644
index 0000000..7571073
--- /dev/null
+++ b/core/java/com/android/internal/expresslog/Counter.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.expresslog;
+
+import android.annotation.NonNull;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+/** Counter encapsulates StatsD write API calls */
+public final class Counter {
+
+ // Not instantiable.
+ private Counter() {}
+
+ /**
+ * Increments Telemetry Express Counter metric by 1
+ * @hide
+ */
+ public static void logIncrement(@NonNull String metricId) {
+ logIncrement(metricId, 1);
+ }
+
+ /**
+ * Increments Telemetry Express Counter metric by arbitrary value
+ * @hide
+ */
+ public static void logIncrement(@NonNull String metricId, long amount) {
+ final long metricIdHash = hashString(metricId);
+ FrameworkStatsLog.write(FrameworkStatsLog.EXPRESS_EVENT_REPORTED, metricIdHash, amount);
+ }
+
+ private static native long hashString(String stringToHash);
+}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index c50abb3..cc3d906 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -34,6 +34,8 @@
"-Wno-error=deprecated-declarations",
"-Wunused",
"-Wunreachable-code",
+
+ "-DNAMESPACE_FOR_HASH_FUNCTIONS=farmhash",
],
cppflags: ["-Wno-conversion-null"],
@@ -212,6 +214,7 @@
"android_content_res_ResourceTimer.cpp",
"android_security_Scrypt.cpp",
"com_android_internal_content_om_OverlayConfig.cpp",
+ "com_android_internal_expresslog_Counter.cpp",
"com_android_internal_net_NetworkUtilsInternal.cpp",
"com_android_internal_os_ClassLoaderFactory.cpp",
"com_android_internal_os_FuseAppLoop.cpp",
@@ -247,6 +250,7 @@
"libscrypt_static",
"libstatssocket_lazy",
"libskia",
+ "libtextclassifier_hash_static",
],
shared_libs: [
@@ -329,6 +333,7 @@
header_libs: [
"bionic_libc_platform_headers",
"dnsproxyd_protocol_headers",
+ "libtextclassifier_hash_headers",
],
},
host: {
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 9da28a3..d9ca16e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -194,6 +194,7 @@
extern int register_com_android_internal_content_F2fsUtils(JNIEnv* env);
extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
extern int register_com_android_internal_content_om_OverlayConfig(JNIEnv *env);
+extern int register_com_android_internal_expresslog_Counter(JNIEnv* env);
extern int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv* env);
extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
@@ -1575,6 +1576,7 @@
REG_JNI(register_android_os_SharedMemory),
REG_JNI(register_android_os_incremental_IncrementalManager),
REG_JNI(register_com_android_internal_content_om_OverlayConfig),
+ REG_JNI(register_com_android_internal_expresslog_Counter),
REG_JNI(register_com_android_internal_net_NetworkUtilsInternal),
REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
REG_JNI(register_com_android_internal_os_LongArrayMultiStateCounter),
diff --git a/core/jni/com_android_internal_expresslog_Counter.cpp b/core/jni/com_android_internal_expresslog_Counter.cpp
new file mode 100644
index 0000000..d4a8c23
--- /dev/null
+++ b/core/jni/com_android_internal_expresslog_Counter.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <nativehelper/JNIHelp.h>
+#include <utils/hash/farmhash.h>
+
+#include "core_jni_helpers.h"
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+static jclass g_stringClass = nullptr;
+
+/**
+ * Class: com_android_internal_expresslog_Counter
+ * Method: hashString
+ * Signature: (Ljava/lang/String;)J
+ */
+static jlong hashString(JNIEnv* env, jclass /*class*/, jstring metricNameObj) {
+ ScopedUtfChars name(env, metricNameObj);
+ if (name.c_str() == nullptr) {
+ return 0;
+ }
+
+ return static_cast<jlong>(farmhash::Fingerprint64(name.c_str(), name.size()));
+}
+
+static const JNINativeMethod g_methods[] = {
+ {"hashString", "(Ljava/lang/String;)J", (void*)hashString},
+};
+
+static const char* const kCounterPathName = "com/android/internal/expresslog/Counter";
+
+namespace android {
+
+int register_com_android_internal_expresslog_Counter(JNIEnv* env) {
+ jclass stringClass = FindClassOrDie(env, "java/lang/String");
+ g_stringClass = MakeGlobalRefOrDie(env, stringClass);
+
+ return RegisterMethodsOrDie(env, kCounterPathName, g_methods, NELEM(g_methods));
+}
+
+} // namespace android
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7d20335..3d245e9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2027,10 +2027,6 @@
STREAM_MUSIC as if it's on TV platform. -->
<bool name="config_single_volume">false</bool>
- <!-- Flag indicating whether notification and ringtone volumes
- are controlled together (aliasing is true) or not. -->
- <bool name="config_alias_ring_notif_stream_types">true</bool>
-
<!-- The number of volume steps for the notification stream -->
<integer name="config_audio_notif_vol_steps">7</integer>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2a17199..f2bbbc4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -282,7 +282,6 @@
<java-symbol type="attr" name="autofillSaveCustomSubtitleMaxHeight"/>
<java-symbol type="bool" name="action_bar_embed_tabs" />
<java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" />
- <java-symbol type="bool" name="config_alias_ring_notif_stream_types" />
<java-symbol type="integer" name="config_audio_notif_vol_default" />
<java-symbol type="integer" name="config_audio_notif_vol_steps" />
<java-symbol type="integer" name="config_audio_ring_vol_default" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 753e110..7a4a93f 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -125,6 +125,7 @@
"jsr330",
"lottie",
"LowLightDreamLib",
+ "motion_tool_lib",
],
manifest: "AndroidManifest.xml",
@@ -230,6 +231,7 @@
"jsr330",
"WindowManager-Shell",
"LowLightDreamLib",
+ "motion_tool_lib",
],
libs: [
"android.test.runner",
diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
index 006b260..9add32c 100644
--- a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
@@ -18,6 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/dream_overlay_status_bar"
+ android:visibility="invisible"
android:layout_width="match_parent"
android:layout_height="@dimen/dream_overlay_status_bar_height"
android:paddingEnd="@dimen/dream_overlay_status_bar_margin"
diff --git a/packages/SystemUI/res/values-sw600dp-h900dp/dimens.xml b/packages/SystemUI/res/values-sw600dp-h900dp/dimens.xml
new file mode 100644
index 0000000..aab914f
--- /dev/null
+++ b/packages/SystemUI/res/values-sw600dp-h900dp/dimens.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- Intended for wide devices that are currently oriented with a lot of available height,
+ such as tablets. 'hxxxdp' is used instead of 'port' in order to avoid this being applied
+ to wide devices that are shorter in height, like foldables. -->
+<resources>
+ <!-- Space between status view and notification shelf -->
+ <dimen name="keyguard_status_view_bottom_margin">35dp</dimen>
+ <dimen name="keyguard_clock_top_margin">40dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
index 347cf29..d9df337 100644
--- a/packages/SystemUI/res/values-sw600dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
@@ -17,8 +17,6 @@
<resources>
<dimen name="notification_panel_margin_horizontal">48dp</dimen>
<dimen name="status_view_margin_horizontal">62dp</dimen>
- <dimen name="keyguard_clock_top_margin">40dp</dimen>
- <dimen name="keyguard_status_view_bottom_margin">40dp</dimen>
<dimen name="bouncer_user_switcher_y_trans">20dp</dimen>
<!-- qs_tiles_page_horizontal_margin should be margin / 2, otherwise full space between two
diff --git a/packages/SystemUI/res/values-sw720dp-h1000dp/dimens.xml b/packages/SystemUI/res/values-sw720dp-h1000dp/dimens.xml
new file mode 100644
index 0000000..97ead01
--- /dev/null
+++ b/packages/SystemUI/res/values-sw720dp-h1000dp/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<!-- Intended for wide devices that are currently oriented with a lot of available height,
+ such as tablets. 'hxxxdp' is used instead of 'port' in order to avoid this being applied
+ to wide devices that are shorter in height, like foldables. -->
+<resources>
+ <!-- Space between status view and notification shelf -->
+ <dimen name="keyguard_status_view_bottom_margin">70dp</dimen>
+ <dimen name="keyguard_clock_top_margin">80dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-port/dimens.xml b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
index 3d8da8a..17f82b5 100644
--- a/packages/SystemUI/res/values-sw720dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
@@ -21,8 +21,6 @@
for different hardware and product builds. -->
<resources>
<dimen name="status_view_margin_horizontal">124dp</dimen>
- <dimen name="keyguard_clock_top_margin">80dp</dimen>
- <dimen name="keyguard_status_view_bottom_margin">80dp</dimen>
<dimen name="bouncer_user_switcher_y_trans">200dp</dimen>
<dimen name="large_screen_shade_header_left_padding">24dp</dimen>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 93982cb..ce9829b 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -743,6 +743,17 @@
<integer name="complicationRestoreMs">1000</integer>
+ <!-- Duration in milliseconds of the dream in un-blur animation. -->
+ <integer name="config_dreamOverlayInBlurDurationMs">249</integer>
+ <!-- Delay in milliseconds of the dream in un-blur animation. -->
+ <integer name="config_dreamOverlayInBlurDelayMs">133</integer>
+ <!-- Duration in milliseconds of the dream in complications fade-in animation. -->
+ <integer name="config_dreamOverlayInComplicationsDurationMs">282</integer>
+ <!-- Delay in milliseconds of the dream in top complications fade-in animation. -->
+ <integer name="config_dreamOverlayInTopComplicationsDelayMs">216</integer>
+ <!-- Delay in milliseconds of the dream in bottom complications fade-in animation. -->
+ <integer name="config_dreamOverlayInBottomComplicationsDelayMs">299</integer>
+
<!-- Icons that don't show in a collapsed non-keyguard statusbar -->
<string-array name="config_collapsed_statusbar_icon_blocklist" translatable="false">
<item>@*android:string/status_bar_volume</item>
@@ -783,4 +794,8 @@
<item>@color/dream_overlay_aqi_very_unhealthy</item>
<item>@color/dream_overlay_aqi_hazardous</item>
</integer-array>
+
+ <!-- Whether the device should display hotspot UI. If true, UI will display only when tethering
+ is available. If false, UI will never show regardless of tethering availability" -->
+ <bool name="config_show_wifi_tethering">true</bool>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java b/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
index 8aa3aba..a14f971 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
@@ -291,8 +291,10 @@
}
private void endAnimations(String reason, boolean cancel) {
- Trace.beginSection("KeyButtonRipple.endAnim: reason=" + reason + " cancel=" + cancel);
- Trace.endSection();
+ if (Trace.isEnabled()) {
+ Trace.instant(Trace.TRACE_TAG_APP,
+ "KeyButtonRipple.endAnim: reason=" + reason + " cancel=" + cancel);
+ }
mVisible = false;
mTmpArray.addAll(mRunningAnimations);
int size = mTmpArray.size();
@@ -502,20 +504,23 @@
@Override
public void onAnimationStart(Animator animation) {
- Trace.beginSection("KeyButtonRipple.start." + mName);
- Trace.endSection();
+ if (Trace.isEnabled()) {
+ Trace.instant(Trace.TRACE_TAG_APP, "KeyButtonRipple.start." + mName);
+ }
}
@Override
public void onAnimationCancel(Animator animation) {
- Trace.beginSection("KeyButtonRipple.cancel." + mName);
- Trace.endSection();
+ if (Trace.isEnabled()) {
+ Trace.instant(Trace.TRACE_TAG_APP, "KeyButtonRipple.cancel." + mName);
+ }
}
@Override
public void onAnimationEnd(Animator animation) {
- Trace.beginSection("KeyButtonRipple.end." + mName);
- Trace.endSection();
+ if (Trace.isEnabled()) {
+ Trace.instant(Trace.TRACE_TAG_APP, "KeyButtonRipple.end." + mName);
+ }
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 3961438..ca780c8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -188,13 +188,10 @@
dozeFraction: Float,
foldFraction: Float,
) : ClockAnimations {
- private var foldState = AnimationState(0f)
- private var dozeState = AnimationState(0f)
+ private val dozeState = AnimationState(dozeFraction)
+ private val foldState = AnimationState(foldFraction)
init {
- dozeState = AnimationState(dozeFraction)
- foldState = AnimationState(foldFraction)
-
if (foldState.isActive) {
clocks.forEach { it.animateFoldAppear(false) }
} else {
@@ -235,7 +232,7 @@
private class AnimationState(
var fraction: Float,
) {
- var isActive: Boolean = fraction < 0.5f
+ var isActive: Boolean = fraction > 0.5f
fun update(newFraction: Float): Pair<Boolean, Boolean> {
if (newFraction == fraction) {
return Pair(isActive, false)
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 47ee71e..0e7deeb 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -269,7 +269,11 @@
}
private static void notifyBootCompleted(CoreStartable coreStartable) {
- Trace.beginSection(coreStartable.getClass().getSimpleName() + ".onBootCompleted()");
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(
+ Trace.TRACE_TAG_APP,
+ coreStartable.getClass().getSimpleName() + ".onBootCompleted()");
+ }
coreStartable.onBootCompleted();
Trace.endSection();
}
@@ -291,14 +295,18 @@
private static CoreStartable startAdditionalStartable(String clsName) {
CoreStartable startable;
if (DEBUG) Log.d(TAG, "loading: " + clsName);
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(
+ Trace.TRACE_TAG_APP, clsName + ".newInstance()");
+ }
try {
- Trace.beginSection(clsName + ".newInstance()");
startable = (CoreStartable) Class.forName(clsName).newInstance();
- Trace.endSection();
} catch (ClassNotFoundException
| IllegalAccessException
| InstantiationException ex) {
throw new RuntimeException(ex);
+ } finally {
+ Trace.endSection();
}
return startStartable(startable);
@@ -306,7 +314,10 @@
private static CoreStartable startStartable(String clsName, Provider<CoreStartable> provider) {
if (DEBUG) Log.d(TAG, "loading: " + clsName);
- Trace.beginSection("Provider<" + clsName + ">.get()");
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(
+ Trace.TRACE_TAG_APP, "Provider<" + clsName + ">.get()");
+ }
CoreStartable startable = provider.get();
Trace.endSection();
return startStartable(startable);
@@ -314,7 +325,10 @@
private static CoreStartable startStartable(CoreStartable startable) {
if (DEBUG) Log.d(TAG, "running: " + startable);
- Trace.beginSection(startable.getClass().getSimpleName() + ".start()");
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(
+ Trace.TRACE_TAG_APP, startable.getClass().getSimpleName() + ".start()");
+ }
startable.start();
Trace.endSection();
@@ -355,15 +369,22 @@
public void onConfigurationChanged(Configuration newConfig) {
if (mServicesStarted) {
ConfigurationController configController = mSysUIComponent.getConfigurationController();
- Trace.beginSection(
- configController.getClass().getSimpleName() + ".onConfigurationChanged()");
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(
+ Trace.TRACE_TAG_APP,
+ configController.getClass().getSimpleName() + ".onConfigurationChanged()");
+ }
configController.onConfigurationChanged(newConfig);
Trace.endSection();
int len = mServices.length;
for (int i = 0; i < len; i++) {
if (mServices[i] != null) {
- Trace.beginSection(
- mServices[i].getClass().getSimpleName() + ".onConfigurationChanged()");
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(
+ Trace.TRACE_TAG_APP,
+ mServices[i].getClass().getSimpleName()
+ + ".onConfigurationChanged()");
+ }
mServices[i].onConfigurationChanged(newConfig);
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 0cf3cfe..c7c7f86 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -810,17 +810,26 @@
private void updateUdfpsLocation() {
if (mUdfpsController != null) {
final FingerprintSensorPropertiesInternal udfpsProp = mUdfpsProps.get(0);
+
final Rect previousUdfpsBounds = mUdfpsBounds;
mUdfpsBounds = udfpsProp.getLocation().getRect();
mUdfpsBounds.scale(mScaleFactor);
- mUdfpsController.updateOverlayParams(udfpsProp.sensorId,
- new UdfpsOverlayParams(mUdfpsBounds, new Rect(
- 0, mCachedDisplayInfo.getNaturalHeight() / 2,
- mCachedDisplayInfo.getNaturalWidth(),
- mCachedDisplayInfo.getNaturalHeight()),
- mCachedDisplayInfo.getNaturalWidth(),
- mCachedDisplayInfo.getNaturalHeight(), mScaleFactor,
- mCachedDisplayInfo.rotation));
+
+ final Rect overlayBounds = new Rect(
+ 0, /* left */
+ mCachedDisplayInfo.getNaturalHeight() / 2, /* top */
+ mCachedDisplayInfo.getNaturalWidth(), /* right */
+ mCachedDisplayInfo.getNaturalHeight() /* botom */);
+
+ final UdfpsOverlayParams overlayParams = new UdfpsOverlayParams(
+ mUdfpsBounds,
+ overlayBounds,
+ mCachedDisplayInfo.getNaturalWidth(),
+ mCachedDisplayInfo.getNaturalHeight(),
+ mScaleFactor,
+ mCachedDisplayInfo.rotation);
+
+ mUdfpsController.updateOverlayParams(udfpsProp, overlayParams);
if (!Objects.equals(previousUdfpsBounds, mUdfpsBounds)) {
for (Callback cb : mCallbacks) {
cb.onUdfpsLocationChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index a091ed9..3631057 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -23,8 +23,6 @@
import static com.android.systemui.classifier.Classifier.LOCK_ICON;
import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -32,8 +30,11 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.SensorProperties;
import android.hardware.display.DisplayManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorProperties;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
import android.os.Handler;
@@ -51,10 +52,14 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.FaceAuthApiRequestReason;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.biometrics.dagger.BiometricsBackground;
import com.android.systemui.dagger.SysUISingleton;
@@ -79,6 +84,8 @@
import com.android.systemui.util.concurrency.Execution;
import com.android.systemui.util.time.SystemClock;
+import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
@@ -101,7 +108,7 @@
*/
@SuppressWarnings("deprecation")
@SysUISingleton
-public class UdfpsController implements DozeReceiver {
+public class UdfpsController implements DozeReceiver, Dumpable {
private static final String TAG = "UdfpsController";
private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000;
@@ -139,7 +146,7 @@
// 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.
- @VisibleForTesting int mSensorId;
+ @VisibleForTesting @NonNull FingerprintSensorPropertiesInternal mSensorProps;
@VisibleForTesting @NonNull UdfpsOverlayParams mOverlayParams = new UdfpsOverlayParams();
// TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
@Nullable private Runnable mAuthControllerUpdateUdfpsLocation;
@@ -204,6 +211,11 @@
}
};
+ @Override
+ public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("mSensorProps=(" + mSensorProps + ")");
+ }
+
public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
@Override
public void showUdfpsOverlay(long requestId, int sensorId, int reason,
@@ -259,7 +271,7 @@
}
mAcquiredReceived = true;
final UdfpsView view = mOverlay.getOverlayView();
- if (view != null) {
+ if (view != null && isOptical()) {
unconfigureDisplay(view);
}
if (acquiredGood) {
@@ -329,12 +341,13 @@
/**
* Updates the overlay parameters and reconstructs or redraws the overlay, if necessary.
*
- * @param sensorId sensor for which the overlay is getting updated.
+ * @param sensorProps sensor for which the overlay is getting updated.
* @param overlayParams See {@link UdfpsOverlayParams}.
*/
- public void updateOverlayParams(int sensorId, @NonNull UdfpsOverlayParams overlayParams) {
- if (sensorId != mSensorId) {
- mSensorId = sensorId;
+ public void updateOverlayParams(@NonNull FingerprintSensorPropertiesInternal sensorProps,
+ @NonNull UdfpsOverlayParams overlayParams) {
+ if (mSensorProps.sensorId != sensorProps.sensorId) {
+ mSensorProps = sensorProps;
Log.w(TAG, "updateUdfpsParams | sensorId has changed");
}
@@ -358,7 +371,7 @@
mAuthControllerUpdateUdfpsLocation = r;
}
- public void setUdfpsDisplayMode(UdfpsDisplayModeProvider udfpsDisplayMode) {
+ public void setUdfpsDisplayMode(@NonNull UdfpsDisplayModeProvider udfpsDisplayMode) {
mUdfpsDisplayMode = udfpsDisplayMode;
}
@@ -462,7 +475,6 @@
}
final UdfpsView udfpsView = mOverlay.getOverlayView();
- final boolean isDisplayConfigured = udfpsView.isDisplayConfigured();
boolean handled = false;
switch (event.getActionMasked()) {
case MotionEvent.ACTION_OUTSIDE:
@@ -546,15 +558,14 @@
"minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b",
minor, major, v, exceedsVelocityThreshold);
final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime;
- if (!isDisplayConfigured && !mAcquiredReceived
- && !exceedsVelocityThreshold) {
+ if (!mOnFingerDown && !mAcquiredReceived && !exceedsVelocityThreshold) {
final float scale = mOverlayParams.getScaleFactor();
float scaledMinor = minor / scale;
float scaledMajor = major / scale;
-
onFingerDown(requestId, scaledTouch.x, scaledTouch.y, scaledMinor,
scaledMajor);
+
Log.v(TAG, "onTouch | finger down: " + touchInfo);
mTouchLogTime = mSystemClock.elapsedRealtime();
handled = true;
@@ -648,7 +659,7 @@
@NonNull LatencyTracker latencyTracker,
@NonNull ActivityLaunchAnimator activityLaunchAnimator,
@NonNull Optional<AlternateUdfpsTouchProvider> alternateTouchProvider,
- @BiometricsBackground Executor biometricsExecutor,
+ @NonNull @BiometricsBackground Executor biometricsExecutor,
@NonNull BouncerInteractor bouncerInteractor) {
mContext = context;
mExecution = execution;
@@ -679,9 +690,19 @@
mLatencyTracker = latencyTracker;
mActivityLaunchAnimator = activityLaunchAnimator;
mAlternateTouchProvider = alternateTouchProvider.orElse(null);
+ mSensorProps = new FingerprintSensorPropertiesInternal(
+ -1 /* sensorId */,
+ SensorProperties.STRENGTH_CONVENIENCE,
+ 0 /* maxEnrollmentsPerUser */,
+ new ArrayList<>() /* componentInfo */,
+ FingerprintSensorProperties.TYPE_UNKNOWN,
+ false /* resetLockoutRequiresHardwareAuthToken */);
+
mBiometricExecutor = biometricsExecutor;
mBouncerInteractor = bouncerInteractor;
+ mDumpManager.registerDumpable(TAG, this);
+
mOrientationListener = new BiometricDisplayListener(
context,
displayManager,
@@ -877,6 +898,10 @@
mIsAodInterruptActive = false;
}
+ private boolean isOptical() {
+ return mSensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
+ }
+
public boolean isFingerDown() {
return mOnFingerDown;
}
@@ -893,7 +918,9 @@
+ " current: " + mOverlay.getRequestId());
return;
}
- mLatencyTracker.onActionStart(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
+ if (isOptical()) {
+ mLatencyTracker.onActionStart(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
+ }
// Refresh screen timeout and boost process priority if possible.
mPowerManager.userActivity(mSystemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
@@ -916,11 +943,11 @@
}
});
} else {
- mFingerprintManager.onPointerDown(requestId, mSensorId, x, y, minor, major);
+ mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, x, y, minor, major);
}
Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);
final UdfpsView view = mOverlay.getOverlayView();
- if (view != null) {
+ if (view != null && isOptical()) {
view.configureDisplay(() -> {
if (mAlternateTouchProvider != null) {
mBiometricExecutor.execute(() -> {
@@ -928,7 +955,7 @@
mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
});
} else {
- mFingerprintManager.onUiReady(requestId, mSensorId);
+ mFingerprintManager.onUiReady(requestId, mSensorProps.sensorId);
mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
}
});
@@ -954,15 +981,16 @@
}
});
} else {
- mFingerprintManager.onPointerUp(requestId, mSensorId);
+ mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId);
}
for (Callback cb : mCallbacks) {
cb.onFingerUp();
}
}
mOnFingerDown = false;
- unconfigureDisplay(view);
-
+ if (isOptical()) {
+ unconfigureDisplay(view);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
index 5850c95..08c7c0f 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
@@ -127,7 +127,10 @@
action,
userId,
{
- Trace.beginSection("registerReceiver act=$action user=$userId")
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(
+ Trace.TRACE_TAG_APP, "registerReceiver act=$action user=$userId")
+ }
context.registerReceiverAsUser(
this,
UserHandle.of(userId),
@@ -141,7 +144,11 @@
},
{
try {
- Trace.beginSection("unregisterReceiver act=$action user=$userId")
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(
+ Trace.TRACE_TAG_APP,
+ "unregisterReceiver act=$action user=$userId")
+ }
context.unregisterReceiver(this)
Trace.endSection()
logger.logContextReceiverUnregistered(userId, action)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 482bdaf..271a8c5ff 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -47,6 +47,7 @@
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.mediaprojection.appselector.MediaProjectionModule;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.motiontool.MotionToolModule;
import com.android.systemui.navigationbar.NavigationBarComponent;
import com.android.systemui.notetask.NoteTaskModule;
import com.android.systemui.people.PeopleModule;
@@ -133,6 +134,7 @@
FooterActionsModule.class,
LogModule.class,
MediaProjectionModule.class,
+ MotionToolModule.class,
PeopleHubModule.class,
PeopleModule.class,
PluginModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 60227ee..937884c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -171,7 +171,10 @@
@Override
public void onSensorChanged(SensorEvent event) {
- Trace.beginSection("DozeScreenBrightness.onSensorChanged" + event.values[0]);
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(
+ Trace.TRACE_TAG_APP, "DozeScreenBrightness.onSensorChanged" + event.values[0]);
+ }
try {
if (mRegistered) {
mLastSensorValue = (int) event.values[0];
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
new file mode 100644
index 0000000..d8dd6a2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
+import android.animation.ValueAnimator
+import android.view.View
+import androidx.core.animation.doOnEnd
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.dreams.complication.ComplicationHostViewController
+import com.android.systemui.dreams.complication.ComplicationLayoutParams
+import com.android.systemui.dreams.dagger.DreamOverlayModule
+import com.android.systemui.statusbar.BlurUtils
+import java.util.function.Consumer
+import javax.inject.Inject
+import javax.inject.Named
+
+/** Controller for dream overlay animations. */
+class DreamOverlayAnimationsController
+@Inject
+constructor(
+ private val mBlurUtils: BlurUtils,
+ private val mComplicationHostViewController: ComplicationHostViewController,
+ private val mStatusBarViewController: DreamOverlayStatusBarViewController,
+ private val mOverlayStateController: DreamOverlayStateController,
+ @Named(DreamOverlayModule.DREAM_IN_BLUR_ANIMATION_DURATION)
+ private val mDreamInBlurAnimDuration: Int,
+ @Named(DreamOverlayModule.DREAM_IN_BLUR_ANIMATION_DELAY) private val mDreamInBlurAnimDelay: Int,
+ @Named(DreamOverlayModule.DREAM_IN_COMPLICATIONS_ANIMATION_DURATION)
+ private val mDreamInComplicationsAnimDuration: Int,
+ @Named(DreamOverlayModule.DREAM_IN_TOP_COMPLICATIONS_ANIMATION_DELAY)
+ private val mDreamInTopComplicationsAnimDelay: Int,
+ @Named(DreamOverlayModule.DREAM_IN_BOTTOM_COMPLICATIONS_ANIMATION_DELAY)
+ private val mDreamInBottomComplicationsAnimDelay: Int
+) {
+
+ var mEntryAnimations: AnimatorSet? = null
+
+ /** Starts the dream content and dream overlay entry animations. */
+ fun startEntryAnimations(view: View) {
+ cancelRunningEntryAnimations()
+
+ mEntryAnimations = AnimatorSet()
+ mEntryAnimations?.apply {
+ playTogether(
+ buildDreamInBlurAnimator(view),
+ buildDreamInTopComplicationsAnimator(),
+ buildDreamInBottomComplicationsAnimator()
+ )
+ doOnEnd { mOverlayStateController.setEntryAnimationsFinished(true) }
+ start()
+ }
+ }
+
+ /** Cancels the dream content and dream overlay animations, if they're currently running. */
+ fun cancelRunningEntryAnimations() {
+ if (mEntryAnimations?.isRunning == true) {
+ mEntryAnimations?.cancel()
+ }
+ mEntryAnimations = null
+ }
+
+ private fun buildDreamInBlurAnimator(view: View): Animator {
+ return ValueAnimator.ofFloat(1f, 0f).apply {
+ duration = mDreamInBlurAnimDuration.toLong()
+ startDelay = mDreamInBlurAnimDelay.toLong()
+ interpolator = Interpolators.LINEAR
+ addUpdateListener { animator: ValueAnimator ->
+ mBlurUtils.applyBlur(
+ view.viewRootImpl,
+ mBlurUtils.blurRadiusOfRatio(animator.animatedValue as Float).toInt(),
+ false /*opaque*/
+ )
+ }
+ }
+ }
+
+ private fun buildDreamInTopComplicationsAnimator(): Animator {
+ return ValueAnimator.ofFloat(0f, 1f).apply {
+ duration = mDreamInComplicationsAnimDuration.toLong()
+ startDelay = mDreamInTopComplicationsAnimDelay.toLong()
+ interpolator = Interpolators.LINEAR
+ addUpdateListener { va: ValueAnimator ->
+ setTopElementsAlpha(va.animatedValue as Float)
+ }
+ }
+ }
+
+ private fun buildDreamInBottomComplicationsAnimator(): Animator {
+ return ValueAnimator.ofFloat(0f, 1f).apply {
+ duration = mDreamInComplicationsAnimDuration.toLong()
+ startDelay = mDreamInBottomComplicationsAnimDelay.toLong()
+ interpolator = Interpolators.LINEAR
+ addUpdateListener { va: ValueAnimator ->
+ setBottomElementsAlpha(va.animatedValue as Float)
+ }
+ addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animation: Animator) {
+ mComplicationHostViewController
+ .getViewsAtPosition(ComplicationLayoutParams.POSITION_BOTTOM)
+ .forEach(Consumer { v: View -> v.visibility = View.VISIBLE })
+ }
+ }
+ )
+ }
+ }
+
+ /** Sets alpha of top complications and the status bar. */
+ private fun setTopElementsAlpha(alpha: Float) {
+ mComplicationHostViewController
+ .getViewsAtPosition(ComplicationLayoutParams.POSITION_TOP)
+ .forEach(Consumer { v: View -> setAlphaAndEnsureVisible(v, alpha) })
+ mStatusBarViewController.setAlpha(alpha)
+ }
+
+ /** Sets alpha of bottom complications. */
+ private fun setBottomElementsAlpha(alpha: Float) {
+ mComplicationHostViewController
+ .getViewsAtPosition(ComplicationLayoutParams.POSITION_BOTTOM)
+ .forEach(Consumer { v: View -> setAlphaAndEnsureVisible(v, alpha) })
+ }
+
+ private fun setAlphaAndEnsureVisible(view: View, alpha: Float) {
+ if (alpha > 0 && view.visibility != View.VISIBLE) {
+ view.visibility = View.VISIBLE
+ }
+
+ view.alpha = alpha
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 733a80d..d0d0184 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -54,6 +54,8 @@
private final DreamOverlayStatusBarViewController mStatusBarViewController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final BlurUtils mBlurUtils;
+ private final DreamOverlayAnimationsController mDreamOverlayAnimationsController;
+ private final DreamOverlayStateController mStateController;
private final ComplicationHostViewController mComplicationHostViewController;
@@ -134,12 +136,16 @@
@Named(DreamOverlayModule.BURN_IN_PROTECTION_UPDATE_INTERVAL) long
burnInProtectionUpdateInterval,
@Named(DreamOverlayModule.MILLIS_UNTIL_FULL_JITTER) long millisUntilFullJitter,
- BouncerCallbackInteractor bouncerCallbackInteractor) {
+ BouncerCallbackInteractor bouncerCallbackInteractor,
+ DreamOverlayAnimationsController animationsController,
+ DreamOverlayStateController stateController) {
super(containerView);
mDreamOverlayContentView = contentView;
mStatusBarViewController = statusBarViewController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mBlurUtils = blurUtils;
+ mDreamOverlayAnimationsController = animationsController;
+ mStateController = stateController;
mComplicationHostViewController = complicationHostViewController;
mDreamOverlayMaxTranslationY = resources.getDimensionPixelSize(
@@ -172,6 +178,11 @@
bouncer.addBouncerExpansionCallback(mBouncerExpansionCallback);
}
mBouncerCallbackInteractor.addBouncerExpansionCallback(mBouncerExpansionCallback);
+
+ // Start dream entry animations. Skip animations for low light clock.
+ if (!mStateController.isLowLightActive()) {
+ mDreamOverlayAnimationsController.startEntryAnimations(mView);
+ }
}
@Override
@@ -182,6 +193,8 @@
bouncer.removeBouncerExpansionCallback(mBouncerExpansionCallback);
}
mBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback);
+
+ mDreamOverlayAnimationsController.cancelRunningEntryAnimations();
}
View getContainerView() {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index d1b7368..8542412 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -90,13 +90,15 @@
new KeyguardUpdateMonitorCallback() {
@Override
public void onShadeExpandedChanged(boolean expanded) {
- if (mLifecycleRegistry.getCurrentState() != Lifecycle.State.RESUMED
- && mLifecycleRegistry.getCurrentState() != Lifecycle.State.STARTED) {
- return;
- }
+ mExecutor.execute(() -> {
+ if (getCurrentStateLocked() != Lifecycle.State.RESUMED
+ && getCurrentStateLocked() != Lifecycle.State.STARTED) {
+ return;
+ }
- mLifecycleRegistry.setCurrentState(
- expanded ? Lifecycle.State.STARTED : Lifecycle.State.RESUMED);
+ setCurrentStateLocked(
+ expanded ? Lifecycle.State.STARTED : Lifecycle.State.RESUMED);
+ });
}
};
@@ -146,29 +148,30 @@
() -> mExecutor.execute(DreamOverlayService.this::requestExit);
mDreamOverlayComponent = dreamOverlayComponentFactory.create(viewModelStore, host);
mLifecycleRegistry = mDreamOverlayComponent.getLifecycleRegistry();
- setCurrentState(Lifecycle.State.CREATED);
- }
- private void setCurrentState(Lifecycle.State state) {
- mExecutor.execute(() -> mLifecycleRegistry.setCurrentState(state));
+ mExecutor.execute(() -> setCurrentStateLocked(Lifecycle.State.CREATED));
}
@Override
public void onDestroy() {
mKeyguardUpdateMonitor.removeCallback(mKeyguardCallback);
- setCurrentState(Lifecycle.State.DESTROYED);
- resetCurrentDreamOverlay();
+ mExecutor.execute(() -> {
+ setCurrentStateLocked(Lifecycle.State.DESTROYED);
- mDestroyed = true;
+ resetCurrentDreamOverlayLocked();
+
+ mDestroyed = true;
+ });
+
super.onDestroy();
}
@Override
public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) {
- setCurrentState(Lifecycle.State.STARTED);
-
mExecutor.execute(() -> {
+ setCurrentStateLocked(Lifecycle.State.STARTED);
+
mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_ENTER_START);
if (mDestroyed) {
@@ -181,7 +184,7 @@
// Reset the current dream overlay before starting a new one. This can happen
// when two dreams overlap (briefly, for a smoother dream transition) and both
// dreams are bound to the dream overlay service.
- resetCurrentDreamOverlay();
+ resetCurrentDreamOverlayLocked();
}
mDreamOverlayContainerViewController =
@@ -191,7 +194,7 @@
mStateController.setShouldShowComplications(shouldShowComplications());
addOverlayWindowLocked(layoutParams);
- setCurrentState(Lifecycle.State.RESUMED);
+ setCurrentStateLocked(Lifecycle.State.RESUMED);
mStateController.setOverlayActive(true);
final ComponentName dreamComponent = getDreamComponent();
mStateController.setLowLightActive(
@@ -202,6 +205,14 @@
});
}
+ private Lifecycle.State getCurrentStateLocked() {
+ return mLifecycleRegistry.getCurrentState();
+ }
+
+ private void setCurrentStateLocked(Lifecycle.State state) {
+ mLifecycleRegistry.setCurrentState(state);
+ }
+
/**
* Inserts {@link Window} to host the dream overlay into the dream's parent window. Must be
* called from the main executing thread. The window attributes closely mirror those that are
@@ -231,13 +242,13 @@
// Make extra sure the container view has been removed from its old parent (otherwise we
// risk an IllegalStateException in some cases when setting the container view as the
// window's content view and the container view hasn't been properly removed previously).
- removeContainerViewFromParent();
+ removeContainerViewFromParentLocked();
mWindow.setContentView(mDreamOverlayContainerViewController.getContainerView());
mWindowManager.addView(mWindow.getDecorView(), mWindow.getAttributes());
}
- private void removeContainerViewFromParent() {
+ private void removeContainerViewFromParentLocked() {
View containerView = mDreamOverlayContainerViewController.getContainerView();
if (containerView == null) {
return;
@@ -250,13 +261,14 @@
parentView.removeView(containerView);
}
- private void resetCurrentDreamOverlay() {
+ private void resetCurrentDreamOverlayLocked() {
if (mStarted && mWindow != null) {
mWindowManager.removeView(mWindow.getDecorView());
}
mStateController.setOverlayActive(false);
mStateController.setLowLightActive(false);
+ mStateController.setEntryAnimationsFinished(false);
mDreamOverlayContainerViewController = null;
mDreamOverlayTouchMonitor = null;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index 72feaca..e80d0be 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -51,6 +51,7 @@
public static final int STATE_DREAM_OVERLAY_ACTIVE = 1 << 0;
public static final int STATE_LOW_LIGHT_ACTIVE = 1 << 1;
+ public static final int STATE_DREAM_ENTRY_ANIMATIONS_FINISHED = 1 << 2;
private static final int OP_CLEAR_STATE = 1;
private static final int OP_SET_STATE = 2;
@@ -202,6 +203,14 @@
return containsState(STATE_LOW_LIGHT_ACTIVE);
}
+ /**
+ * Returns whether the dream content and dream overlay entry animations are finished.
+ * @return {@code true} if animations are finished, {@code false} otherwise.
+ */
+ public boolean areEntryAnimationsFinished() {
+ return containsState(STATE_DREAM_ENTRY_ANIMATIONS_FINISHED);
+ }
+
private boolean containsState(int state) {
return (mState & state) != 0;
}
@@ -218,7 +227,7 @@
}
if (existingState != mState) {
- notifyCallbacks(callback -> callback.onStateChanged());
+ notifyCallbacks(Callback::onStateChanged);
}
}
@@ -239,6 +248,15 @@
}
/**
+ * Sets whether dream content and dream overlay entry animations are finished.
+ * @param finished {@code true} if entry animations are finished, {@code false} otherwise.
+ */
+ public void setEntryAnimationsFinished(boolean finished) {
+ modifyState(finished ? OP_SET_STATE : OP_CLEAR_STATE,
+ STATE_DREAM_ENTRY_ANIMATIONS_FINISHED);
+ }
+
+ /**
* Returns the available complication types.
*/
@Complication.ComplicationType
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index bb1c430..d17fbe3 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -16,10 +16,6 @@
package com.android.systemui.dreams;
-import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
-import static android.app.StatusBarManager.WINDOW_STATE_HIDING;
-import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
-
import android.app.AlarmManager;
import android.app.StatusBarManager;
import android.content.res.Resources;
@@ -83,6 +79,9 @@
private boolean mIsAttached;
+ // Whether dream entry animations are finished.
+ private boolean mEntryAnimationsFinished = false;
+
private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
.clearCapabilities()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
@@ -109,7 +108,9 @@
new DreamOverlayStateController.Callback() {
@Override
public void onStateChanged() {
- updateLowLightState();
+ mEntryAnimationsFinished =
+ mDreamOverlayStateController.areEntryAnimationsFinished();
+ updateVisibility();
}
};
@@ -195,7 +196,6 @@
mStatusBarItemsProvider.addCallback(mStatusBarItemsProviderCallback);
mDreamOverlayStateController.addCallback(mDreamOverlayStateCallback);
- updateLowLightState();
mTouchInsetSession.addViewToTracking(mView);
}
@@ -216,6 +216,26 @@
mIsAttached = false;
}
+ /**
+ * Sets alpha of the dream overlay status bar.
+ *
+ * No-op if the dream overlay status bar should not be shown.
+ */
+ protected void setAlpha(float alpha) {
+ updateVisibility();
+
+ if (mView.getVisibility() != View.VISIBLE) {
+ return;
+ }
+
+ mView.setAlpha(alpha);
+ }
+
+ private boolean shouldShowStatusBar() {
+ return !mDreamOverlayStateController.isLowLightActive()
+ && !mStatusBarWindowStateController.windowIsShowing();
+ }
+
private void updateWifiUnavailableStatusIcon() {
final NetworkCapabilities capabilities =
mConnectivityManager.getNetworkCapabilities(
@@ -235,13 +255,12 @@
hasAlarm ? buildAlarmContentDescription(alarm) : null);
}
- private void updateLowLightState() {
- int visibility = View.VISIBLE;
- if (mDreamOverlayStateController.isLowLightActive()
- || mStatusBarWindowStateController.windowIsShowing()) {
- visibility = View.INVISIBLE;
+ private void updateVisibility() {
+ if (shouldShowStatusBar()) {
+ mView.setVisibility(View.VISIBLE);
+ } else {
+ mView.setVisibility(View.INVISIBLE);
}
- mView.setVisibility(visibility);
}
private String buildAlarmContentDescription(AlarmManager.AlarmClockInfo alarm) {
@@ -298,21 +317,11 @@
}
private void onSystemStatusBarStateChanged(@StatusBarManager.WindowVisibleState int state) {
- mMainExecutor.execute(() -> {
- if (!mIsAttached || mDreamOverlayStateController.isLowLightActive()) {
- return;
- }
+ if (!mIsAttached || !mEntryAnimationsFinished) {
+ return;
+ }
- switch (state) {
- case WINDOW_STATE_SHOWING:
- mView.setVisibility(View.INVISIBLE);
- break;
- case WINDOW_STATE_HIDING:
- case WINDOW_STATE_HIDDEN:
- mView.setVisibility(View.VISIBLE);
- break;
- }
- });
+ mMainExecutor.execute(this::updateVisibility);
}
private void onStatusBarItemsChanged(List<StatusBarItem> newItems) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
index fd6cfc0..100ccc3 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
@@ -28,6 +28,7 @@
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.lifecycle.LifecycleOwner;
+import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.util.ViewController;
import java.util.Collection;
@@ -49,20 +50,34 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final ComplicationLayoutEngine mLayoutEngine;
+ private final DreamOverlayStateController mDreamOverlayStateController;
private final LifecycleOwner mLifecycleOwner;
private final ComplicationCollectionViewModel mComplicationCollectionViewModel;
private final HashMap<ComplicationId, Complication.ViewHolder> mComplications = new HashMap<>();
+ // Whether dream entry animations are finished.
+ private boolean mEntryAnimationsFinished = false;
+
@Inject
protected ComplicationHostViewController(
@Named(SCOPED_COMPLICATIONS_LAYOUT) ConstraintLayout view,
ComplicationLayoutEngine layoutEngine,
+ DreamOverlayStateController dreamOverlayStateController,
LifecycleOwner lifecycleOwner,
@Named(SCOPED_COMPLICATIONS_MODEL) ComplicationCollectionViewModel viewModel) {
super(view);
mLayoutEngine = layoutEngine;
mLifecycleOwner = lifecycleOwner;
mComplicationCollectionViewModel = viewModel;
+ mDreamOverlayStateController = dreamOverlayStateController;
+
+ mDreamOverlayStateController.addCallback(new DreamOverlayStateController.Callback() {
+ @Override
+ public void onStateChanged() {
+ mEntryAnimationsFinished =
+ mDreamOverlayStateController.areEntryAnimationsFinished();
+ }
+ });
}
@Override
@@ -123,6 +138,11 @@
final ComplicationId id = complication.getId();
final Complication.ViewHolder viewHolder = complication.getComplication()
.createView(complication);
+ // Complications to be added before dream entry animations are finished are set
+ // to invisible and are animated in.
+ if (!mEntryAnimationsFinished) {
+ viewHolder.getView().setVisibility(View.INVISIBLE);
+ }
mComplications.put(id, viewHolder);
if (viewHolder.getView().getParent() != null) {
Log.e(TAG, "View for complication "
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 4fe1622..cb012fa 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -47,6 +47,14 @@
public static final String BURN_IN_PROTECTION_UPDATE_INTERVAL =
"burn_in_protection_update_interval";
public static final String MILLIS_UNTIL_FULL_JITTER = "millis_until_full_jitter";
+ public static final String DREAM_IN_BLUR_ANIMATION_DURATION = "dream_in_blur_anim_duration";
+ public static final String DREAM_IN_BLUR_ANIMATION_DELAY = "dream_in_blur_anim_delay";
+ public static final String DREAM_IN_COMPLICATIONS_ANIMATION_DURATION =
+ "dream_in_complications_anim_duration";
+ public static final String DREAM_IN_TOP_COMPLICATIONS_ANIMATION_DELAY =
+ "dream_in_top_complications_anim_delay";
+ public static final String DREAM_IN_BOTTOM_COMPLICATIONS_ANIMATION_DELAY =
+ "dream_in_bottom_complications_anim_delay";
/** */
@Provides
@@ -114,6 +122,51 @@
return resources.getInteger(R.integer.config_dreamOverlayMillisUntilFullJitter);
}
+ /**
+ * Duration in milliseconds of the dream in un-blur animation.
+ */
+ @Provides
+ @Named(DREAM_IN_BLUR_ANIMATION_DURATION)
+ static int providesDreamInBlurAnimationDuration(@Main Resources resources) {
+ return resources.getInteger(R.integer.config_dreamOverlayInBlurDurationMs);
+ }
+
+ /**
+ * Delay in milliseconds of the dream in un-blur animation.
+ */
+ @Provides
+ @Named(DREAM_IN_BLUR_ANIMATION_DELAY)
+ static int providesDreamInBlurAnimationDelay(@Main Resources resources) {
+ return resources.getInteger(R.integer.config_dreamOverlayInBlurDelayMs);
+ }
+
+ /**
+ * Duration in milliseconds of the dream in complications fade-in animation.
+ */
+ @Provides
+ @Named(DREAM_IN_COMPLICATIONS_ANIMATION_DURATION)
+ static int providesDreamInComplicationsAnimationDuration(@Main Resources resources) {
+ return resources.getInteger(R.integer.config_dreamOverlayInComplicationsDurationMs);
+ }
+
+ /**
+ * Delay in milliseconds of the dream in top complications fade-in animation.
+ */
+ @Provides
+ @Named(DREAM_IN_TOP_COMPLICATIONS_ANIMATION_DELAY)
+ static int providesDreamInTopComplicationsAnimationDelay(@Main Resources resources) {
+ return resources.getInteger(R.integer.config_dreamOverlayInTopComplicationsDelayMs);
+ }
+
+ /**
+ * Delay in milliseconds of the dream in bottom complications fade-in animation.
+ */
+ @Provides
+ @Named(DREAM_IN_BOTTOM_COMPLICATIONS_ANIMATION_DELAY)
+ static int providesDreamInBottomComplicationsAnimationDelay(@Main Resources resources) {
+ return resources.getInteger(R.integer.config_dreamOverlayInBottomComplicationsDelayMs);
+ }
+
@Provides
@DreamOverlayComponent.DreamOverlayScope
static LifecycleOwner providesLifecycleOwner(Lazy<LifecycleRegistry> lifecycleRegistryLazy) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index e6e0e52..bcea262 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -57,7 +57,7 @@
val INSTANT_VOICE_REPLY = UnreleasedFlag(111, teamfood = true)
// TODO(b/254512425): Tracking Bug
- val NOTIFICATION_MEMORY_MONITOR_ENABLED = UnreleasedFlag(112, teamfood = true)
+ val NOTIFICATION_MEMORY_MONITOR_ENABLED = ReleasedFlag(112)
// TODO(b/254512731): Tracking Bug
@JvmField val NOTIFICATION_DISMISSAL_FADE = UnreleasedFlag(113, teamfood = true)
@@ -89,7 +89,7 @@
* replacement of KeyguardBouncer.java.
*/
// TODO(b/254512385): Tracking Bug
- @JvmField val MODERN_BOUNCER = UnreleasedFlag(208)
+ @JvmField val MODERN_BOUNCER = ReleasedFlag(208)
/**
* Whether the user interactor and repository should use `UserSwitcherController`.
@@ -186,10 +186,18 @@
@Deprecated("Replaced by mobile and wifi specific flags.")
val NEW_STATUS_BAR_PIPELINE_FRONTEND = UnreleasedFlag(605, teamfood = false)
+ // TODO(b/256614753): Tracking Bug
val NEW_STATUS_BAR_MOBILE_ICONS = UnreleasedFlag(606)
+ // TODO(b/256614210): Tracking Bug
val NEW_STATUS_BAR_WIFI_ICON = UnreleasedFlag(607)
+ // TODO(b/256614751): Tracking Bug
+ val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND = UnreleasedFlag(608)
+
+ // TODO(b/256613548): Tracking Bug
+ val NEW_STATUS_BAR_WIFI_ICON_BACKEND = UnreleasedFlag(609)
+
// 700 - dialer/calls
// TODO(b/254512734): Tracking Bug
val ONGOING_CALL_STATUS_BAR_CHIP = ReleasedFlag(700)
@@ -354,6 +362,9 @@
// 1900 - note task
@JvmField val NOTE_TASKS = SysPropBooleanFlag(1900, "persist.sysui.debug.note_tasks")
+ // 2000 - device controls
+ @Keep val USE_APP_PANELS = UnreleasedFlag(2000, true)
+
// Pay no attention to the reflection behind the curtain.
// ========================== Curtain ==========================
// | |
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
index 546a409..450fa14 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
@@ -33,6 +33,8 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.widget.ImageView;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
@@ -61,6 +63,7 @@
private UserManager mUserManager;
private PackageManager mPackageManager;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final OnBackInvokedCallback mBackCallback = this::onBackInvoked;
@Inject
public WorkLockActivity(BroadcastDispatcher broadcastDispatcher, UserManager userManager,
@@ -95,6 +98,10 @@
if (badgedIcon != null) {
((ImageView) findViewById(R.id.icon)).setImageDrawable(badgedIcon);
}
+
+ getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ mBackCallback);
}
@VisibleForTesting
@@ -134,11 +141,16 @@
@Override
public void onDestroy() {
unregisterBroadcastReceiver();
+ getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mBackCallback);
super.onDestroy();
}
@Override
public void onBackPressed() {
+ onBackInvoked();
+ }
+
+ private void onBackInvoked() {
// Ignore back presses.
}
diff --git a/packages/SystemUI/src/com/android/systemui/motiontool/MotionToolModule.kt b/packages/SystemUI/src/com/android/systemui/motiontool/MotionToolModule.kt
new file mode 100644
index 0000000..1324d2c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/motiontool/MotionToolModule.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.motiontool
+
+import android.view.WindowManagerGlobal
+import com.android.app.motiontool.DdmHandleMotionTool
+import com.android.app.motiontool.MotionToolManager
+import com.android.app.viewcapture.ViewCapture
+import com.android.systemui.CoreStartable
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+
+@Module
+interface MotionToolModule {
+
+ companion object {
+
+ @Provides
+ fun provideDdmHandleMotionTool(motionToolManager: MotionToolManager): DdmHandleMotionTool {
+ return DdmHandleMotionTool.getInstance(motionToolManager)
+ }
+
+ @Provides
+ fun provideMotionToolManager(
+ viewCapture: ViewCapture,
+ windowManagerGlobal: WindowManagerGlobal
+ ): MotionToolManager {
+ return MotionToolManager.getInstance(viewCapture, windowManagerGlobal)
+ }
+
+ @Provides
+ fun provideWindowManagerGlobal(): WindowManagerGlobal = WindowManagerGlobal.getInstance()
+
+ @Provides fun provideViewCapture(): ViewCapture = ViewCapture.getInstance()
+ }
+
+ @Binds
+ @IntoMap
+ @ClassKey(MotionToolStartable::class)
+ fun bindMotionToolStartable(impl: MotionToolStartable): CoreStartable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/motiontool/MotionToolStartable.kt b/packages/SystemUI/src/com/android/systemui/motiontool/MotionToolStartable.kt
new file mode 100644
index 0000000..fbb9538
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/motiontool/MotionToolStartable.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.motiontool
+
+import com.android.app.motiontool.DdmHandleMotionTool
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+@SysUISingleton
+class MotionToolStartable
+@Inject
+internal constructor(private val ddmHandleMotionTool: DdmHandleMotionTool) : CoreStartable {
+
+ override fun start() {
+ ddmHandleMotionTool.register()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 73c6d50..85b259e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.shade
import android.view.View
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
index 7615301..40ed40a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.shade
import android.view.MotionEvent
diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/NoOpOverScroller.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/NoOpOverScroller.kt
index f4db3ab..8847dbd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/transition/NoOpOverScroller.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/NoOpOverScroller.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.shade.transition
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt
index a77c21a..218e897 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.shade.transition
import android.content.res.Configuration
diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeOverScroller.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeOverScroller.kt
index 22e847d..a4642e0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeOverScroller.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeOverScroller.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.shade.transition
import com.android.systemui.shade.PanelState
diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt
index 1e8208f..1054aa5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.shade.transition
import android.content.Context
diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/SplitShadeOverScroller.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/SplitShadeOverScroller.kt
index 8c57194..fde08ee 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/transition/SplitShadeOverScroller.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/SplitShadeOverScroller.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.shade.transition
import android.animation.Animator
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index 470cbcb..5dbb4f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -33,6 +33,7 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback
+import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider
import com.android.systemui.statusbar.notification.collection.render.NodeController
import com.android.systemui.statusbar.notification.dagger.IncomingHeader
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
@@ -68,6 +69,7 @@
private val mHeadsUpViewBinder: HeadsUpViewBinder,
private val mNotificationInterruptStateProvider: NotificationInterruptStateProvider,
private val mRemoteInputManager: NotificationRemoteInputManager,
+ private val mLaunchFullScreenIntentProvider: LaunchFullScreenIntentProvider,
@IncomingHeader private val mIncomingHeaderController: NodeController,
@Main private val mExecutor: DelayableExecutor,
) : Coordinator {
@@ -380,6 +382,12 @@
* Notification was just added and if it should heads up, bind the view and then show it.
*/
override fun onEntryAdded(entry: NotificationEntry) {
+ // First check whether this notification should launch a full screen intent, and
+ // launch it if needed.
+ if (mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) {
+ mLaunchFullScreenIntentProvider.launchFullScreenIntent(entry)
+ }
+
// shouldHeadsUp includes check for whether this notification should be filtered
val shouldHeadsUpEver = mNotificationInterruptStateProvider.shouldHeadsUp(entry)
mPostedEntries[entry.key] = PostedEntry(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/LaunchFullScreenIntentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/LaunchFullScreenIntentProvider.kt
new file mode 100644
index 0000000..74ff78e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/LaunchFullScreenIntentProvider.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.provider
+
+import android.util.Log
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.util.ListenerSet
+import javax.inject.Inject
+
+/**
+ * A class that enables communication of decisions to launch a notification's full screen intent.
+ */
+@SysUISingleton
+class LaunchFullScreenIntentProvider @Inject constructor() {
+ companion object {
+ private const val TAG = "LaunchFullScreenIntentProvider"
+ }
+ private val listeners = ListenerSet<Listener>()
+
+ /**
+ * Registers a listener with this provider. These listeners will be alerted whenever a full
+ * screen intent should be launched for a notification entry.
+ */
+ fun registerListener(listener: Listener) {
+ listeners.addIfAbsent(listener)
+ }
+
+ /** Removes the specified listener. */
+ fun removeListener(listener: Listener) {
+ listeners.remove(listener)
+ }
+
+ /**
+ * Sends a request to launch full screen intent for the given notification entry to all
+ * registered listeners.
+ */
+ fun launchFullScreenIntent(entry: NotificationEntry) {
+ if (listeners.isEmpty()) {
+ // This should never happen, but we should definitely know if it does because having
+ // no listeners would indicate that FSIs are getting entirely dropped on the floor.
+ Log.wtf(TAG, "no listeners found when launchFullScreenIntent requested")
+ }
+ for (listener in listeners) {
+ listener.onFullScreenIntentRequested(entry)
+ }
+ }
+
+ /** Listener interface for passing full screen intent launch decisions. */
+ fun interface Listener {
+ /**
+ * Invoked whenever a full screen intent launch is requested for the given notification
+ * entry.
+ */
+ fun onFullScreenIntentRequested(entry: NotificationEntry)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 86f6ff8..0a0ded2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -18,6 +18,7 @@
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE;
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE_NEW;
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI;
+import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI_NEW;
import android.annotation.Nullable;
import android.content.Context;
@@ -53,8 +54,9 @@
import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconsBinder;
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView;
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
+import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter;
import com.android.systemui.statusbar.pipeline.wifi.ui.view.ModernStatusBarWifiView;
-import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel;
+import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel;
import com.android.systemui.util.Assert;
import java.util.ArrayList;
@@ -84,7 +86,18 @@
/** */
void setIcon(String slot, StatusBarIcon icon);
/** */
- void setSignalIcon(String slot, WifiIconState state);
+ void setWifiIcon(String slot, WifiIconState state);
+
+ /**
+ * Sets up a wifi icon using the new data pipeline. No effect if the wifi icon has already been
+ * set up (inflated and added to the view hierarchy).
+ *
+ * This method completely replaces {@link #setWifiIcon} with the information from the new wifi
+ * data pipeline. Icons will automatically keep their state up to date, so we don't have to
+ * worry about funneling state objects through anymore.
+ */
+ void setNewWifiIcon();
+
/** */
void setMobileIcons(String slot, List<MobileIconState> states);
@@ -151,14 +164,14 @@
LinearLayout linearLayout,
StatusBarLocation location,
StatusBarPipelineFlags statusBarPipelineFlags,
- WifiViewModel wifiViewModel,
+ WifiUiAdapter wifiUiAdapter,
MobileUiAdapter mobileUiAdapter,
MobileContextProvider mobileContextProvider,
DarkIconDispatcher darkIconDispatcher) {
super(linearLayout,
location,
statusBarPipelineFlags,
- wifiViewModel,
+ wifiUiAdapter,
mobileUiAdapter,
mobileContextProvider);
mIconHPadding = mContext.getResources().getDimensionPixelSize(
@@ -218,7 +231,7 @@
@SysUISingleton
public static class Factory {
private final StatusBarPipelineFlags mStatusBarPipelineFlags;
- private final WifiViewModel mWifiViewModel;
+ private final WifiUiAdapter mWifiUiAdapter;
private final MobileContextProvider mMobileContextProvider;
private final MobileUiAdapter mMobileUiAdapter;
private final DarkIconDispatcher mDarkIconDispatcher;
@@ -226,12 +239,12 @@
@Inject
public Factory(
StatusBarPipelineFlags statusBarPipelineFlags,
- WifiViewModel wifiViewModel,
+ WifiUiAdapter wifiUiAdapter,
MobileContextProvider mobileContextProvider,
MobileUiAdapter mobileUiAdapter,
DarkIconDispatcher darkIconDispatcher) {
mStatusBarPipelineFlags = statusBarPipelineFlags;
- mWifiViewModel = wifiViewModel;
+ mWifiUiAdapter = wifiUiAdapter;
mMobileContextProvider = mobileContextProvider;
mMobileUiAdapter = mobileUiAdapter;
mDarkIconDispatcher = darkIconDispatcher;
@@ -242,7 +255,7 @@
group,
location,
mStatusBarPipelineFlags,
- mWifiViewModel,
+ mWifiUiAdapter,
mMobileUiAdapter,
mMobileContextProvider,
mDarkIconDispatcher);
@@ -260,14 +273,14 @@
ViewGroup group,
StatusBarLocation location,
StatusBarPipelineFlags statusBarPipelineFlags,
- WifiViewModel wifiViewModel,
+ WifiUiAdapter wifiUiAdapter,
MobileUiAdapter mobileUiAdapter,
MobileContextProvider mobileContextProvider
) {
super(group,
location,
statusBarPipelineFlags,
- wifiViewModel,
+ wifiUiAdapter,
mobileUiAdapter,
mobileContextProvider);
}
@@ -302,19 +315,19 @@
@SysUISingleton
public static class Factory {
private final StatusBarPipelineFlags mStatusBarPipelineFlags;
- private final WifiViewModel mWifiViewModel;
+ private final WifiUiAdapter mWifiUiAdapter;
private final MobileContextProvider mMobileContextProvider;
private final MobileUiAdapter mMobileUiAdapter;
@Inject
public Factory(
StatusBarPipelineFlags statusBarPipelineFlags,
- WifiViewModel wifiViewModel,
+ WifiUiAdapter wifiUiAdapter,
MobileUiAdapter mobileUiAdapter,
MobileContextProvider mobileContextProvider
) {
mStatusBarPipelineFlags = statusBarPipelineFlags;
- mWifiViewModel = wifiViewModel;
+ mWifiUiAdapter = wifiUiAdapter;
mMobileUiAdapter = mobileUiAdapter;
mMobileContextProvider = mobileContextProvider;
}
@@ -324,7 +337,7 @@
group,
location,
mStatusBarPipelineFlags,
- mWifiViewModel,
+ mWifiUiAdapter,
mMobileUiAdapter,
mMobileContextProvider);
}
@@ -336,10 +349,9 @@
*/
class IconManager implements DemoModeCommandReceiver {
protected final ViewGroup mGroup;
- private final StatusBarLocation mLocation;
private final StatusBarPipelineFlags mStatusBarPipelineFlags;
- private final WifiViewModel mWifiViewModel;
private final MobileContextProvider mMobileContextProvider;
+ private final LocationBasedWifiViewModel mWifiViewModel;
private final MobileIconsViewModel mMobileIconsViewModel;
protected final Context mContext;
@@ -359,26 +371,33 @@
ViewGroup group,
StatusBarLocation location,
StatusBarPipelineFlags statusBarPipelineFlags,
- WifiViewModel wifiViewModel,
+ WifiUiAdapter wifiUiAdapter,
MobileUiAdapter mobileUiAdapter,
MobileContextProvider mobileContextProvider
) {
mGroup = group;
- mLocation = location;
mStatusBarPipelineFlags = statusBarPipelineFlags;
- mWifiViewModel = wifiViewModel;
mMobileContextProvider = mobileContextProvider;
mContext = group.getContext();
mIconSize = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_icon_size);
- if (statusBarPipelineFlags.useNewMobileIcons()) {
- // This starts the flow for the new pipeline, and will notify us of changes
+ if (statusBarPipelineFlags.runNewMobileIconsBackend()) {
+ // This starts the flow for the new pipeline, and will notify us of changes if
+ // {@link StatusBarPipelineFlags#useNewMobileIcons} is also true.
mMobileIconsViewModel = mobileUiAdapter.createMobileIconsViewModel();
MobileIconsBinder.bind(mGroup, mMobileIconsViewModel);
} else {
mMobileIconsViewModel = null;
}
+
+ if (statusBarPipelineFlags.runNewWifiIconBackend()) {
+ // This starts the flow for the new pipeline, and will notify us of changes if
+ // {@link StatusBarPipelineFlags#useNewWifiIcon} is also true.
+ mWifiViewModel = wifiUiAdapter.bindGroup(mGroup, location);
+ } else {
+ mWifiViewModel = null;
+ }
}
public boolean isDemoable() {
@@ -429,6 +448,9 @@
case TYPE_WIFI:
return addWifiIcon(index, slot, holder.getWifiState());
+ case TYPE_WIFI_NEW:
+ return addNewWifiIcon(index, slot);
+
case TYPE_MOBILE:
return addMobileIcon(index, slot, holder.getMobileState());
@@ -450,16 +472,13 @@
@VisibleForTesting
protected StatusIconDisplayable addWifiIcon(int index, String slot, WifiIconState state) {
- final BaseStatusBarFrameLayout view;
if (mStatusBarPipelineFlags.useNewWifiIcon()) {
- view = onCreateModernStatusBarWifiView(slot);
- // When [ModernStatusBarWifiView] is created, it will automatically apply the
- // correct view state so we don't need to call applyWifiState.
- } else {
- StatusBarWifiView wifiView = onCreateStatusBarWifiView(slot);
- wifiView.applyWifiState(state);
- view = wifiView;
+ throw new IllegalStateException("Attempting to add a mobile icon while the new "
+ + "icons are enabled is not supported");
}
+
+ final StatusBarWifiView view = onCreateStatusBarWifiView(slot);
+ view.applyWifiState(state);
mGroup.addView(view, index, onCreateLayoutParams());
if (mIsInDemoMode) {
@@ -468,6 +487,17 @@
return view;
}
+ protected StatusIconDisplayable addNewWifiIcon(int index, String slot) {
+ if (!mStatusBarPipelineFlags.useNewWifiIcon()) {
+ throw new IllegalStateException("Attempting to add a wifi icon using the new"
+ + "pipeline, but the enabled flag is false.");
+ }
+
+ ModernStatusBarWifiView view = onCreateModernStatusBarWifiView(slot);
+ mGroup.addView(view, index, onCreateLayoutParams());
+ return view;
+ }
+
@VisibleForTesting
protected StatusIconDisplayable addMobileIcon(
int index,
@@ -523,8 +553,7 @@
}
private ModernStatusBarWifiView onCreateModernStatusBarWifiView(String slot) {
- return ModernStatusBarWifiView.constructAndBind(
- mContext, slot, mWifiViewModel, mLocation);
+ return ModernStatusBarWifiView.constructAndBind(mContext, slot, mWifiViewModel);
}
private StatusBarMobileView onCreateStatusBarMobileView(int subId, String slot) {
@@ -600,7 +629,8 @@
onSetMobileIcon(viewIndex, holder.getMobileState());
return;
case TYPE_MOBILE_NEW:
- // Nothing, the icon updates itself now
+ case TYPE_WIFI_NEW:
+ // Nothing, the new icons update themselves
return;
default:
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 31e960a..674e574 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -195,12 +195,13 @@
}
}
- /**
- * Signal icons need to be handled differently, because they can be
- * composite views
- */
@Override
- public void setSignalIcon(String slot, WifiIconState state) {
+ public void setWifiIcon(String slot, WifiIconState state) {
+ if (mStatusBarPipelineFlags.useNewWifiIcon()) {
+ Log.d(TAG, "ignoring old pipeline callback because the new wifi icon is enabled");
+ return;
+ }
+
if (state == null) {
removeIcon(slot, 0);
return;
@@ -216,6 +217,24 @@
}
}
+
+ @Override
+ public void setNewWifiIcon() {
+ if (!mStatusBarPipelineFlags.useNewWifiIcon()) {
+ Log.d(TAG, "ignoring new pipeline callback because the new wifi icon is disabled");
+ return;
+ }
+
+ String slot = mContext.getString(com.android.internal.R.string.status_bar_wifi);
+ StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, /* tag= */ 0);
+ if (holder == null) {
+ holder = StatusBarIconHolder.forNewWifiIcon();
+ setIcon(slot, holder);
+ } else {
+ // Don't have to do anything in the new world
+ }
+ }
+
/**
* Accept a list of MobileIconStates, which all live in the same slot(?!), and then are sorted
* by subId. Don't worry this definitely makes sense and works.
@@ -225,7 +244,7 @@
@Override
public void setMobileIcons(String slot, List<MobileIconState> iconStates) {
if (mStatusBarPipelineFlags.useNewMobileIcons()) {
- Log.d(TAG, "ignoring old pipeline callbacks, because the new "
+ Log.d(TAG, "ignoring old pipeline callbacks, because the new mobile "
+ "icons are enabled");
return;
}
@@ -251,10 +270,11 @@
public void setNewMobileIconSubIds(List<Integer> subIds) {
if (!mStatusBarPipelineFlags.useNewMobileIcons()) {
Log.d(TAG, "ignoring new pipeline callback, "
- + "since the new icons are disabled");
+ + "since the new mobile icons are disabled");
return;
}
- Slot mobileSlot = mStatusBarIconList.getSlot("mobile");
+ String slotName = mContext.getString(com.android.internal.R.string.status_bar_mobile);
+ Slot mobileSlot = mStatusBarIconList.getSlot(slotName);
Collections.reverse(subIds);
@@ -262,7 +282,7 @@
StatusBarIconHolder holder = mobileSlot.getHolderForTag(subId);
if (holder == null) {
holder = StatusBarIconHolder.fromSubIdForModernMobileIcon(subId);
- setIcon("mobile", holder);
+ setIcon(slotName, holder);
} else {
// Don't have to do anything in the new world
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
index 68a203e..f6c0da8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
@@ -51,11 +51,24 @@
@Deprecated
public static final int TYPE_MOBILE_NEW = 3;
+ /**
+ * TODO (b/238425913): address this once the new pipeline is in place
+ * This type exists so that the new wifi pipeline can be used to inform the old view system
+ * about the existence of the wifi icon. The design of the new pipeline should allow for removal
+ * of this icon holder type, and obsolete the need for this entire class.
+ *
+ * @deprecated This field only exists so the new status bar pipeline can interface with the
+ * view holder system.
+ */
+ @Deprecated
+ public static final int TYPE_WIFI_NEW = 4;
+
@IntDef({
TYPE_ICON,
TYPE_WIFI,
TYPE_MOBILE,
- TYPE_MOBILE_NEW
+ TYPE_MOBILE_NEW,
+ TYPE_WIFI_NEW
})
@Retention(RetentionPolicy.SOURCE)
@interface IconType {}
@@ -95,6 +108,13 @@
return holder;
}
+ /** Creates a new holder with for the new wifi icon. */
+ public static StatusBarIconHolder forNewWifiIcon() {
+ StatusBarIconHolder holder = new StatusBarIconHolder();
+ holder.mType = TYPE_WIFI_NEW;
+ return holder;
+ }
+
/** */
public static StatusBarIconHolder fromMobileIconState(MobileIconState state) {
StatusBarIconHolder holder = new StatusBarIconHolder();
@@ -172,9 +192,10 @@
case TYPE_MOBILE:
return mMobileState.visible;
case TYPE_MOBILE_NEW:
- //TODO (b/249790733), the new pipeline can control visibility via the ViewModel
+ case TYPE_WIFI_NEW:
+ // The new pipeline controls visibilities via the view model and view binder, so
+ // this is effectively an unused return value.
return true;
-
default:
return true;
}
@@ -199,7 +220,9 @@
break;
case TYPE_MOBILE_NEW:
- //TODO (b/249790733), the new pipeline can control visibility via the ViewModel
+ case TYPE_WIFI_NEW:
+ // The new pipeline controls visibilities via the view model and view binder, so
+ // ignore setVisible.
break;
}
}
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 5cd2ba1..b6ae4a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -23,7 +23,6 @@
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.Notification;
-import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.content.Context;
@@ -60,9 +59,8 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -126,7 +124,6 @@
Context context,
Handler mainThreadHandler,
Executor uiBgExecutor,
- NotifPipeline notifPipeline,
NotificationVisibilityProvider visibilityProvider,
HeadsUpManagerPhone headsUpManager,
ActivityStarter activityStarter,
@@ -151,7 +148,8 @@
NotificationPresenter presenter,
NotificationPanelViewController panel,
ActivityLaunchAnimator activityLaunchAnimator,
- NotificationLaunchAnimatorControllerProvider notificationAnimationProvider) {
+ NotificationLaunchAnimatorControllerProvider notificationAnimationProvider,
+ LaunchFullScreenIntentProvider launchFullScreenIntentProvider) {
mContext = context;
mMainThreadHandler = mainThreadHandler;
mUiBgExecutor = uiBgExecutor;
@@ -182,12 +180,7 @@
mActivityLaunchAnimator = activityLaunchAnimator;
mNotificationAnimationProvider = notificationAnimationProvider;
- notifPipeline.addCollectionListener(new NotifCollectionListener() {
- @Override
- public void onEntryAdded(NotificationEntry entry) {
- handleFullScreenIntent(entry);
- }
- });
+ launchFullScreenIntentProvider.registerListener(entry -> launchFullScreenIntent(entry));
}
/**
@@ -549,38 +542,36 @@
}
@VisibleForTesting
- void handleFullScreenIntent(NotificationEntry entry) {
- if (mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) {
- if (shouldSuppressFullScreenIntent(entry)) {
- mLogger.logFullScreenIntentSuppressedByDnD(entry);
- } else if (entry.getImportance() < NotificationManager.IMPORTANCE_HIGH) {
- mLogger.logFullScreenIntentNotImportantEnough(entry);
- } else {
- // Stop screensaver if the notification has a fullscreen intent.
- // (like an incoming phone call)
- mUiBgExecutor.execute(() -> {
- try {
- mDreamManager.awaken();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- });
+ void launchFullScreenIntent(NotificationEntry entry) {
+ // Skip if device is in VR mode.
+ if (mPresenter.isDeviceInVrMode()) {
+ mLogger.logFullScreenIntentSuppressedByVR(entry);
+ return;
+ }
- // not immersive & a fullscreen alert should be shown
- final PendingIntent fullscreenIntent =
- entry.getSbn().getNotification().fullScreenIntent;
- mLogger.logSendingFullScreenIntent(entry, fullscreenIntent);
- try {
- EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
- entry.getKey());
- mCentralSurfaces.wakeUpForFullScreenIntent();
- fullscreenIntent.send();
- entry.notifyFullScreenIntentLaunched();
- mMetricsLogger.count("note_fullscreen", 1);
- } catch (PendingIntent.CanceledException e) {
- // ignore
- }
+ // Stop screensaver if the notification has a fullscreen intent.
+ // (like an incoming phone call)
+ mUiBgExecutor.execute(() -> {
+ try {
+ mDreamManager.awaken();
+ } catch (RemoteException e) {
+ e.printStackTrace();
}
+ });
+
+ // not immersive & a fullscreen alert should be shown
+ final PendingIntent fullscreenIntent =
+ entry.getSbn().getNotification().fullScreenIntent;
+ mLogger.logSendingFullScreenIntent(entry, fullscreenIntent);
+ try {
+ EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
+ entry.getKey());
+ mCentralSurfaces.wakeUpForFullScreenIntent();
+ fullscreenIntent.send();
+ entry.notifyFullScreenIntentLaunched();
+ mMetricsLogger.count("note_fullscreen", 1);
+ } catch (PendingIntent.CanceledException e) {
+ // ignore
}
}
@@ -607,12 +598,4 @@
mMainThreadHandler.post(mShadeController::collapsePanel);
}
}
-
- private boolean shouldSuppressFullScreenIntent(NotificationEntry entry) {
- if (mPresenter.isDeviceInVrMode()) {
- return true;
- }
-
- return entry.shouldSuppressFullScreenIntent();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt
index 81edff4..1f0b96a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt
@@ -96,19 +96,11 @@
})
}
- fun logFullScreenIntentSuppressedByDnD(entry: NotificationEntry) {
+ fun logFullScreenIntentSuppressedByVR(entry: NotificationEntry) {
buffer.log(TAG, DEBUG, {
str1 = entry.logKey
}, {
- "No Fullscreen intent: suppressed by DND: $str1"
- })
- }
-
- fun logFullScreenIntentNotImportantEnough(entry: NotificationEntry) {
- buffer.log(TAG, DEBUG, {
- str1 = entry.logKey
- }, {
- "No Fullscreen intent: not important enough: $str1"
+ "No Fullscreen intent: suppressed by VR mode: $str1"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 492734e..de7bf3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -212,7 +212,7 @@
private void updateWifiIconWithState(WifiIconState state) {
if (DEBUG) Log.d(TAG, "WifiIconState: " + state == null ? "" : state.toString());
if (state.visible && state.resId > 0) {
- mIconController.setSignalIcon(mSlotWifi, state);
+ mIconController.setWifiIcon(mSlotWifi, state);
mIconController.setIconVisibility(mSlotWifi, true);
} else {
mIconController.setIconVisibility(mSlotWifi, false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
index 06cd12d..946d7e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
@@ -27,11 +27,26 @@
/** True if we should display the mobile icons using the new status bar data pipeline. */
fun useNewMobileIcons(): Boolean = featureFlags.isEnabled(Flags.NEW_STATUS_BAR_MOBILE_ICONS)
+ /**
+ * True if we should run the new mobile icons backend to get the logging.
+ *
+ * Does *not* affect whether we render the mobile icons using the new backend data. See
+ * [useNewMobileIcons] for that.
+ */
+ fun runNewMobileIconsBackend(): Boolean =
+ featureFlags.isEnabled(Flags.NEW_STATUS_BAR_MOBILE_ICONS_BACKEND) || useNewMobileIcons()
+
/** True if we should display the wifi icon using the new status bar data pipeline. */
fun useNewWifiIcon(): Boolean = featureFlags.isEnabled(Flags.NEW_STATUS_BAR_WIFI_ICON)
- // TODO(b/238425913): Add flags to only run the mobile backend or wifi backend so we get the
- // logging without getting the UI effects.
+ /**
+ * True if we should run the new wifi icon backend to get the logging.
+ *
+ * Does *not* affect whether we render the wifi icon using the new backend data. See
+ * [useNewWifiIcon] for that.
+ */
+ fun runNewWifiIconBackend(): Boolean =
+ featureFlags.isEnabled(Flags.NEW_STATUS_BAR_WIFI_ICON_BACKEND) || useNewWifiIcon()
/**
* Returns true if we should apply some coloring to the wifi icon that was rendered with the new
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
index 380017c..c7e0ce1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
@@ -20,6 +20,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager
+import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
import javax.inject.Inject
@@ -50,6 +51,7 @@
private val iconController: StatusBarIconController,
private val iconsViewModelFactory: MobileIconsViewModel.Factory,
@Application scope: CoroutineScope,
+ private val statusBarPipelineFlags: StatusBarPipelineFlags,
) {
private val mobileSubIds: Flow<List<Int>> =
interactor.filteredSubscriptions.mapLatest { infos ->
@@ -66,8 +68,14 @@
private val mobileSubIdsState: StateFlow<List<Int>> =
mobileSubIds
.onEach {
- // Notify the icon controller here so that it knows to add icons
- iconController.setNewMobileIconSubIds(it)
+ // Only notify the icon controller if we want to *render* the new icons.
+ // Note that this flow may still run if
+ // [statusBarPipelineFlags.runNewMobileIconsBackend] is true because we may want to
+ // get the logging data without rendering.
+ if (statusBarPipelineFlags.useNewMobileIcons()) {
+ // Notify the icon controller here so that it knows to add icons
+ iconController.setNewMobileIconSubIds(it)
+ }
}
.stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt
new file mode 100644
index 0000000..b816364
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.wifi.ui
+
+import android.view.ViewGroup
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.phone.StatusBarIconController
+import com.android.systemui.statusbar.phone.StatusBarLocation
+import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
+import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+/**
+ * This class serves as a bridge between the old UI classes and the new data pipeline.
+ *
+ * Once the new pipeline notifies [wifiViewModel] that the wifi icon should be visible, this class
+ * notifies [iconController] to inflate the wifi icon (if needed). After that, the [wifiViewModel]
+ * has sole responsibility for updating the wifi icon drawable, visibility, etc. and the
+ * [iconController] will not do any updates to the icon.
+ */
+@SysUISingleton
+class WifiUiAdapter
+@Inject
+constructor(
+ private val iconController: StatusBarIconController,
+ private val wifiViewModel: WifiViewModel,
+ private val statusBarPipelineFlags: StatusBarPipelineFlags,
+) {
+ /**
+ * Binds the container for all the status bar icons to a view model, so that we inflate the wifi
+ * view once we receive a valid icon from the data pipeline.
+ *
+ * NOTE: This should go away as we better integrate the data pipeline with the UI.
+ *
+ * @return the view model used for this particular group in the given [location].
+ */
+ fun bindGroup(
+ statusBarIconGroup: ViewGroup,
+ location: StatusBarLocation,
+ ): LocationBasedWifiViewModel {
+ val locationViewModel =
+ when (location) {
+ StatusBarLocation.HOME -> wifiViewModel.home
+ StatusBarLocation.KEYGUARD -> wifiViewModel.keyguard
+ StatusBarLocation.QS -> wifiViewModel.qs
+ }
+
+ statusBarIconGroup.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ locationViewModel.wifiIcon.collect { wifiIcon ->
+ // Only notify the icon controller if we want to *render* the new icon.
+ // Note that this flow may still run if
+ // [statusBarPipelineFlags.runNewWifiIconBackend] is true because we may
+ // want to get the logging data without rendering.
+ if (wifiIcon != null && statusBarPipelineFlags.useNewWifiIcon()) {
+ iconController.setNewWifiIcon()
+ }
+ }
+ }
+ }
+ }
+
+ return locationViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
index 25537b9..345f8cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
@@ -30,9 +30,7 @@
import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON
-import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
-import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
@@ -62,26 +60,9 @@
fun onVisibilityStateChanged(@StatusBarIconView.VisibleState state: Int)
}
- /**
- * Binds the view to the appropriate view-model based on the given location. The view will
- * continue to be updated following updates from the view-model.
- */
- @JvmStatic
- fun bind(
- view: ViewGroup,
- wifiViewModel: WifiViewModel,
- location: StatusBarLocation,
- ): Binding {
- return when (location) {
- StatusBarLocation.HOME -> bind(view, wifiViewModel.home)
- StatusBarLocation.KEYGUARD -> bind(view, wifiViewModel.keyguard)
- StatusBarLocation.QS -> bind(view, wifiViewModel.qs)
- }
- }
-
/** Binds the view to the view-model, continuing to update the former based on the latter. */
@JvmStatic
- private fun bind(
+ fun bind(
view: ViewGroup,
viewModel: LocationBasedWifiViewModel,
): Binding {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
index 0cd9bd7..a45076b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
@@ -26,9 +26,8 @@
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
-import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.pipeline.wifi.ui.binder.WifiViewBinder
-import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
+import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
/**
* A new and more modern implementation of [com.android.systemui.statusbar.StatusBarWifiView] that
@@ -81,12 +80,11 @@
private fun initView(
slotName: String,
- wifiViewModel: WifiViewModel,
- location: StatusBarLocation,
+ wifiViewModel: LocationBasedWifiViewModel,
) {
slot = slotName
initDotView()
- binding = WifiViewBinder.bind(this, wifiViewModel, location)
+ binding = WifiViewBinder.bind(this, wifiViewModel)
}
// Mostly duplicated from [com.android.systemui.statusbar.StatusBarWifiView].
@@ -116,14 +114,13 @@
fun constructAndBind(
context: Context,
slot: String,
- wifiViewModel: WifiViewModel,
- location: StatusBarLocation,
+ wifiViewModel: LocationBasedWifiViewModel,
): ModernStatusBarWifiView {
return (
LayoutInflater.from(context).inflate(R.layout.new_status_bar_wifi_group, null)
as ModernStatusBarWifiView
).also {
- it.initView(slot, wifiViewModel, location)
+ it.initView(slot, wifiViewModel)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index 89b96b7..0782bbb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -145,7 +145,8 @@
else -> null
}
}
- .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = null)
+ .logOutputChange(logger, "icon") { icon -> icon?.contentDescription.toString() }
+ .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = null)
/** The wifi activity status. Null if we shouldn't display the activity status. */
private val activity: Flow<WifiActivityModel?> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index 1d414745..7acdaff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -118,7 +118,10 @@
private void updateDeviceState(int state) {
Log.v(TAG, "updateDeviceState [state=" + state + "]");
- Trace.beginSection("updateDeviceState [state=" + state + "]");
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(
+ Trace.TRACE_TAG_APP, "updateDeviceState [state=" + state + "]");
+ }
try {
if (mDeviceState == state) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index bd2123a..69b55c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -33,6 +33,7 @@
import androidx.annotation.NonNull;
import com.android.internal.util.ConcurrentUtils;
+import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -63,6 +64,7 @@
private volatile int mNumConnectedDevices;
// Assume tethering is available until told otherwise
private volatile boolean mIsTetheringSupported = true;
+ private final boolean mIsTetheringSupportedConfig;
private volatile boolean mHasTetherableWifiRegexs = true;
private boolean mWaitingForTerminalState;
@@ -100,23 +102,29 @@
mTetheringManager = context.getSystemService(TetheringManager.class);
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
mMainHandler = mainHandler;
- mTetheringManager.registerTetheringEventCallback(
- new HandlerExecutor(backgroundHandler), mTetheringCallback);
+ mIsTetheringSupportedConfig = context.getResources()
+ .getBoolean(R.bool.config_show_wifi_tethering);
+ if (mIsTetheringSupportedConfig) {
+ mTetheringManager.registerTetheringEventCallback(
+ new HandlerExecutor(backgroundHandler), mTetheringCallback);
+ }
dumpManager.registerDumpable(getClass().getSimpleName(), this);
}
/**
* Whether hotspot is currently supported.
*
- * This will return {@code true} immediately on creation of the controller, but may be updated
- * later. Callbacks from this controllers will notify if the state changes.
+ * This may return {@code true} immediately on creation of the controller, but may be updated
+ * later as capabilities are collected from System Server.
+ *
+ * Callbacks from this controllers will notify if the state changes.
*
* @return {@code true} if hotspot is supported (or we haven't been told it's not)
* @see #addCallback
*/
@Override
public boolean isHotspotSupported() {
- return mIsTetheringSupported && mHasTetherableWifiRegexs
+ return mIsTetheringSupportedConfig && mIsTetheringSupported && mHasTetherableWifiRegexs
&& UserManager.get(mContext).isUserAdmin(ActivityManager.getCurrentUser());
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
index f9d14cd..a374885 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
@@ -28,6 +28,8 @@
import android.view.WindowManagerGlobal
import android.widget.Toast
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.GuestResetOrExitSessionReceiver
+import com.android.systemui.GuestResumeSessionReceiver
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -58,6 +60,8 @@
private val devicePolicyManager: DevicePolicyManager,
private val refreshUsersScheduler: RefreshUsersScheduler,
private val uiEventLogger: UiEventLogger,
+ resumeSessionReceiver: GuestResumeSessionReceiver,
+ resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver,
) {
/** Whether the device is configured to always have a guest user available. */
val isGuestUserAutoCreated: Boolean = repository.isGuestUserAutoCreated
@@ -65,6 +69,11 @@
/** Whether the guest user is currently being reset. */
val isGuestUserResetting: Boolean = repository.isGuestUserResetting
+ init {
+ resumeSessionReceiver.register()
+ resetOrExitSessionReceiver.register()
+ }
+
/** Notifies that the device has finished booting. */
fun onDeviceBootCompleted() {
applicationScope.launch {
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 41c307a..aa14a40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -20,6 +20,8 @@
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
+import static com.android.internal.util.FunctionalUtils.ThrowingConsumer;
+
import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -108,9 +110,6 @@
@RunWithLooper(setAsMainLooper = true)
public class UdfpsControllerTest extends SysuiTestCase {
- // Use this for inputs going into SystemUI. Use UdfpsController.mUdfpsSensorId for things
- // leaving SystemUI.
- private static final int TEST_UDFPS_SENSOR_ID = 1;
private static final long TEST_REQUEST_ID = 70;
@Rule
@@ -121,7 +120,6 @@
// Dependencies
private FakeExecutor mBiometricsExecutor;
- private Execution mExecution;
@Mock
private LayoutInflater mLayoutInflater;
@Mock
@@ -197,17 +195,24 @@
private BouncerInteractor mBouncerInteractor;
// Capture listeners so that they can be used to send events
- @Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
+ @Captor
+ private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
private IUdfpsOverlayController mOverlayController;
- @Captor private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor;
- @Captor private ArgumentCaptor<View.OnHoverListener> mHoverListenerCaptor;
- @Captor private ArgumentCaptor<Runnable> mOnDisplayConfiguredCaptor;
- @Captor private ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor;
+ @Captor
+ private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor;
+ @Captor
+ private ArgumentCaptor<View.OnHoverListener> mHoverListenerCaptor;
+ @Captor
+ private ArgumentCaptor<Runnable> mOnDisplayConfiguredCaptor;
+ @Captor
+ private ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor;
private ScreenLifecycle.Observer mScreenObserver;
+ private FingerprintSensorPropertiesInternal mOpticalProps;
+ private FingerprintSensorPropertiesInternal mUltrasonicProps;
@Before
public void setUp() {
- mExecution = new FakeExecution();
+ Execution execution = new FakeExecution();
when(mLayoutInflater.inflate(R.layout.udfps_view, null, false))
.thenReturn(mUdfpsView);
@@ -220,9 +225,7 @@
when(mLayoutInflater.inflate(R.layout.udfps_fpm_other_view, null))
.thenReturn(mFpmOtherView);
when(mEnrollView.getContext()).thenReturn(mContext);
- when(mKeyguardStateController.isOccluded()).thenReturn(false);
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
- final List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
@@ -232,13 +235,25 @@
"" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
"vendor/version/revision" /* softwareVersion */));
- props.add(new FingerprintSensorPropertiesInternal(TEST_UDFPS_SENSOR_ID,
+ mOpticalProps = new FingerprintSensorPropertiesInternal(1 /* sensorId */,
SensorProperties.STRENGTH_STRONG,
5 /* maxEnrollmentsPerUser */,
componentInfo,
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
- true /* resetLockoutRequiresHardwareAuthToken */));
+ true /* resetLockoutRequiresHardwareAuthToken */);
+
+ mUltrasonicProps = new FingerprintSensorPropertiesInternal(2 /* sensorId */,
+ SensorProperties.STRENGTH_STRONG,
+ 5 /* maxEnrollmentsPerUser */,
+ componentInfo,
+ FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC,
+ true /* resetLockoutRequiresHardwareAuthToken */);
+
+ List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
+ props.add(mOpticalProps);
+ props.add(mUltrasonicProps);
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
+
mFgExecutor = new FakeExecutor(new FakeSystemClock());
// Create a fake background executor.
@@ -246,7 +261,7 @@
mUdfpsController = new UdfpsController(
mContext,
- mExecution,
+ execution,
mLayoutInflater,
mFingerprintManager,
mWindowManager,
@@ -281,13 +296,13 @@
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
mScreenObserver = mScreenObserverCaptor.getValue();
- mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID, new UdfpsOverlayParams());
+ mUdfpsController.updateOverlayParams(mOpticalProps, new UdfpsOverlayParams());
mUdfpsController.setUdfpsDisplayMode(mUdfpsDisplayMode);
}
@Test
public void dozeTimeTick() throws RemoteException {
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
mUdfpsController.dozeTimeTick();
@@ -302,7 +317,7 @@
when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
// GIVEN that the overlay is showing
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
@@ -337,7 +352,7 @@
when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
// GIVEN that the overlay is showing
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
@@ -345,7 +360,7 @@
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
if (stale) {
- mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
+ mOverlayController.hideUdfpsOverlay(mOpticalProps.sensorId);
mFgExecutor.runAllReady();
}
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
@@ -365,7 +380,7 @@
when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
// GIVEN that the overlay is showing
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
@@ -388,12 +403,12 @@
@Test
public void hideUdfpsOverlay_resetsAltAuthBouncerWhenShowing() throws RemoteException {
// GIVEN overlay was showing and the udfps bouncer is showing
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true);
// WHEN the overlay is hidden
- mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
+ mOverlayController.hideUdfpsOverlay(mOpticalProps.sensorId);
mFgExecutor.runAllReady();
// THEN the udfps bouncer is reset
@@ -402,13 +417,13 @@
@Test
public void testSubscribesToOrientationChangesWhenShowingOverlay() throws Exception {
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler), anyLong());
- mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
+ mOverlayController.hideUdfpsOverlay(mOpticalProps.sensorId);
mFgExecutor.runAllReady();
verify(mDisplayManager).unregisterDisplayListener(any());
@@ -438,12 +453,12 @@
}
// Initialize the overlay with old parameters.
- mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID, oldParams);
+ mUdfpsController.updateOverlayParams(mOpticalProps, oldParams);
// Show the overlay.
reset(mWindowManager);
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID,
- TEST_UDFPS_SENSOR_ID,
+ mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_ENROLL_ENROLLING,
mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
@@ -451,7 +466,7 @@
// Update overlay parameters.
reset(mWindowManager);
- mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID, newParams);
+ mUdfpsController.updateOverlayParams(mOpticalProps, newParams);
mFgExecutor.runAllReady();
// Ensure the overlay was recreated.
@@ -473,18 +488,18 @@
final int rotation = Surface.ROTATION_0;
// Initialize the overlay.
- mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
+ mUdfpsController.updateOverlayParams(mOpticalProps,
new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
scaleFactor, rotation));
// Show the overlay.
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mWindowManager).addView(any(), any());
// Update overlay with the same parameters.
- mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
+ mUdfpsController.updateOverlayParams(mOpticalProps,
new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
scaleFactor, rotation));
mFgExecutor.runAllReady();
@@ -526,13 +541,13 @@
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// Show the overlay.
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
// Test ROTATION_0
- mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
+ mUdfpsController.updateOverlayParams(mOpticalProps,
new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
scaleFactor, Surface.ROTATION_0));
MotionEvent event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor,
@@ -549,7 +564,7 @@
// Test ROTATION_90
reset(mAlternateTouchProvider);
- mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
+ mUdfpsController.updateOverlayParams(mOpticalProps,
new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
scaleFactor, Surface.ROTATION_90));
event = obtainMotionEvent(ACTION_DOWN, displayHeight, 0, touchMinor, touchMajor);
@@ -565,7 +580,7 @@
// Test ROTATION_270
reset(mAlternateTouchProvider);
- mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
+ mUdfpsController.updateOverlayParams(mOpticalProps,
new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
scaleFactor, Surface.ROTATION_270));
event = obtainMotionEvent(ACTION_DOWN, 0, displayWidth, touchMinor, touchMajor);
@@ -581,7 +596,7 @@
// Test ROTATION_180
reset(mAlternateTouchProvider);
- mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
+ mUdfpsController.updateOverlayParams(mOpticalProps,
new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
scaleFactor, Surface.ROTATION_180));
// ROTATION_180 is not supported. It should be treated like ROTATION_0.
@@ -597,63 +612,108 @@
eq(expectedY), eq(expectedMinor), eq(expectedMajor));
}
+ private void runForAllUdfpsTypes(
+ ThrowingConsumer<FingerprintSensorPropertiesInternal> sensorPropsConsumer) {
+ for (FingerprintSensorPropertiesInternal sensorProps : List.of(mOpticalProps,
+ mUltrasonicProps)) {
+ mUdfpsController.updateOverlayParams(sensorProps, new UdfpsOverlayParams());
+ sensorPropsConsumer.accept(sensorProps);
+ }
+ }
+
@Test
- public void fingerDown() throws RemoteException {
+ public void fingerDown() {
+ runForAllUdfpsTypes(this::fingerDownForSensor);
+ }
+
+ private void fingerDownForSensor(FingerprintSensorPropertiesInternal sensorProps)
+ throws RemoteException {
+ reset(mUdfpsView, mAlternateTouchProvider, mFingerprintManager, mLatencyTracker,
+ mKeyguardUpdateMonitor);
+
// Configure UdfpsView to accept the ACTION_DOWN event
when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
// GIVEN that the overlay is showing
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
- // WHEN ACTION_DOWN is received
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+
+ // WHEN ACTION_DOWN is received
MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
mBiometricsExecutor.runAllReady();
downEvent.recycle();
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
- // FIX THIS TEST
+ MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
mBiometricsExecutor.runAllReady();
moveEvent.recycle();
+
mFgExecutor.runAllReady();
+
// THEN FingerprintManager is notified about onPointerDown
verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(0), eq(0), eq(0f),
eq(0f));
verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
anyFloat(), anyFloat());
- verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
+
// AND display configuration begins
- verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
+ if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
+ verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
+ } else {
+ verify(mLatencyTracker, never()).onActionStart(
+ eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
+ verify(mUdfpsView, never()).configureDisplay(any());
+ }
verify(mLatencyTracker, never()).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID));
- // AND onDisplayConfigured notifies FingerprintManager about onUiReady
- mOnDisplayConfiguredCaptor.getValue().run();
- mBiometricsExecutor.runAllReady();
- InOrder inOrder = inOrder(mAlternateTouchProvider, mLatencyTracker);
- inOrder.verify(mAlternateTouchProvider).onUiReady();
- inOrder.verify(mLatencyTracker).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
+
+ if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ // AND onDisplayConfigured notifies FingerprintManager about onUiReady
+ mOnDisplayConfiguredCaptor.getValue().run();
+ mBiometricsExecutor.runAllReady();
+ InOrder inOrder = inOrder(mAlternateTouchProvider, mLatencyTracker);
+ inOrder.verify(mAlternateTouchProvider).onUiReady();
+ inOrder.verify(mLatencyTracker).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
+ } else {
+ verify(mAlternateTouchProvider, never()).onUiReady();
+ verify(mLatencyTracker, never()).onActionEnd(
+ eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
+ }
}
@Test
- public void aodInterrupt() throws RemoteException {
+ public void aodInterrupt() {
+ runForAllUdfpsTypes(this::aodInterruptForSensor);
+ }
+
+ private void aodInterruptForSensor(FingerprintSensorPropertiesInternal sensorProps)
+ throws RemoteException {
+ mUdfpsController.cancelAodInterrupt();
+ reset(mUdfpsView, mAlternateTouchProvider, mFingerprintManager, mKeyguardUpdateMonitor);
+ when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
+
// GIVEN that the overlay is showing and screen is on and fp is running
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
- when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
// WHEN fingerprint is requested because of AOD interrupt
mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
mFgExecutor.runAllReady();
- // THEN display configuration begins
- // AND onDisplayConfigured notifies FingerprintManager about onUiReady
- verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
- mOnDisplayConfiguredCaptor.getValue().run();
+ if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ // THEN display configuration begins
+ // AND onDisplayConfigured notifies FingerprintManager about onUiReady
+ verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
+ mOnDisplayConfiguredCaptor.getValue().run();
+ } else {
+ verify(mUdfpsView, never()).configureDisplay(mOnDisplayConfiguredCaptor.capture());
+ }
mBiometricsExecutor.runAllReady();
verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID),
eq(0), eq(0), eq(3f) /* minor */, eq(2f) /* major */);
@@ -663,54 +723,92 @@
}
@Test
- public void cancelAodInterrupt() throws RemoteException {
+ public void cancelAodInterrupt() {
+ runForAllUdfpsTypes(this::cancelAodInterruptForSensor);
+ }
+
+ private void cancelAodInterruptForSensor(FingerprintSensorPropertiesInternal sensorProps)
+ throws RemoteException {
+ reset(mUdfpsView);
+
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
- when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
- when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
- // WHEN it is cancelled
- mUdfpsController.cancelAodInterrupt();
- // THEN the display is unconfigured
- verify(mUdfpsView).unconfigureDisplay();
+ if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
+ // WHEN it is cancelled
+ mUdfpsController.cancelAodInterrupt();
+ // THEN the display is unconfigured
+ verify(mUdfpsView).unconfigureDisplay();
+ } else {
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+ // WHEN it is cancelled
+ mUdfpsController.cancelAodInterrupt();
+ // THEN the display configuration is unchanged.
+ verify(mUdfpsView, never()).unconfigureDisplay();
+ }
}
@Test
- public void aodInterruptTimeout() throws RemoteException {
+ public void aodInterruptTimeout() {
+ runForAllUdfpsTypes(this::aodInterruptTimeoutForSensor);
+ }
+
+ private void aodInterruptTimeoutForSensor(FingerprintSensorPropertiesInternal sensorProps)
+ throws RemoteException {
+ reset(mUdfpsView);
+
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
- when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
mFgExecutor.runAllReady();
- when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
+ if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
+ } else {
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+ }
// WHEN it times out
mFgExecutor.advanceClockToNext();
mFgExecutor.runAllReady();
- // THEN the display is unconfigured
- verify(mUdfpsView).unconfigureDisplay();
+ if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ // THEN the display is unconfigured.
+ verify(mUdfpsView).unconfigureDisplay();
+ } else {
+ // THEN the display configuration is unchanged.
+ verify(mUdfpsView, never()).unconfigureDisplay();
+ }
}
@Test
- public void aodInterruptCancelTimeoutActionOnFingerUp() throws RemoteException {
+ public void aodInterruptCancelTimeoutActionOnFingerUp() {
+ runForAllUdfpsTypes(this::aodInterruptCancelTimeoutActionOnFingerUpForSensor);
+ }
+
+ private void aodInterruptCancelTimeoutActionOnFingerUpForSensor(
+ FingerprintSensorPropertiesInternal sensorProps) throws RemoteException {
+ reset(mUdfpsView);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
- when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
mFgExecutor.runAllReady();
- // Configure UdfpsView to accept the ACTION_UP event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
+ if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ // Configure UdfpsView to accept the ACTION_UP event
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
+ } else {
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+ }
// WHEN ACTION_UP is received
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
@@ -735,37 +833,54 @@
moveEvent.recycle();
mFgExecutor.runAllReady();
- // Configure UdfpsView to accept the finger up event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
+ if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ // Configure UdfpsView to accept the finger up event
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
+ } else {
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+ }
// WHEN it times out
mFgExecutor.advanceClockToNext();
mFgExecutor.runAllReady();
- // THEN the display should be unconfigured once. If the timeout action is not
- // cancelled, the display would be unconfigured twice which would cause two
- // FP attempts.
- verify(mUdfpsView, times(1)).unconfigureDisplay();
+ if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ // THEN the display should be unconfigured once. If the timeout action is not
+ // cancelled, the display would be unconfigured twice which would cause two
+ // FP attempts.
+ verify(mUdfpsView, times(1)).unconfigureDisplay();
+ } else {
+ verify(mUdfpsView, never()).unconfigureDisplay();
+ }
}
@Test
- public void aodInterruptCancelTimeoutActionOnAcquired() throws RemoteException {
+ public void aodInterruptCancelTimeoutActionOnAcquired() {
+ runForAllUdfpsTypes(this::aodInterruptCancelTimeoutActionOnAcquiredForSensor);
+ }
+
+ private void aodInterruptCancelTimeoutActionOnAcquiredForSensor(
+ FingerprintSensorPropertiesInternal sensorProps) throws RemoteException {
+ reset(mUdfpsView);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
- when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
mFgExecutor.runAllReady();
- // Configure UdfpsView to accept the acquired event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
+ if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ // Configure UdfpsView to accept the acquired event
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
+ } else {
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+ }
// WHEN acquired is received
- mOverlayController.onAcquired(TEST_UDFPS_SENSOR_ID,
+ mOverlayController.onAcquired(sensorProps.sensorId,
BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD);
// Configure UdfpsView to accept the ACTION_DOWN event
@@ -785,29 +900,43 @@
moveEvent.recycle();
mFgExecutor.runAllReady();
- // Configure UdfpsView to accept the finger up event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
+ if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ // Configure UdfpsView to accept the finger up event
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
+ } else {
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+ }
// WHEN it times out
mFgExecutor.advanceClockToNext();
mFgExecutor.runAllReady();
- // THEN the display should be unconfigured once. If the timeout action is not
- // cancelled, the display would be unconfigured twice which would cause two
- // FP attempts.
- verify(mUdfpsView, times(1)).unconfigureDisplay();
+ if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ // THEN the display should be unconfigured once. If the timeout action is not
+ // cancelled, the display would be unconfigured twice which would cause two
+ // FP attempts.
+ verify(mUdfpsView, times(1)).unconfigureDisplay();
+ } else {
+ verify(mUdfpsView, never()).unconfigureDisplay();
+ }
}
@Test
- public void aodInterruptScreenOff() throws RemoteException {
+ public void aodInterruptScreenOff() {
+ runForAllUdfpsTypes(this::aodInterruptScreenOffForSensor);
+ }
+
+ private void aodInterruptScreenOffForSensor(FingerprintSensorPropertiesInternal sensorProps)
+ throws RemoteException {
+ reset(mUdfpsView);
+
// GIVEN screen off
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOff();
mFgExecutor.runAllReady();
// WHEN aod interrupt is received
- when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
// THEN display doesn't get configured because it's off
@@ -815,9 +944,16 @@
}
@Test
- public void aodInterrupt_fingerprintNotRunning() throws RemoteException {
+ public void aodInterrupt_fingerprintNotRunning() {
+ runForAllUdfpsTypes(this::aodInterrupt_fingerprintNotRunningForSensor);
+ }
+
+ private void aodInterrupt_fingerprintNotRunningForSensor(
+ FingerprintSensorPropertiesInternal sensorProps) throws RemoteException {
+ reset(mUdfpsView);
+
// GIVEN showing overlay
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD,
mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
@@ -839,7 +975,7 @@
// GIVEN that the overlay is showing and a11y touch exploration enabled
when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
@@ -874,7 +1010,7 @@
// GIVEN that the overlay is showing and a11y touch exploration NOT enabled
when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index c5a7de4..c234178 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -92,6 +92,12 @@
@Mock
BouncerCallbackInteractor mBouncerCallbackInteractor;
+ @Mock
+ DreamOverlayAnimationsController mAnimationsController;
+
+ @Mock
+ DreamOverlayStateController mStateController;
+
DreamOverlayContainerViewController mController;
@Before
@@ -115,7 +121,9 @@
MAX_BURN_IN_OFFSET,
BURN_IN_PROTECTION_UPDATE_INTERVAL,
MILLIS_UNTIL_FULL_JITTER,
- mBouncerCallbackInteractor);
+ mBouncerCallbackInteractor,
+ mAnimationsController,
+ mStateController);
}
@Test
@@ -188,4 +196,31 @@
verify(mBlurUtils).blurRadiusOfRatio(1 - scaledFraction);
verify(mBlurUtils).applyBlur(mViewRoot, (int) blurRadius, false);
}
+
+ @Test
+ public void testStartDreamEntryAnimationsOnAttachedNonLowLight() {
+ when(mStateController.isLowLightActive()).thenReturn(false);
+
+ mController.onViewAttached();
+
+ verify(mAnimationsController).startEntryAnimations(mDreamOverlayContainerView);
+ verify(mAnimationsController, never()).cancelRunningEntryAnimations();
+ }
+
+ @Test
+ public void testNeverStartDreamEntryAnimationsOnAttachedForLowLight() {
+ when(mStateController.isLowLightActive()).thenReturn(true);
+
+ mController.onViewAttached();
+
+ verify(mAnimationsController, never()).startEntryAnimations(mDreamOverlayContainerView);
+ }
+
+ @Test
+ public void testCancelDreamEntryAnimationsOnDetached() {
+ mController.onViewAttached();
+ mController.onViewDetached();
+
+ verify(mAnimationsController).cancelRunningEntryAnimations();
+ }
}
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 f370be1..f04a37f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -253,6 +253,7 @@
verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED);
verify(mStateController).setOverlayActive(false);
verify(mStateController).setLowLightActive(false);
+ verify(mStateController).setEntryAnimationsFinished(false);
}
@Test
@@ -273,27 +274,31 @@
@Test
public void testDecorViewNotAddedToWindowAfterDestroy() throws Exception {
- when(mDreamOverlayContainerView.getParent())
- .thenReturn(mDreamOverlayContainerViewParent)
- .thenReturn(null);
-
final IBinder proxy = mService.onBind(new Intent());
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+ // Destroy the service.
+ mService.onDestroy();
+ mMainExecutor.runAllReady();
+
// Inform the overlay service of dream starting.
overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
false /*shouldShowComplication*/);
-
- // Destroy the service.
- mService.onDestroy();
-
- // Run executor tasks.
mMainExecutor.runAllReady();
verify(mWindowManager, never()).addView(any(), any());
}
@Test
+ public void testNeverRemoveDecorViewIfNotAdded() {
+ // Service destroyed before dream started.
+ mService.onDestroy();
+ mMainExecutor.runAllReady();
+
+ verify(mWindowManager, never()).removeView(any());
+ }
+
+ @Test
public void testResetCurrentOverlayWhenConnectedToNewDream() 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/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index d1d32a1..c21c7a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -234,4 +234,20 @@
verify(mCallback, times(1)).onStateChanged();
assertThat(stateController.isLowLightActive()).isTrue();
}
+
+ @Test
+ public void testNotifyEntryAnimationsFinishedChanged() {
+ final DreamOverlayStateController stateController =
+ new DreamOverlayStateController(mExecutor);
+
+ stateController.addCallback(mCallback);
+ mExecutor.runAllReady();
+ assertThat(stateController.areEntryAnimationsFinished()).isFalse();
+
+ stateController.setEntryAnimationsFinished(true);
+ mExecutor.runAllReady();
+
+ verify(mCallback, times(1)).onStateChanged();
+ assertThat(stateController.areEntryAnimationsFinished()).isTrue();
+ }
}
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 aa02178..85c2819 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -19,16 +19,20 @@
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AlarmManager;
+import android.content.Context;
import android.content.res.Resources;
import android.hardware.SensorPrivacyManager;
import android.net.ConnectivityManager;
@@ -55,6 +59,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -69,7 +74,7 @@
"{count, plural, =1 {# notification} other {# notifications}}";
@Mock
- DreamOverlayStatusBarView mView;
+ MockDreamOverlayStatusBarView mView;
@Mock
ConnectivityManager mConnectivityManager;
@Mock
@@ -105,6 +110,9 @@
@Mock
DreamOverlayStateController mDreamOverlayStateController;
+ @Captor
+ private ArgumentCaptor<DreamOverlayStateController.Callback> mCallbackCaptor;
+
private final Executor mMainExecutor = Runnable::run;
DreamOverlayStatusBarViewController mController;
@@ -115,6 +123,8 @@
when(mResources.getString(R.string.dream_overlay_status_bar_notification_indicator))
.thenReturn(NOTIFICATION_INDICATOR_FORMATTER_STRING);
+ doCallRealMethod().when(mView).setVisibility(anyInt());
+ doCallRealMethod().when(mView).getVisibility();
mController = new DreamOverlayStatusBarViewController(
mView,
@@ -454,12 +464,10 @@
public void testStatusBarHiddenWhenSystemStatusBarShown() {
mController.onViewAttached();
- final ArgumentCaptor<StatusBarWindowStateListener>
- callbackCapture = ArgumentCaptor.forClass(StatusBarWindowStateListener.class);
- verify(mStatusBarWindowStateController).addListener(callbackCapture.capture());
- callbackCapture.getValue().onStatusBarWindowStateChanged(WINDOW_STATE_SHOWING);
+ updateEntryAnimationsFinished();
+ updateStatusBarWindowState(true);
- verify(mView).setVisibility(View.INVISIBLE);
+ assertThat(mView.getVisibility()).isEqualTo(View.INVISIBLE);
}
@Test
@@ -467,29 +475,43 @@
mController.onViewAttached();
reset(mView);
- final ArgumentCaptor<StatusBarWindowStateListener>
- callbackCapture = ArgumentCaptor.forClass(StatusBarWindowStateListener.class);
- verify(mStatusBarWindowStateController).addListener(callbackCapture.capture());
- callbackCapture.getValue().onStatusBarWindowStateChanged(WINDOW_STATE_HIDDEN);
+ updateEntryAnimationsFinished();
+ updateStatusBarWindowState(false);
- verify(mView).setVisibility(View.VISIBLE);
+ assertThat(mView.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
public void testUnattachedStatusBarVisibilityUnchangedWhenSystemStatusBarHidden() {
mController.onViewAttached();
+ updateEntryAnimationsFinished();
mController.onViewDetached();
reset(mView);
- final ArgumentCaptor<StatusBarWindowStateListener>
- callbackCapture = ArgumentCaptor.forClass(StatusBarWindowStateListener.class);
- verify(mStatusBarWindowStateController).addListener(callbackCapture.capture());
- callbackCapture.getValue().onStatusBarWindowStateChanged(WINDOW_STATE_SHOWING);
+ updateStatusBarWindowState(true);
verify(mView, never()).setVisibility(anyInt());
}
@Test
+ public void testNoChangeToVisibilityBeforeDreamStartedWhenStatusBarHidden() {
+ mController.onViewAttached();
+
+ // Trigger status bar window state change.
+ final StatusBarWindowStateListener listener = updateStatusBarWindowState(false);
+
+ // Verify no visibility change because dream not started.
+ verify(mView, never()).setVisibility(anyInt());
+
+ // Dream entry animations finished.
+ updateEntryAnimationsFinished();
+
+ // Trigger another status bar window state change, and verify visibility change.
+ listener.onStatusBarWindowStateChanged(WINDOW_STATE_HIDDEN);
+ assertThat(mView.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
public void testExtraStatusBarItemSetWhenItemsChange() {
mController.onViewAttached();
when(mStatusBarItem.getView()).thenReturn(mStatusBarItemView);
@@ -507,16 +529,75 @@
public void testLowLightHidesStatusBar() {
when(mDreamOverlayStateController.isLowLightActive()).thenReturn(true);
mController.onViewAttached();
+ updateEntryAnimationsFinished();
- verify(mView).setVisibility(View.INVISIBLE);
- reset(mView);
-
- when(mDreamOverlayStateController.isLowLightActive()).thenReturn(false);
final ArgumentCaptor<DreamOverlayStateController.Callback> callbackCapture =
ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
verify(mDreamOverlayStateController).addCallback(callbackCapture.capture());
callbackCapture.getValue().onStateChanged();
- verify(mView).setVisibility(View.VISIBLE);
+ assertThat(mView.getVisibility()).isEqualTo(View.INVISIBLE);
+ reset(mView);
+
+ when(mDreamOverlayStateController.isLowLightActive()).thenReturn(false);
+ callbackCapture.getValue().onStateChanged();
+
+ assertThat(mView.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void testNoChangeToVisibilityBeforeDreamStartedWhenLowLightStateChange() {
+ when(mDreamOverlayStateController.isLowLightActive()).thenReturn(false);
+ mController.onViewAttached();
+
+ // No change to visibility because dream not fully started.
+ verify(mView, never()).setVisibility(anyInt());
+
+ // Dream entry animations finished.
+ updateEntryAnimationsFinished();
+
+ // Trigger state change and verify visibility changed.
+ final ArgumentCaptor<DreamOverlayStateController.Callback> callbackCapture =
+ ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
+ verify(mDreamOverlayStateController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onStateChanged();
+
+ assertThat(mView.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ private StatusBarWindowStateListener updateStatusBarWindowState(boolean show) {
+ when(mStatusBarWindowStateController.windowIsShowing()).thenReturn(show);
+ final ArgumentCaptor<StatusBarWindowStateListener>
+ callbackCapture = ArgumentCaptor.forClass(StatusBarWindowStateListener.class);
+ verify(mStatusBarWindowStateController).addListener(callbackCapture.capture());
+ final StatusBarWindowStateListener listener = callbackCapture.getValue();
+ listener.onStatusBarWindowStateChanged(show ? WINDOW_STATE_SHOWING : WINDOW_STATE_HIDDEN);
+ return listener;
+ }
+
+ private void updateEntryAnimationsFinished() {
+ when(mDreamOverlayStateController.areEntryAnimationsFinished()).thenReturn(true);
+
+ verify(mDreamOverlayStateController).addCallback(mCallbackCaptor.capture());
+ final DreamOverlayStateController.Callback callback = mCallbackCaptor.getValue();
+ callback.onStateChanged();
+ }
+
+ private static class MockDreamOverlayStatusBarView extends DreamOverlayStatusBarView {
+ private int mVisibility = View.VISIBLE;
+
+ private MockDreamOverlayStatusBarView(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ mVisibility = visibility;
+ }
+
+ @Override
+ public int getVisibility() {
+ return mVisibility;
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
index 3b9e398..b477592 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.dreams.complication;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -29,16 +30,18 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.DreamOverlayStateController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashSet;
@SmallTest
@@ -77,9 +80,20 @@
@Mock
ComplicationLayoutParams mComplicationLayoutParams;
+ @Mock
+ DreamOverlayStateController mDreamOverlayStateController;
+
+ @Captor
+ private ArgumentCaptor<Observer<Collection<ComplicationViewModel>>> mObserverCaptor;
+
+ @Captor
+ private ArgumentCaptor<DreamOverlayStateController.Callback> mCallbackCaptor;
+
@Complication.Category
static final int COMPLICATION_CATEGORY = Complication.CATEGORY_SYSTEM;
+ private ComplicationHostViewController mController;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -91,6 +105,15 @@
when(mViewHolder.getCategory()).thenReturn(COMPLICATION_CATEGORY);
when(mViewHolder.getLayoutParams()).thenReturn(mComplicationLayoutParams);
when(mComplicationView.getParent()).thenReturn(mComplicationHostView);
+
+ mController = new ComplicationHostViewController(
+ mComplicationHostView,
+ mLayoutEngine,
+ mDreamOverlayStateController,
+ mLifecycleOwner,
+ mViewModel);
+
+ mController.init();
}
/**
@@ -98,25 +121,12 @@
*/
@Test
public void testViewModelObservation() {
- final ArgumentCaptor<Observer<Collection<ComplicationViewModel>>> observerArgumentCaptor =
- ArgumentCaptor.forClass(Observer.class);
- final ComplicationHostViewController controller = new ComplicationHostViewController(
- mComplicationHostView,
- mLayoutEngine,
- mLifecycleOwner,
- mViewModel);
-
- controller.init();
-
- verify(mComplicationViewModelLiveData).observe(eq(mLifecycleOwner),
- observerArgumentCaptor.capture());
-
final Observer<Collection<ComplicationViewModel>> observer =
- observerArgumentCaptor.getValue();
+ captureComplicationViewModelsObserver();
- // Add complication and ensure it is added to the view.
+ // Add a complication and ensure it is added to the view.
final HashSet<ComplicationViewModel> complications = new HashSet<>(
- Arrays.asList(mComplicationViewModel));
+ Collections.singletonList(mComplicationViewModel));
observer.onChanged(complications);
verify(mLayoutEngine).addComplication(eq(mComplicationId), eq(mComplicationView),
@@ -127,4 +137,48 @@
verify(mLayoutEngine).removeComplication(eq(mComplicationId));
}
+
+ @Test
+ public void testNewComplicationsBeforeEntryAnimationsFinishSetToInvisible() {
+ final Observer<Collection<ComplicationViewModel>> observer =
+ captureComplicationViewModelsObserver();
+
+ // Add a complication before entry animations are finished.
+ final HashSet<ComplicationViewModel> complications = new HashSet<>(
+ Collections.singletonList(mComplicationViewModel));
+ observer.onChanged(complications);
+
+ // The complication view should be set to invisible.
+ verify(mComplicationView).setVisibility(View.INVISIBLE);
+ }
+
+ @Test
+ public void testNewComplicationsAfterEntryAnimationsFinishNotSetToInvisible() {
+ final Observer<Collection<ComplicationViewModel>> observer =
+ captureComplicationViewModelsObserver();
+
+ // Dream entry animations finished.
+ when(mDreamOverlayStateController.areEntryAnimationsFinished()).thenReturn(true);
+ final DreamOverlayStateController.Callback stateCallback = captureOverlayStateCallback();
+ stateCallback.onStateChanged();
+
+ // Add a complication after entry animations are finished.
+ final HashSet<ComplicationViewModel> complications = new HashSet<>(
+ Collections.singletonList(mComplicationViewModel));
+ observer.onChanged(complications);
+
+ // The complication view should not be set to invisible.
+ verify(mComplicationView, never()).setVisibility(View.INVISIBLE);
+ }
+
+ private Observer<Collection<ComplicationViewModel>> captureComplicationViewModelsObserver() {
+ verify(mComplicationViewModelLiveData).observe(eq(mLifecycleOwner),
+ mObserverCaptor.capture());
+ return mObserverCaptor.getValue();
+ }
+
+ private DreamOverlayStateController.Callback captureOverlayStateCallback() {
+ verify(mDreamOverlayStateController).addCallback(mCallbackCaptor.capture());
+ return mCallbackCaptor.getValue();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.java
deleted file mode 100644
index 640e6dc..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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
- *
- * 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;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-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.annotation.UserIdInt;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.os.UserManager;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * runtest systemui -c com.android.systemui.keyguard.WorkLockActivityTest
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class WorkLockActivityTest extends SysuiTestCase {
- private static final @UserIdInt int USER_ID = 270;
- private static final String CALLING_PACKAGE_NAME = "com.android.test";
-
- private @Mock UserManager mUserManager;
- private @Mock PackageManager mPackageManager;
- private @Mock Context mContext;
- private @Mock BroadcastDispatcher mBroadcastDispatcher;
- private @Mock Drawable mDrawable;
- private @Mock Drawable mBadgedDrawable;
-
- private WorkLockActivity mActivity;
-
- private static class WorkLockActivityTestable extends WorkLockActivity {
- WorkLockActivityTestable(Context baseContext, BroadcastDispatcher broadcastDispatcher,
- UserManager userManager, PackageManager packageManager) {
- super(broadcastDispatcher, userManager, packageManager);
- attachBaseContext(baseContext);
- }
- }
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- mActivity = new WorkLockActivityTestable(mContext, mBroadcastDispatcher, mUserManager,
- mPackageManager);
- }
-
- @Test
- public void testGetBadgedIcon() throws Exception {
- ApplicationInfo info = new ApplicationInfo();
- when(mPackageManager.getApplicationInfoAsUser(eq(CALLING_PACKAGE_NAME), any(),
- eq(USER_ID))).thenReturn(info);
- when(mPackageManager.getApplicationIcon(eq(info))).thenReturn(mDrawable);
- when(mUserManager.getBadgedIconForUser(any(), eq(UserHandle.of(USER_ID)))).thenReturn(
- mBadgedDrawable);
- mActivity.setIntent(new Intent()
- .putExtra(Intent.EXTRA_USER_ID, USER_ID)
- .putExtra(Intent.EXTRA_PACKAGE_NAME, CALLING_PACKAGE_NAME));
-
- assertEquals(mBadgedDrawable, mActivity.getBadgedIcon());
- }
-
- @Test
- public void testUnregisteredFromDispatcher() {
- mActivity.unregisterBroadcastReceiver();
- verify(mBroadcastDispatcher).unregisterReceiver(any());
- verify(mContext, never()).unregisterReceiver(any());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.kt
new file mode 100644
index 0000000..c7f1dec
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.kt
@@ -0,0 +1,125 @@
+/*
+ * 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
+ *
+ * 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
+
+import android.annotation.UserIdInt
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ApplicationInfoFlags
+import android.graphics.drawable.Drawable
+import android.os.Looper
+import android.os.UserHandle
+import android.os.UserManager
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+/** runtest systemui -c com.android.systemui.keyguard.WorkLockActivityTest */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class WorkLockActivityTest : SysuiTestCase() {
+ private val context: Context = mock()
+ private val userManager: UserManager = mock()
+ private val packageManager: PackageManager = mock()
+ private val broadcastDispatcher: BroadcastDispatcher = mock()
+ private val drawable: Drawable = mock()
+ private val badgedDrawable: Drawable = mock()
+ private lateinit var activity: WorkLockActivity
+
+ private class WorkLockActivityTestable
+ constructor(
+ baseContext: Context,
+ broadcastDispatcher: BroadcastDispatcher,
+ userManager: UserManager,
+ packageManager: PackageManager,
+ ) : WorkLockActivity(broadcastDispatcher, userManager, packageManager) {
+ init {
+ attachBaseContext(baseContext)
+ }
+ }
+
+ @Before
+ fun setUp() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare()
+ }
+ activity =
+ WorkLockActivityTestable(
+ baseContext = context,
+ broadcastDispatcher = broadcastDispatcher,
+ userManager = userManager,
+ packageManager = packageManager
+ )
+ }
+
+ @Test
+ fun testGetBadgedIcon() {
+ val info = ApplicationInfo()
+ whenever(
+ packageManager.getApplicationInfoAsUser(
+ eq(CALLING_PACKAGE_NAME),
+ any<ApplicationInfoFlags>(),
+ eq(USER_ID)
+ )
+ )
+ .thenReturn(info)
+ whenever(packageManager.getApplicationIcon(ArgumentMatchers.eq(info))).thenReturn(drawable)
+ whenever(userManager.getBadgedIconForUser(any(), eq(UserHandle.of(USER_ID))))
+ .thenReturn(badgedDrawable)
+ activity.intent =
+ Intent()
+ .putExtra(Intent.EXTRA_USER_ID, USER_ID)
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, CALLING_PACKAGE_NAME)
+ assertEquals(badgedDrawable, activity.badgedIcon)
+ }
+
+ @Test
+ fun testUnregisteredFromDispatcher() {
+ activity.unregisterBroadcastReceiver()
+ verify(broadcastDispatcher).unregisterReceiver(any())
+ verify(context, never()).unregisterReceiver(nullable())
+ }
+
+ @Test
+ fun onBackPressed_finishActivity() {
+ assertFalse(activity.isFinishing)
+
+ activity.onBackPressed()
+
+ assertFalse(activity.isFinishing)
+ }
+
+ companion object {
+ @UserIdInt private val USER_ID = 270
+ private const val CALLING_PACKAGE_NAME = "com.android.test"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index 4084cf4..3ae8428 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -42,6 +42,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -52,6 +53,7 @@
import java.util.List;
+@Ignore("b/257089187")
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index f96c39f..aa1114b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -34,6 +34,7 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback
+import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider
import com.android.systemui.statusbar.notification.collection.render.NodeController
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider
@@ -86,6 +87,7 @@
private val mRemoteInputManager: NotificationRemoteInputManager = mock()
private val mEndLifetimeExtension: OnEndLifetimeExtensionCallback = mock()
private val mHeaderController: NodeController = mock()
+ private val mLaunchFullScreenIntentProvider: LaunchFullScreenIntentProvider = mock()
private lateinit var mEntry: NotificationEntry
private lateinit var mGroupSummary: NotificationEntry
@@ -110,6 +112,7 @@
mHeadsUpViewBinder,
mNotificationInterruptStateProvider,
mRemoteInputManager,
+ mLaunchFullScreenIntentProvider,
mHeaderController,
mExecutor)
mCoordinator.attach(mNotifPipeline)
@@ -242,6 +245,20 @@
}
@Test
+ fun testOnEntryAdded_shouldFullScreen() {
+ setShouldFullScreen(mEntry)
+ mCollectionListener.onEntryAdded(mEntry)
+ verify(mLaunchFullScreenIntentProvider).launchFullScreenIntent(mEntry)
+ }
+
+ @Test
+ fun testOnEntryAdded_shouldNotFullScreen() {
+ setShouldFullScreen(mEntry, should = false)
+ mCollectionListener.onEntryAdded(mEntry)
+ verify(mLaunchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
+ }
+
+ @Test
fun testPromotesAddedHUN() {
// GIVEN the current entry should heads up
whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true)
@@ -794,6 +811,11 @@
.thenReturn(should)
}
+ private fun setShouldFullScreen(entry: NotificationEntry, should: Boolean = true) {
+ whenever(mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+ .thenReturn(should)
+ }
+
private fun finishBind(entry: NotificationEntry) {
verify(mHeadsUpManager, never()).showNotification(entry)
withArgCaptor<BindCallback> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
index 9c56c26..6fb6893 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
@@ -45,7 +45,7 @@
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
-import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel;
+import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.Before;
@@ -80,7 +80,7 @@
layout,
StatusBarLocation.HOME,
mock(StatusBarPipelineFlags.class),
- mock(WifiViewModel.class),
+ mock(WifiUiAdapter.class),
mock(MobileUiAdapter.class),
mMobileContextProvider,
mock(DarkIconDispatcher.class));
@@ -124,14 +124,14 @@
LinearLayout group,
StatusBarLocation location,
StatusBarPipelineFlags statusBarPipelineFlags,
- WifiViewModel wifiViewModel,
+ WifiUiAdapter wifiUiAdapter,
MobileUiAdapter mobileUiAdapter,
MobileContextProvider contextProvider,
DarkIconDispatcher darkIconDispatcher) {
super(group,
location,
statusBarPipelineFlags,
- wifiViewModel,
+ wifiUiAdapter,
mobileUiAdapter,
contextProvider,
darkIconDispatcher);
@@ -172,7 +172,7 @@
super(group,
StatusBarLocation.HOME,
mock(StatusBarPipelineFlags.class),
- mock(WifiViewModel.class),
+ mock(WifiUiAdapter.class),
mock(MobileUiAdapter.class),
contextProvider);
}
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 c409857..ce54d78 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
@@ -67,8 +67,8 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -123,8 +123,6 @@
@Mock
private ShadeControllerImpl mShadeController;
@Mock
- private NotifPipeline mNotifPipeline;
- @Mock
private NotificationVisibilityProvider mVisibilityProvider;
@Mock
private ActivityIntentHelper mActivityIntentHelper;
@@ -197,7 +195,6 @@
getContext(),
mHandler,
mUiBgExecutor,
- mNotifPipeline,
mVisibilityProvider,
headsUpManager,
mActivityStarter,
@@ -222,7 +219,8 @@
mock(NotificationPresenter.class),
mock(NotificationPanelViewController.class),
mActivityLaunchAnimator,
- notificationAnimationProvider
+ notificationAnimationProvider,
+ mock(LaunchFullScreenIntentProvider.class)
);
// set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg
@@ -384,11 +382,9 @@
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.getImportance()).thenReturn(NotificationManager.IMPORTANCE_HIGH);
when(entry.getSbn()).thenReturn(sbn);
- when(mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(eq(entry)))
- .thenReturn(true);
// WHEN
- mNotificationActivityStarter.handleFullScreenIntent(entry);
+ mNotificationActivityStarter.launchFullScreenIntent(entry);
// THEN display should try wake up for the full screen intent
verify(mCentralSurfaces).wakeUpForFullScreenIntent();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index c584109..37457b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -28,7 +28,6 @@
import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON
-import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
@@ -40,6 +39,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
+import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
@@ -70,7 +70,7 @@
private lateinit var connectivityRepository: FakeConnectivityRepository
private lateinit var wifiRepository: FakeWifiRepository
private lateinit var interactor: WifiInteractor
- private lateinit var viewModel: WifiViewModel
+ private lateinit var viewModel: LocationBasedWifiViewModel
private lateinit var scope: CoroutineScope
private lateinit var airplaneModeViewModel: AirplaneModeViewModel
@@ -105,23 +105,19 @@
scope,
statusBarPipelineFlags,
wifiConstants,
- )
+ ).home
}
@Test
fun constructAndBind_hasCorrectSlot() {
- val view = ModernStatusBarWifiView.constructAndBind(
- context, "slotName", viewModel, StatusBarLocation.HOME
- )
+ val view = ModernStatusBarWifiView.constructAndBind(context, "slotName", viewModel)
assertThat(view.slot).isEqualTo("slotName")
}
@Test
fun getVisibleState_icon_returnsIcon() {
- val view = ModernStatusBarWifiView.constructAndBind(
- context, SLOT_NAME, viewModel, StatusBarLocation.HOME
- )
+ val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
view.setVisibleState(STATE_ICON, /* animate= */ false)
@@ -130,9 +126,7 @@
@Test
fun getVisibleState_dot_returnsDot() {
- val view = ModernStatusBarWifiView.constructAndBind(
- context, SLOT_NAME, viewModel, StatusBarLocation.HOME
- )
+ val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
view.setVisibleState(STATE_DOT, /* animate= */ false)
@@ -141,9 +135,7 @@
@Test
fun getVisibleState_hidden_returnsHidden() {
- val view = ModernStatusBarWifiView.constructAndBind(
- context, SLOT_NAME, viewModel, StatusBarLocation.HOME
- )
+ val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
view.setVisibleState(STATE_HIDDEN, /* animate= */ false)
@@ -155,9 +147,7 @@
@Test
fun setVisibleState_icon_iconShownDotHidden() {
- val view = ModernStatusBarWifiView.constructAndBind(
- context, SLOT_NAME, viewModel, StatusBarLocation.HOME
- )
+ val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
view.setVisibleState(STATE_ICON, /* animate= */ false)
@@ -172,9 +162,7 @@
@Test
fun setVisibleState_dot_iconHiddenDotShown() {
- val view = ModernStatusBarWifiView.constructAndBind(
- context, SLOT_NAME, viewModel, StatusBarLocation.HOME
- )
+ val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
view.setVisibleState(STATE_DOT, /* animate= */ false)
@@ -189,9 +177,7 @@
@Test
fun setVisibleState_hidden_iconAndDotHidden() {
- val view = ModernStatusBarWifiView.constructAndBind(
- context, SLOT_NAME, viewModel, StatusBarLocation.HOME
- )
+ val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
view.setVisibleState(STATE_HIDDEN, /* animate= */ false)
@@ -211,9 +197,7 @@
WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 2)
)
- val view = ModernStatusBarWifiView.constructAndBind(
- context, SLOT_NAME, viewModel, StatusBarLocation.HOME
- )
+ val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
ViewUtils.attachView(view)
testableLooper.processAllMessages()
@@ -230,9 +214,7 @@
WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 2)
)
- val view = ModernStatusBarWifiView.constructAndBind(
- context, SLOT_NAME, viewModel, StatusBarLocation.HOME
- )
+ val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
ViewUtils.attachView(view)
testableLooper.processAllMessages()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
index 4f1fb02..26df03f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -25,6 +26,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.net.TetheringManager;
@@ -36,6 +38,7 @@
import androidx.test.filters.SmallTest;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
@@ -96,6 +99,9 @@
}).when(mWifiManager).registerSoftApCallback(any(Executor.class),
any(WifiManager.SoftApCallback.class));
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_show_wifi_tethering, true);
+
Handler handler = new Handler(mLooper.getLooper());
mController = new HotspotControllerImpl(mContext, handler, handler, mDumpManager);
@@ -176,4 +182,18 @@
verify(mCallback1).onHotspotAvailabilityChanged(false);
}
+
+ @Test
+ public void testHotspotSupported_resource_false() {
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_show_wifi_tethering, false);
+
+ Handler handler = new Handler(mLooper.getLooper());
+
+ HotspotController controller =
+ new HotspotControllerImpl(mContext, handler, handler, mDumpManager);
+
+ verifyNoMoreInteractions(mTetheringManager);
+ assertFalse(controller.isHotspotSupported());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
index e496521..fb781e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
@@ -23,6 +23,8 @@
import android.os.UserManager
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.GuestResetOrExitSessionReceiver
+import com.android.systemui.GuestResumeSessionReceiver
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.user.data.repository.FakeUserRepository
@@ -55,6 +57,8 @@
@Mock private lateinit var dismissDialog: () -> Unit
@Mock private lateinit var selectUser: (Int) -> Unit
@Mock private lateinit var switchUser: (Int) -> Unit
+ @Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
+ @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
private lateinit var underTest: GuestUserInteractor
@@ -87,10 +91,18 @@
repository = repository,
),
uiEventLogger = uiEventLogger,
+ resumeSessionReceiver = resumeSessionReceiver,
+ resetOrExitSessionReceiver = resetOrExitSessionReceiver,
)
}
@Test
+ fun `registers broadcast receivers`() {
+ verify(resumeSessionReceiver).register()
+ verify(resetOrExitSessionReceiver).register()
+ }
+
+ @Test
fun `onDeviceBootCompleted - allowed to add - create guest`() =
runBlocking(IMMEDIATE) {
setAllowedToAdd()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index 1680c36c..58f5531 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -21,6 +21,8 @@
import android.app.admin.DevicePolicyManager
import android.os.UserManager
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.GuestResetOrExitSessionReceiver
+import com.android.systemui.GuestResumeSessionReceiver
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -48,6 +50,8 @@
@Mock protected lateinit var devicePolicyManager: DevicePolicyManager
@Mock protected lateinit var uiEventLogger: UiEventLogger
@Mock protected lateinit var dialogShower: UserSwitchDialogController.DialogShower
+ @Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
+ @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
protected lateinit var underTest: UserInteractor
@@ -107,6 +111,8 @@
devicePolicyManager = devicePolicyManager,
refreshUsersScheduler = refreshUsersScheduler,
uiEventLogger = uiEventLogger,
+ resumeSessionReceiver = resumeSessionReceiver,
+ resetOrExitSessionReceiver = resetOrExitSessionReceiver,
)
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index c12a868..116023a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -23,6 +23,8 @@
import android.os.UserManager
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.GuestResetOrExitSessionReceiver
+import com.android.systemui.GuestResumeSessionReceiver
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Text
import com.android.systemui.flags.FakeFeatureFlags
@@ -69,6 +71,8 @@
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var uiEventLogger: UiEventLogger
+ @Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
+ @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
private lateinit var underTest: UserSwitcherViewModel
@@ -104,6 +108,8 @@
devicePolicyManager = devicePolicyManager,
refreshUsersScheduler = refreshUsersScheduler,
uiEventLogger = uiEventLogger,
+ resumeSessionReceiver = resumeSessionReceiver,
+ resetOrExitSessionReceiver = resetOrExitSessionReceiver,
)
underTest =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
index 23c7a61..2d6d29a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
@@ -62,7 +62,11 @@
}
@Override
- public void setSignalIcon(String slot, WifiIconState state) {
+ public void setWifiIcon(String slot, WifiIconState state) {
+ }
+
+ @Override
+ public void setNewWifiIcon() {
}
@Override
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f8a6462..4fda233 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -41,6 +41,7 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.ActivityThread;
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -153,6 +154,7 @@
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorManager;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.System;
import android.service.notification.ZenModeConfig;
@@ -174,6 +176,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
@@ -233,6 +236,7 @@
AudioSystemAdapter.OnVolRangeInitRequestListener {
private static final String TAG = "AS.AudioService";
+ private static final boolean CONFIG_DEFAULT_VAL = false;
private final AudioSystemAdapter mAudioSystem;
private final SystemServerAdapter mSystemServer;
@@ -987,6 +991,7 @@
* @param looper Looper to use for the service's message handler. If this is null, an
* {@link AudioSystemThread} is created as the messaging thread instead.
*/
+ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
public AudioService(Context context, AudioSystemAdapter audioSystem,
SystemServerAdapter systemServer, SettingsAdapter settings, @Nullable Looper looper,
AppOpsManager appOps) {
@@ -1026,8 +1031,12 @@
mUseVolumeGroupAliases = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_handleVolumeAliasesUsingVolumeGroups);
- mNotifAliasRing = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_alias_ring_notif_stream_types);
+ mNotifAliasRing = !DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false);
+
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ ActivityThread.currentApplication().getMainExecutor(),
+ this::onDeviceConfigChange);
// Initialize volume
// Priority 1 - Android Property
@@ -1246,6 +1255,22 @@
}
/**
+ * Separating notification volume from ring is NOT of aliasing the corresponding streams
+ * @param properties
+ */
+ private void onDeviceConfigChange(DeviceConfig.Properties properties) {
+ Set<String> changeSet = properties.getKeyset();
+ if (changeSet.contains(SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION)) {
+ boolean newNotifAliasRing = !properties.getBoolean(
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL);
+ if (mNotifAliasRing != newNotifAliasRing) {
+ mNotifAliasRing = newNotifAliasRing;
+ updateStreamVolumeAlias(true, TAG);
+ }
+ }
+ }
+
+ /**
* Called by handling of MSG_INIT_STREAMS_VOLUMES
*/
private void onInitStreamsAndVolumes() {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index c671a2c..6a000d9 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -2928,7 +2928,6 @@
ikeConfiguration.isIkeExtensionEnabled(
IkeSessionConfiguration.EXTENSION_TYPE_MOBIKE);
onIkeConnectionInfoChanged(token, ikeConfiguration.getIkeSessionConnectionInfo());
- mRetryCount = 0;
}
/**
@@ -3048,6 +3047,7 @@
}
doSendLinkProperties(networkAgent, lp);
+ mRetryCount = 0;
} catch (Exception e) {
Log.d(TAG, "Error in ChildOpened for token " + token, e);
onSessionLost(token, e);
@@ -3345,6 +3345,10 @@
}
private void scheduleRetryNewIkeSession() {
+ if (mScheduledHandleRetryIkeSessionFuture != null) {
+ Log.d(TAG, "There is a pending retrying task, skip the new retrying task");
+ return;
+ }
final long retryDelay = mDeps.getNextRetryDelaySeconds(mRetryCount++);
Log.d(TAG, "Retry new IKE session after " + retryDelay + " seconds.");
// If the default network is lost during the retry delay, the mActiveNetwork will be
diff --git a/services/core/java/com/android/server/pm/dex/OdsignStatsLogger.java b/services/core/java/com/android/server/pm/dex/OdsignStatsLogger.java
index fa08add..227a3a1 100644
--- a/services/core/java/com/android/server/pm/dex/OdsignStatsLogger.java
+++ b/services/core/java/com/android/server/pm/dex/OdsignStatsLogger.java
@@ -39,6 +39,7 @@
// These need to be kept in sync with system/security/ondevice-signing/StatsReporter.{h, cpp}.
private static final String METRICS_FILE = "/data/misc/odsign/metrics/odsign-metrics.txt";
private static final String COMPOS_METRIC_NAME = "comp_os_artifacts_check_record";
+ private static final String ODSIGN_METRIC_NAME = "odsign_record";
/**
* Arrange for stats to be uploaded in the background.
@@ -64,18 +65,45 @@
for (String line : lines.split("\n")) {
String[] metrics = line.split(" ");
- if (metrics.length != 4 || !metrics[0].equals(COMPOS_METRIC_NAME)) {
- Slog.w(TAG, "Malformed metrics file");
- break;
+ if (line.isEmpty() || metrics.length < 1) {
+ Slog.w(TAG, "Empty metrics line");
+ continue;
}
- boolean currentArtifactsOk = metrics[1].equals("1");
- boolean compOsPendingArtifactsExists = metrics[2].equals("1");
- boolean useCompOsGeneratedArtifacts = metrics[3].equals("1");
+ switch (metrics[0]) {
+ case COMPOS_METRIC_NAME: {
+ if (metrics.length != 4) {
+ Slog.w(TAG, "Malformed CompOS metrics line '" + line + "'");
+ continue;
+ }
- ArtStatsLog.write(ArtStatsLog.EARLY_BOOT_COMP_OS_ARTIFACTS_CHECK_REPORTED,
- currentArtifactsOk, compOsPendingArtifactsExists,
- useCompOsGeneratedArtifacts);
+ boolean currentArtifactsOk = metrics[1].equals("1");
+ boolean compOsPendingArtifactsExists = metrics[2].equals("1");
+ boolean useCompOsGeneratedArtifacts = metrics[3].equals("1");
+
+ ArtStatsLog.write(ArtStatsLog.EARLY_BOOT_COMP_OS_ARTIFACTS_CHECK_REPORTED,
+ currentArtifactsOk, compOsPendingArtifactsExists,
+ useCompOsGeneratedArtifacts);
+ break;
+ }
+ case ODSIGN_METRIC_NAME: {
+ if (metrics.length != 2) {
+ Slog.w(TAG, "Malformed odsign metrics line '" + line + "'");
+ continue;
+ }
+
+ try {
+ int status = Integer.parseInt(metrics[1]);
+ ArtStatsLog.write(ArtStatsLog.ODSIGN_REPORTED, status);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Malformed odsign metrics line '" + line + "'");
+ }
+
+ break;
+ }
+ default:
+ Slog.w(TAG, "Malformed metrics line '" + line + "'");
+ }
}
} catch (FileNotFoundException e) {
// This is normal and probably means no new metrics have been generated.
diff --git a/services/core/java/com/android/server/rollback/README.md b/services/core/java/com/android/server/rollback/README.md
index 0c5cc15..08800da 100644
--- a/services/core/java/com/android/server/rollback/README.md
+++ b/services/core/java/com/android/server/rollback/README.md
@@ -1,4 +1,4 @@
-#Rollback Manager
+# Rollback Manager
## Introduction
@@ -7,9 +7,9 @@
APEX update to the previous version installed on the device, and reverting any
APK or APEX data to the state it was in at the time of install.
-##Rollback Basics
+## Rollback Basics
-###How Rollbacks Work
+### How Rollbacks Work
A new install parameter ENABLE_ROLLBACK can be specified to enable rollback when
updating an application. For example:
@@ -42,27 +42,27 @@
See below for more details of shell commands for rollback.
-###Rollback Triggers
+### Rollback Triggers
-####Manually Triggered Rollback
+#### Manually Triggered Rollback
As mentioned above, it is possible to trigger rollback on device using a shell
command. This is for testing purposes only. We do not expect this mechanism to
be used in production in practice.
-####Watchdog Triggered Rollback
+#### Watchdog Triggered Rollback
Watchdog triggered rollback is intended to address severe issues with the
device. The platform provides several different watchdogs that can trigger
rollback.
-#####Package Watchdog
+##### Package Watchdog
There is a package watchdog service running on device that will trigger rollback
of an update if there are 5 ANRs or process crashes within a 1 minute window for
a package in the update.
-#####Native Watchdog
+##### Native Watchdog
If a native service crashes repeatedly after an update is installed, rollback
will be triggered. This particularly applies to updates that include APEXes
@@ -70,25 +70,25 @@
native services have been affected by an update, *any* crashing native service
will cause the rollback to be triggered.
-#####Explicit Health Check
+##### Explicit Health Check
There is an explicit check to verify the network stack is functional after an
update. If there is no network connectivity within a certain time period after
an update, rollback is triggered.
-####Server Triggered Rollback
+#### Server Triggered Rollback
The RollbackManager API may be used by the installer to roll back an update
based on a request from the server.
-##Rollback Details
+## Rollback Details
-###RollbackManager API
+### RollbackManager API
The RollbackManager API is an @SystemAPI guarded by the MANAGE_ROLLBACKS and
TEST_MANAGE_ROLLBACKS permissions. See RollbackManager.java for details about
the RollbackManager API.
-###Rollback of APEX modules
+### Rollback of APEX modules
Rollback is supported for APEX modules in addition to APK modules. In Q, there
was no concept of data associated with an APEX, so only the APEX itself is
@@ -100,7 +100,7 @@
directories). For example, FooV2.apex must not change the file format of some
state stored on the device in such a way that FooV1.apex cannot read the file.
-###Rollback of MultiPackage Installs
+### Rollback of MultiPackage Installs
Rollback can be enabled for multi-package installs. This requires that all
packages in the install session, including the parent session, have the
@@ -119,7 +119,7 @@
install session, rollback will not be enabled for any package in the
multi-package install session.
-###Rollback of Staged Installs
+### Rollback of Staged Installs
Rollback can be enabled for staged installs, which require reboot to take
effect. If reboot was required when the package was updated, then reboot is
@@ -127,21 +127,21 @@
package was updated, then no reboot is required when the package is rolled back.
-###Rollbacks on Multi User Devices
+### Rollbacks on Multi User Devices
Rollbacks should work properly on devices with multiple users. There is special
handling of user data backup to ensure app user data is properly backed up and
restored for all users, even for credential encrypted users that have not been
unlocked at various points during the flow.
-###Rollback whitelist
+### Rollback whitelist
Outside of testing, rollback may only be enabled for packages listed in the
sysconfig rollback whitelist - see
`SystemConfig#getRollbackWhitelistedPackages`. Attempts to enable rollback for
non-whitelisted packages will fail.
-###Failure to Enable Rollback
+### Failure to Enable Rollback
There are a number of reasons why we may be unable to enable rollback for a
package, including:
@@ -158,13 +158,13 @@
rollback enabled. Failing to enable rollback does not cause the installation to
fail.
-###Failure to Commit Rollback
+### Failure to Commit Rollback
For the most part, a rollback will remain available after failure to commit it.
This allows the caller to retry the rollback if they have reason to believe it
will not fail again the next time the commit of the rollback is attempted.
-###Installing Previously Rolled Back Packages
+### Installing Previously Rolled Back Packages
There is no logic in the platform itself to prevent installing a version of a
package that was previously rolled back.
@@ -175,7 +175,7 @@
installer to prevent reinstall of a previously rolled back package version if so
desired.
-###Rollback Expiration
+### Rollback Expiration
An available rollback is expired if the rollback lifetime has been exceeded or
if there is a new update to package associated with the rollback. When an
@@ -183,9 +183,9 @@
the rollback are deleted. Once a rollback is expired, it can no longer be
executed.
-##Shell Commands for Rollback
+## Shell Commands for Rollback
-###Installing an App with Rollback Enabled
+### Installing an App with Rollback Enabled
The `adb install` command accepts the `--enable-rollback` flag to install an app
with rollback enabled. For example:
@@ -194,7 +194,7 @@
$ adb install --enable-rollback FooV2.apk
```
-###Triggering Rollback Manually
+### Triggering Rollback Manually
If rollback is available for an application, the pm command can be used to
trigger rollback manually on device:
@@ -206,7 +206,7 @@
For rollback of staged installs, you have to manually reboot the device for the
rollback to take effect after running the 'pm rollback-app' command.
-###Listing the Status of Rollbacks on Device
+### Listing the Status of Rollbacks on Device
You can get a list with details about available and recently committed rollbacks
using dumpsys. For example:
@@ -246,9 +246,9 @@
The list of rollbacks is also included in bug reports. Search for "DUMP OF
SERVICE rollback".
-##Configuration Properties
+## Configuration Properties
-###Rollback Lifetime
+### Rollback Lifetime
Rollback lifetime refers to the maximum duration of time after the rollback is
first enabled that it will be available. The default is for rollbacks to be
@@ -263,7 +263,7 @@
The update will not take effect until after system server has been restarted.
-###Enable Rollback Timeout
+### Enable Rollback Timeout
The enable rollback timeout is how long RollbackManager is allowed to take to
enable rollback when performing an update. This includes the time needed to make
@@ -279,7 +279,7 @@
The update will take effect for the next install with rollback enabled.
-##Limitations
+## Limitations
* You cannot enable rollback for the first version of an application installed
on the device. Only updates to a package previously installed on the device can
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a9d8d79..0eee8a3 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -496,7 +496,7 @@
/** The most recently given options. */
private ActivityOptions mPendingOptions;
/** Non-null if {@link #mPendingOptions} specifies the remote animation. */
- private RemoteAnimationAdapter mPendingRemoteAnimation;
+ RemoteAnimationAdapter mPendingRemoteAnimation;
private RemoteTransition mPendingRemoteTransition;
ActivityOptions returningOptions; // options that are coming back via convertToTranslucent
AppTimeTracker appTimeTracker; // set if we are tracking the time in this app/task/activity
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 1d70146..f070064 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2643,10 +2643,14 @@
}
}
- // Update the target's launch cookie to those specified in the options if set
+ // Update the target's launch cookie and pending remote animation to those specified in the
+ // options if set.
if (mStartActivity.mLaunchCookie != null) {
intentActivity.mLaunchCookie = mStartActivity.mLaunchCookie;
}
+ if (mStartActivity.mPendingRemoteAnimation != null) {
+ intentActivity.mPendingRemoteAnimation = mStartActivity.mPendingRemoteAnimation;
+ }
// Need to update mTargetRootTask because if task was moved out of it, the original root
// task may be destroyed.
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index d1bda22..92d19b0 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -294,8 +294,8 @@
final int flags = appTransition.getTransitFlags();
layoutRedo = appTransition.goodToGo(transit, topOpeningApp);
appTransition.postAnimationCallback();
- appTransition.clear();
} finally {
+ appTransition.clear();
mService.mSurfaceAnimationRunner.continueStartingAnimations();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 2b0e76c..053718e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -98,6 +98,7 @@
import android.util.Pair;
import android.util.Size;
import android.view.Gravity;
+import android.view.RemoteAnimationAdapter;
import android.window.TaskFragmentOrganizerToken;
import androidx.test.filters.SmallTest;
@@ -1323,6 +1324,32 @@
}
@Test
+ public void testRemoteAnimation_appliesToExistingTask() {
+ final ActivityStarter starter = prepareStarter(0, false);
+
+ // Put an activity on default display as the top focused activity.
+ ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Intent intent = new Intent();
+ intent.setComponent(ActivityBuilder.getDefaultComponent());
+ starter.setReason("testRemoteAnimation_newTask")
+ .setIntent(intent)
+ .execute();
+
+ assertNull(mRootWindowContainer.topRunningActivity().mPendingRemoteAnimation);
+
+ // Relaunch the activity with remote animation indicated in options.
+ final RemoteAnimationAdapter adaptor = mock(RemoteAnimationAdapter.class);
+ final ActivityOptions options = ActivityOptions.makeRemoteAnimation(adaptor);
+ starter.setReason("testRemoteAnimation_existingTask")
+ .setIntent(intent)
+ .setActivityOptions(options.toBundle())
+ .execute();
+
+ // Verify the remote animation is updated.
+ assertEquals(adaptor, mRootWindowContainer.topRunningActivity().mPendingRemoteAnimation);
+ }
+
+ @Test
public void testStartLaunchIntoPipActivity() {
final ActivityStarter starter = prepareStarter(0, false);