Merge "Allow separate configuration of display color fade" into main
diff --git a/apct-tests/perftests/core/src/android/os/OWNERS b/apct-tests/perftests/core/src/android/os/OWNERS
index a1719c9..76ab303 100644
--- a/apct-tests/perftests/core/src/android/os/OWNERS
+++ b/apct-tests/perftests/core/src/android/os/OWNERS
@@ -1 +1,4 @@
-per-file PackageParsingPerfTest.kt = file:/services/core/java/com/android/server/pm/OWNERS
\ No newline at end of file
+per-file PackageParsingPerfTest.kt = file:/services/core/java/com/android/server/pm/OWNERS
+
+# Bug component: 345036
+per-file VibratorPerfTest.java = file:/services/core/java/com/android/server/vibrator/OWNERS
\ No newline at end of file
diff --git a/cmds/appops/OWNERS b/cmds/appops/OWNERS
index 999ea0e..2fe78c5 100644
--- a/cmds/appops/OWNERS
+++ b/cmds/appops/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
include /core/java/android/permission/OWNERS
diff --git a/core/api/current.txt b/core/api/current.txt
index ff011cf..eaefe84 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9694,6 +9694,7 @@
method public int describeContents();
method public void enforceCallingUid();
method @Nullable public String getAttributionTag();
+ method public int getDeviceId();
method @Nullable public android.content.AttributionSource getNext();
method @Nullable public String getPackageName();
method public int getPid();
@@ -9709,6 +9710,7 @@
ctor public AttributionSource.Builder(@NonNull android.content.AttributionSource);
method @NonNull public android.content.AttributionSource build();
method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@Nullable String);
+ method @NonNull public android.content.AttributionSource.Builder setDeviceId(int);
method @Deprecated @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource);
method @NonNull public android.content.AttributionSource.Builder setNextAttributionSource(@NonNull android.content.AttributionSource);
method @NonNull public android.content.AttributionSource.Builder setPackageName(@Nullable String);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index cf26e12..083c445 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -850,6 +850,7 @@
ctor public AttributionSource(int, @Nullable String, @Nullable String, @NonNull android.os.IBinder);
ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable java.util.Set<java.lang.String>, @Nullable android.content.AttributionSource);
ctor public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], @Nullable android.content.AttributionSource);
+ ctor public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], int, @Nullable android.content.AttributionSource);
method public void enforceCallingPid();
}
@@ -4235,6 +4236,7 @@
field public final boolean isTrustedOverlay;
field public final boolean isVisible;
field @NonNull public final String name;
+ field @NonNull public final android.graphics.Matrix transform;
field @NonNull public final android.os.IBinder windowToken;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 853528f..12ffdb3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -65,6 +65,7 @@
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.TransactionExecutor;
import android.app.servertransaction.TransactionExecutorHelper;
+import android.app.servertransaction.WindowTokenClientController;
import android.bluetooth.BluetoothFrameworkInitializer;
import android.companion.virtual.VirtualDeviceManager;
import android.compat.annotation.UnsupportedAppUsage;
@@ -6221,6 +6222,18 @@
false /* clearPending */);
}
+ @Override
+ public void handleWindowContextConfigurationChanged(@NonNull IBinder clientToken,
+ @NonNull Configuration configuration, int displayId) {
+ WindowTokenClientController.getInstance().onWindowContextConfigurationChanged(clientToken,
+ configuration, displayId);
+ }
+
+ @Override
+ public void handleWindowContextWindowRemoval(@NonNull IBinder clientToken) {
+ WindowTokenClientController.getInstance().onWindowContextWindowRemoved(clientToken);
+ }
+
/**
* Sends windowing mode change callbacks to {@link Activity} if applicable.
*
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 49fb794..f7a43f4 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -163,6 +163,13 @@
public abstract void handleActivityConfigurationChanged(@NonNull ActivityClientRecord r,
Configuration overrideConfig, int displayId);
+ /** Deliver {@link android.window.WindowContext} configuration change. */
+ public abstract void handleWindowContextConfigurationChanged(@NonNull IBinder clientToken,
+ @NonNull Configuration configuration, int displayId);
+
+ /** Deliver {@link android.window.WindowContext} window removal event. */
+ public abstract void handleWindowContextWindowRemoval(@NonNull IBinder clientToken);
+
/** Deliver result from another activity. */
public abstract void handleSendResult(
@NonNull ActivityClientRecord r, List<ResultInfo> results, String reason);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4eaebe0..1f95497 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3465,7 +3465,7 @@
AttributionSource attributionSource = new AttributionSource(Process.myUid(),
Process.myPid(), mOpPackageName, attributionTag,
(renouncedPermissions != null) ? renouncedPermissions.toArray(new String[0]) : null,
- nextAttributionSource);
+ getDeviceId(), nextAttributionSource);
// If we want to access protected data on behalf of another app we need to
// tell the OS that we opt in to participate in the attribution chain.
if (nextAttributionSource != null) {
diff --git a/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java b/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java
new file mode 100644
index 0000000..3ac642f
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.view.Display.INVALID_DISPLAY;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ClientTransactionHandler;
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+
+import java.util.Objects;
+
+/**
+ * {@link android.window.WindowContext} configuration change message.
+ * @hide
+ */
+public class WindowContextConfigurationChangeItem extends ClientTransactionItem {
+
+ @Nullable
+ private IBinder mClientToken;
+ @Nullable
+ private Configuration mConfiguration;
+ private int mDisplayId;
+
+ @Override
+ public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ @NonNull PendingTransactionActions pendingActions) {
+ client.handleWindowContextConfigurationChanged(mClientToken, mConfiguration, mDisplayId);
+ }
+
+ // ObjectPoolItem implementation
+
+ private WindowContextConfigurationChangeItem() {}
+
+ /** Obtains an instance initialized with provided params. */
+ public static WindowContextConfigurationChangeItem obtain(
+ @NonNull IBinder clientToken, @NonNull Configuration config, int displayId) {
+ WindowContextConfigurationChangeItem instance =
+ ObjectPool.obtain(WindowContextConfigurationChangeItem.class);
+ if (instance == null) {
+ instance = new WindowContextConfigurationChangeItem();
+ }
+ instance.mClientToken = requireNonNull(clientToken);
+ instance.mConfiguration = requireNonNull(config);
+ instance.mDisplayId = displayId;
+
+ return instance;
+ }
+
+ @Override
+ public void recycle() {
+ mClientToken = null;
+ mConfiguration = null;
+ mDisplayId = INVALID_DISPLAY;
+ ObjectPool.recycle(this);
+ }
+
+ // Parcelable implementation
+
+ /** Writes to Parcel. */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStrongBinder(mClientToken);
+ dest.writeTypedObject(mConfiguration, flags);
+ dest.writeInt(mDisplayId);
+ }
+
+ /** Reads from Parcel. */
+ private WindowContextConfigurationChangeItem(@NonNull Parcel in) {
+ mClientToken = in.readStrongBinder();
+ mConfiguration = in.readTypedObject(Configuration.CREATOR);
+ mDisplayId = in.readInt();
+ }
+
+ public static final @NonNull Creator<WindowContextConfigurationChangeItem> CREATOR =
+ new Creator<>() {
+ public WindowContextConfigurationChangeItem createFromParcel(Parcel in) {
+ return new WindowContextConfigurationChangeItem(in);
+ }
+
+ public WindowContextConfigurationChangeItem[] newArray(int size) {
+ return new WindowContextConfigurationChangeItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final WindowContextConfigurationChangeItem other = (WindowContextConfigurationChangeItem) o;
+ return Objects.equals(mClientToken, other.mClientToken)
+ && Objects.equals(mConfiguration, other.mConfiguration)
+ && mDisplayId == other.mDisplayId;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + Objects.hashCode(mClientToken);
+ result = 31 * result + Objects.hashCode(mConfiguration);
+ result = 31 * result + mDisplayId;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "WindowContextConfigurationChangeItem{clientToken=" + mClientToken
+ + ", config=" + mConfiguration
+ + ", displayId=" + mDisplayId
+ + "}";
+ }
+}
diff --git a/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
new file mode 100644
index 0000000..ed52a64
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.os.Parcel;
+
+import java.util.Objects;
+
+/**
+ * {@link android.window.WindowContext} window removal message.
+ * @hide
+ */
+public class WindowContextWindowRemovalItem extends ClientTransactionItem {
+
+ @Nullable
+ private IBinder mClientToken;
+
+ @Override
+ public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ @NonNull PendingTransactionActions pendingActions) {
+ client.handleWindowContextWindowRemoval(mClientToken);
+ }
+
+ // ObjectPoolItem implementation
+
+ private WindowContextWindowRemovalItem() {}
+
+ /** Obtains an instance initialized with provided params. */
+ public static WindowContextWindowRemovalItem obtain(@NonNull IBinder clientToken) {
+ WindowContextWindowRemovalItem instance =
+ ObjectPool.obtain(WindowContextWindowRemovalItem.class);
+ if (instance == null) {
+ instance = new WindowContextWindowRemovalItem();
+ }
+ instance.mClientToken = requireNonNull(clientToken);
+
+ return instance;
+ }
+
+ @Override
+ public void recycle() {
+ mClientToken = null;
+ ObjectPool.recycle(this);
+ }
+
+ // Parcelable implementation
+
+ /** Writes to Parcel. */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStrongBinder(mClientToken);
+ }
+
+ /** Reads from Parcel. */
+ private WindowContextWindowRemovalItem(@NonNull Parcel in) {
+ mClientToken = in.readStrongBinder();
+ }
+
+ public static final @NonNull Creator<WindowContextWindowRemovalItem> CREATOR = new Creator<>() {
+ public WindowContextWindowRemovalItem createFromParcel(Parcel in) {
+ return new WindowContextWindowRemovalItem(in);
+ }
+
+ public WindowContextWindowRemovalItem[] newArray(int size) {
+ return new WindowContextWindowRemovalItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final WindowContextWindowRemovalItem other = (WindowContextWindowRemovalItem) o;
+ return Objects.equals(mClientToken, other.mClientToken);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + Objects.hashCode(mClientToken);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "WindowContextWindowRemovalItem{clientToken=" + mClientToken + "}";
+ }
+}
diff --git a/core/java/android/app/servertransaction/WindowTokenClientController.java b/core/java/android/app/servertransaction/WindowTokenClientController.java
index 28e2040..5d123a0 100644
--- a/core/java/android/app/servertransaction/WindowTokenClientController.java
+++ b/core/java/android/app/servertransaction/WindowTokenClientController.java
@@ -27,6 +27,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
+import android.util.Log;
import android.view.IWindowManager;
import android.window.WindowContext;
import android.window.WindowTokenClient;
@@ -41,6 +42,7 @@
*/
public class WindowTokenClientController {
+ private static final String TAG = WindowTokenClientController.class.getSimpleName();
private static WindowTokenClientController sController;
private final Object mLock = new Object();
@@ -61,7 +63,7 @@
/** Overrides the {@link #getInstance()} for test only. */
@VisibleForTesting
- public static void overrideInstance(@NonNull WindowTokenClientController controller) {
+ public static void overrideForTesting(@NonNull WindowTokenClientController controller) {
synchronized (WindowTokenClientController.class) {
sController = controller;
}
@@ -90,7 +92,7 @@
if (configuration == null) {
return false;
}
- onWindowContainerTokenAttached(client, displayId, configuration);
+ onWindowContextTokenAttached(client, displayId, configuration);
return true;
}
@@ -116,7 +118,7 @@
if (configuration == null) {
return false;
}
- onWindowContainerTokenAttached(client, displayId, configuration);
+ onWindowContextTokenAttached(client, displayId, configuration);
return true;
}
@@ -153,7 +155,7 @@
}
}
- private void onWindowContainerTokenAttached(@NonNull WindowTokenClient client, int displayId,
+ private void onWindowContextTokenAttached(@NonNull WindowTokenClient client, int displayId,
@NonNull Configuration configuration) {
synchronized (mLock) {
mWindowTokenClientMap.put(client.asBinder(), client);
@@ -161,4 +163,33 @@
client.onConfigurationChanged(configuration, displayId,
false /* shouldReportConfigChange */);
}
+
+ /** Called when receives {@link WindowContextConfigurationChangeItem}. */
+ public void onWindowContextConfigurationChanged(@NonNull IBinder clientToken,
+ @NonNull Configuration configuration, int displayId) {
+ final WindowTokenClient windowTokenClient = getWindowTokenClient(clientToken);
+ if (windowTokenClient != null) {
+ windowTokenClient.onConfigurationChanged(configuration, displayId);
+ }
+ }
+
+ /** Called when receives {@link WindowContextWindowRemovalItem}. */
+ public void onWindowContextWindowRemoved(@NonNull IBinder clientToken) {
+ final WindowTokenClient windowTokenClient = getWindowTokenClient(clientToken);
+ if (windowTokenClient != null) {
+ windowTokenClient.onWindowTokenRemoved();
+ }
+ }
+
+ @Nullable
+ private WindowTokenClient getWindowTokenClient(@NonNull IBinder clientToken) {
+ final WindowTokenClient windowTokenClient;
+ synchronized (mLock) {
+ windowTokenClient = mWindowTokenClientMap.get(clientToken);
+ }
+ if (windowTokenClient == null) {
+ Log.w(TAG, "Can't find attached WindowTokenClient for " + clientToken);
+ }
+ return windowTokenClient;
+ }
}
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index eb672dc..b159321 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -48,9 +48,11 @@
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.os.BackgroundThread;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
/**
* Updates AppWidget state; gets information about installed AppWidget providers and other
@@ -785,7 +787,25 @@
return;
}
try {
- mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId);
+ if (RemoteViews.isAdapterConversionEnabled()) {
+ List<CompletableFuture<Void>> updateFutures = new ArrayList<>();
+ for (int i = 0; i < appWidgetIds.length; i++) {
+ final int widgetId = appWidgetIds[i];
+ updateFutures.add(CompletableFuture.runAsync(() -> {
+ try {
+ RemoteViews views = mService.getAppWidgetViews(mPackageName, widgetId);
+ if (views.replaceRemoteCollections(viewId)) {
+ updateAppWidget(widgetId, views);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error notifying changes in RemoteViews", e);
+ }
+ }));
+ }
+ CompletableFuture.allOf(updateFutures.toArray(CompletableFuture[]::new)).join();
+ } else {
+ mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId);
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 3dddbc0..d0bb2b9 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -115,14 +115,14 @@
public AttributionSource(int uid, @Nullable String packageName,
@Nullable String attributionTag, @NonNull IBinder token) {
this(uid, Process.INVALID_PID, packageName, attributionTag, token,
- /*renouncedPermissions*/ null, /*next*/ null);
+ /*renouncedPermissions*/ null, Context.DEVICE_ID_DEFAULT, /*next*/ null);
}
/** @hide */
public AttributionSource(int uid, int pid, @Nullable String packageName,
@Nullable String attributionTag, @NonNull IBinder token) {
this(uid, pid, packageName, attributionTag, token, /*renouncedPermissions*/ null,
- /*next*/ null);
+ Context.DEVICE_ID_DEFAULT, /*next*/ null);
}
/** @hide */
@@ -132,21 +132,23 @@
@Nullable AttributionSource next) {
this(uid, Process.INVALID_PID, packageName, attributionTag, sDefaultToken,
(renouncedPermissions != null)
- ? renouncedPermissions.toArray(new String[0]) : null, /*next*/ next);
+ ? renouncedPermissions.toArray(new String[0]) : null, Context.DEVICE_ID_DEFAULT,
+ /*next*/ next);
}
/** @hide */
public AttributionSource(@NonNull AttributionSource current, @Nullable AttributionSource next) {
this(current.getUid(), current.getPid(), current.getPackageName(),
current.getAttributionTag(), current.getToken(),
- current.mAttributionSourceState.renouncedPermissions, next);
+ current.mAttributionSourceState.renouncedPermissions, current.getDeviceId(), next);
}
/** @hide */
public AttributionSource(int uid, int pid, @Nullable String packageName,
- @Nullable String attributionTag, @Nullable String[] renouncedPermissions,
+ @Nullable String attributionTag, @Nullable String[] renouncedPermissions, int deviceId,
@Nullable AttributionSource next) {
- this(uid, pid, packageName, attributionTag, sDefaultToken, renouncedPermissions, next);
+ this(uid, pid, packageName, attributionTag, sDefaultToken, renouncedPermissions, deviceId,
+ next);
}
/** @hide */
@@ -155,6 +157,16 @@
@Nullable String attributionTag, @NonNull IBinder token,
@Nullable String[] renouncedPermissions,
@Nullable AttributionSource next) {
+ this(uid, pid, packageName, attributionTag, token, renouncedPermissions,
+ Context.DEVICE_ID_DEFAULT, next);
+ }
+
+ /** @hide */
+ @TestApi
+ public AttributionSource(int uid, int pid, @Nullable String packageName,
+ @Nullable String attributionTag, @NonNull IBinder token,
+ @Nullable String[] renouncedPermissions,
+ int deviceId, @Nullable AttributionSource next) {
mAttributionSourceState = new AttributionSourceState();
mAttributionSourceState.uid = uid;
mAttributionSourceState.pid = pid;
@@ -162,6 +174,7 @@
mAttributionSourceState.packageName = packageName;
mAttributionSourceState.attributionTag = attributionTag;
mAttributionSourceState.renouncedPermissions = renouncedPermissions;
+ mAttributionSourceState.deviceId = deviceId;
mAttributionSourceState.next = (next != null) ? new AttributionSourceState[]
{next.mAttributionSourceState} : new AttributionSourceState[0];
}
@@ -197,25 +210,31 @@
/** @hide */
public AttributionSource withNextAttributionSource(@Nullable AttributionSource next) {
return new AttributionSource(getUid(), getPid(), getPackageName(), getAttributionTag(),
- getToken(), mAttributionSourceState.renouncedPermissions, next);
+ getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), next);
}
/** @hide */
public AttributionSource withPackageName(@Nullable String packageName) {
return new AttributionSource(getUid(), getPid(), packageName, getAttributionTag(),
- getToken(), mAttributionSourceState.renouncedPermissions, getNext());
+ getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());
}
/** @hide */
public AttributionSource withToken(@NonNull Binder token) {
return new AttributionSource(getUid(), getPid(), getPackageName(), getAttributionTag(),
- token, mAttributionSourceState.renouncedPermissions, getNext());
+ token, mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());
}
/** @hide */
public AttributionSource withPid(int pid) {
return new AttributionSource(getUid(), pid, getPackageName(), getAttributionTag(),
- getToken(), mAttributionSourceState.renouncedPermissions, getNext());
+ getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());
+ }
+
+ /** @hide */
+ public AttributionSource withDeviceId(int deviceId) {
+ return new AttributionSource(getUid(), getPid(), getPackageName(), getAttributionTag(),
+ getToken(), mAttributionSourceState.renouncedPermissions, deviceId, getNext());
}
/** @hide */
@@ -259,6 +278,7 @@
try {
return new AttributionSource.Builder(uid)
.setPid(Process.myPid())
+ .setDeviceId(Context.DEVICE_ID_DEFAULT)
.setPackageName(AppGlobals.getPackageManager().getPackagesForUid(uid)[0])
.build();
} catch (Exception ignored) {
@@ -497,6 +517,13 @@
}
/**
+ * The device ID for which permissions are checked.
+ */
+ public int getDeviceId() {
+ return mAttributionSourceState.deviceId;
+ }
+
+ /**
* Unique token for that source.
*
* @hide
@@ -662,6 +689,19 @@
}
/**
+ * Set the device ID for this attribution source, permission check would happen
+ * against this device ID.
+ *
+ * @return the builder
+ */
+ public @NonNull Builder setDeviceId(int deviceId) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x12;
+ mAttributionSourceState.deviceId = deviceId;
+ return this;
+ }
+
+ /**
* The next app to receive the permission protected data.
*
* @deprecated Use {@link setNextAttributionSource} instead.
@@ -703,6 +743,9 @@
if ((mBuilderFieldsSet & 0x10) == 0) {
mAttributionSourceState.renouncedPermissions = null;
}
+ if ((mBuilderFieldsSet & 0x12) == 0) {
+ mAttributionSourceState.deviceId = Context.DEVICE_ID_DEFAULT;
+ }
if ((mBuilderFieldsSet & 0x20) == 0) {
mAttributionSourceState.next = null;
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c10062c..fe108a5 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6546,6 +6546,14 @@
public static final String EXTRA_REASON = "android.intent.extra.REASON";
/**
+ * Intent extra: Whether to show the wipe progress UI or to skip it.
+ *
+ * <p>Type: boolean
+ * @hide
+ */
+ public static final String EXTRA_SHOW_WIPE_PROGRESS = "android.intent.extra.SHOW_WIPE_PROGRESS";
+
+ /**
* {@hide}
* This extra will be send together with {@link #ACTION_FACTORY_RESET}
*/
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index b43639a..19539c2 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2415,6 +2415,8 @@
public int requireUserAction = USER_ACTION_UNSPECIFIED;
/** {@hide} */
public boolean applicationEnabledSettingPersistent = false;
+ /** {@hide} */
+ public int developmentInstallFlags = 0;
private final ArrayMap<String, Integer> mPermissionStates;
@@ -2464,6 +2466,7 @@
requireUserAction = source.readInt();
packageSource = source.readInt();
applicationEnabledSettingPersistent = source.readBoolean();
+ developmentInstallFlags = source.readInt();
}
/** {@hide} */
@@ -2495,6 +2498,7 @@
ret.requireUserAction = requireUserAction;
ret.packageSource = packageSource;
ret.applicationEnabledSettingPersistent = applicationEnabledSettingPersistent;
+ ret.developmentInstallFlags = developmentInstallFlags;
return ret;
}
@@ -3159,6 +3163,7 @@
pw.printPair("rollbackDataPolicy", rollbackDataPolicy);
pw.printPair("applicationEnabledSettingPersistent",
applicationEnabledSettingPersistent);
+ pw.printHexPair("developmentInstallFlags", developmentInstallFlags);
pw.println();
}
@@ -3200,6 +3205,7 @@
dest.writeInt(requireUserAction);
dest.writeInt(packageSource);
dest.writeBoolean(applicationEnabledSettingPersistent);
+ dest.writeInt(developmentInstallFlags);
}
public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bef023e..3791777 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1437,6 +1437,16 @@
public @interface InstallFlags {}
/**
+ * Install flags that can only be used in development workflows (e.g. {@code adb install}).
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "INSTALL_DEVELOPMENT_" }, value = {
+ INSTALL_DEVELOPMENT_FORCE_NON_STAGED_APEX_UPDATE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DevelopmentInstallFlags {}
+
+ /**
* Flag parameter for {@link #installPackage} to indicate that you want to
* replace an already installed package, if one exists.
*
@@ -1646,6 +1656,14 @@
*/
public static final int INSTALL_FROM_MANAGED_USER_OR_PROFILE = 1 << 26;
+ /**
+ * Flag parameter for {@link #installPackage} to force a non-staged update of an APEX. This is
+ * a development-only feature and should not be used on end user devices.
+ *
+ * @hide
+ */
+ public static final int INSTALL_DEVELOPMENT_FORCE_NON_STAGED_APEX_UPDATE = 1;
+
/** @hide */
@IntDef(flag = true, value = {
DONT_KILL_APP,
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 022f3c4..4323bf8 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1780,6 +1780,15 @@
* @hide
*/
String KEY_USE_NORMAL_BRIGHTNESS_MODE_CONTROLLER = "use_normal_brightness_mode_controller";
+
+ /**
+ * Key for disabling screen wake locks while apps are in cached state.
+ * Read value via {@link android.provider.DeviceConfig#getBoolean(String, String, boolean)}
+ * with {@link android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER} as the namespace.
+ * @hide
+ */
+ String KEY_DISABLE_SCREEN_WAKE_LOCKS_WHILE_CACHED =
+ "disable_screen_wake_locks_while_cached";
}
/**
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 70b72c8..b99996ff 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -433,7 +433,7 @@
@BinderThread
@Override
public void showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
- int flags, ResultReceiver resultReceiver) {
+ @InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER);
mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_SHOW_SOFT_INPUT,
flags, showInputToken, resultReceiver, statsToken));
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index e472a40..44fed67 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -606,6 +606,7 @@
InputConnection mStartedInputConnection;
EditorInfo mInputEditorInfo;
+ @InputMethod.ShowFlags
int mShowInputFlags;
boolean mShowInputRequested;
boolean mLastShowInputRequested;
@@ -930,8 +931,9 @@
*/
@MainThread
@Override
- public void showSoftInputWithToken(int flags, ResultReceiver resultReceiver,
- IBinder showInputToken, @Nullable ImeTracker.Token statsToken) {
+ public void showSoftInputWithToken(@InputMethod.ShowFlags int flags,
+ ResultReceiver resultReceiver, IBinder showInputToken,
+ @Nullable ImeTracker.Token statsToken) {
mSystemCallingShowSoftInput = true;
mCurShowInputToken = showInputToken;
mCurStatsToken = statsToken;
@@ -949,7 +951,7 @@
*/
@MainThread
@Override
- public void showSoftInput(int flags, ResultReceiver resultReceiver) {
+ public void showSoftInput(@InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
ImeTracker.forLogging().onProgress(
mCurStatsToken, ImeTracker.PHASE_IME_SHOW_SOFT_INPUT);
if (DEBUG) Log.v(TAG, "showSoftInput()");
@@ -1325,7 +1327,8 @@
* InputMethodService#requestShowSelf} or {@link InputMethodService#requestHideSelf}
*/
@Deprecated
- public void toggleSoftInput(int showFlags, int hideFlags) {
+ public void toggleSoftInput(@InputMethodManager.ShowFlags int showFlags,
+ @InputMethodManager.HideFlags int hideFlags) {
InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
}
@@ -2797,18 +2800,16 @@
* {@link #onEvaluateInputViewShown()}, {@link #onEvaluateFullscreenMode()},
* and the current configuration to decide whether the input view should
* be shown at this point.
- *
- * @param flags Provides additional information about the show request,
- * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
+ *
* @param configChange This is true if we are re-showing due to a
* configuration change.
* @return Returns true to indicate that the window should be shown.
*/
- public boolean onShowInputRequested(int flags, boolean configChange) {
+ public boolean onShowInputRequested(@InputMethod.ShowFlags int flags, boolean configChange) {
if (!onEvaluateInputViewShown()) {
return false;
}
- if ((flags&InputMethod.SHOW_EXPLICIT) == 0) {
+ if ((flags & InputMethod.SHOW_EXPLICIT) == 0) {
if (!configChange && onEvaluateFullscreenMode() && !isInputViewShown()) {
// Don't show if this is not explicitly requested by the user and
// the input method is fullscreen unless it is already shown. That
@@ -2834,14 +2835,14 @@
* exposed to IME authors as an overridable public method without {@code @CallSuper}, we have
* to have this method to ensure that those internal states are always updated no matter how
* {@link #onShowInputRequested(int, boolean)} is overridden by the IME author.
- * @param flags Provides additional information about the show request,
- * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
+ *
* @param configChange This is true if we are re-showing due to a
* configuration change.
* @return Returns true to indicate that the window should be shown.
* @see #onShowInputRequested(int, boolean)
*/
- private boolean dispatchOnShowInputRequested(int flags, boolean configChange) {
+ private boolean dispatchOnShowInputRequested(@InputMethod.ShowFlags int flags,
+ boolean configChange) {
final boolean result = onShowInputRequested(flags, configChange);
mInlineSuggestionSessionController.notifyOnShowInputRequested(result);
if (result) {
@@ -3274,16 +3275,13 @@
*
* The input method will continue running, but the user can no longer use it to generate input
* by touching the screen.
- *
- * @see InputMethodManager#HIDE_IMPLICIT_ONLY
- * @see InputMethodManager#HIDE_NOT_ALWAYS
- * @param flags Provides additional operating flags.
*/
- public void requestHideSelf(int flags) {
+ public void requestHideSelf(@InputMethodManager.HideFlags int flags) {
requestHideSelf(flags, SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_IME);
}
- private void requestHideSelf(int flags, @SoftInputShowHideReason int reason) {
+ private void requestHideSelf(@InputMethodManager.HideFlags int flags,
+ @SoftInputShowHideReason int reason) {
ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", mDumper,
null /* icProto */);
mPrivOps.hideMySoftInput(flags, reason);
@@ -3292,12 +3290,8 @@
/**
* Show the input method's soft input area, so the user sees the input method window and can
* interact with it.
- *
- * @see InputMethodManager#SHOW_IMPLICIT
- * @see InputMethodManager#SHOW_FORCED
- * @param flags Provides additional operating flags.
*/
- public final void requestShowSelf(int flags) {
+ public final void requestShowSelf(@InputMethodManager.ShowFlags int flags) {
ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", mDumper,
null /* icProto */);
mPrivOps.showMySoftInput(flags);
@@ -3457,7 +3451,8 @@
/**
* Handle a request by the system to toggle the soft input area.
*/
- private void onToggleSoftInput(int showFlags, int hideFlags) {
+ private void onToggleSoftInput(@InputMethodManager.ShowFlags int showFlags,
+ @InputMethodManager.HideFlags int hideFlags) {
if (DEBUG) Log.v(TAG, "toggleSoftInput()");
if (isInputViewShown()) {
requestHideSelf(
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index ceaf337..2584f04 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -119,7 +119,7 @@
* crashes (if a handler is sometimes created on a thread without a Looper active), or race
* conditions, where the thread a handler is associated with is not what the author
* anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper
- * explicitly, using {@link Looper#getMainLooper}, {link android.view.View#getHandler}, or
+ * explicitly, using {@link Looper#getMainLooper}, {@link android.view.View#getHandler}, or
* similar. If the implicit thread local behavior is required for compatibility, use
* {@code new Handler(Looper.myLooper())} to make it clear to readers.
*
@@ -144,7 +144,7 @@
* crashes (if a handler is sometimes created on a thread without a Looper active), or race
* conditions, where the thread a handler is associated with is not what the author
* anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper
- * explicitly, using {@link Looper#getMainLooper}, {link android.view.View#getHandler}, or
+ * explicitly, using {@link Looper#getMainLooper}, {@link android.view.View#getHandler}, or
* similar. If the implicit thread local behavior is required for compatibility, use
* {@code new Handler(Looper.myLooper(), callback)} to make it clear to readers.
*/
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index 5c4aa4a..ea5499f 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -1,6 +1,19 @@
{
"presubmit": [
{
+ "file_patterns": [
+ "[^/]*(Vibrator|Vibration)[^/]*\\.java",
+ "vibrator/.*"
+ ],
+ "name": "FrameworksVibratorCoreTests",
+ "options": [
+ {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ },
+ {
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "BugreportManagerTestCases",
"options": [
diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
index d41a428..e91e04e 100644
--- a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
+++ b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
@@ -28,7 +28,6 @@
import com.android.internal.vibrator.persistence.XmlConstants;
import com.android.internal.vibrator.persistence.XmlParserException;
import com.android.internal.vibrator.persistence.XmlReader;
-import com.android.internal.vibrator.persistence.XmlSerializedVibration;
import com.android.modules.utils.TypedXmlPullParser;
import org.xmlpull.v1.XmlPullParser;
@@ -178,25 +177,62 @@
// Ensure XML starts with expected root tag.
XmlReader.readDocumentStartTag(parser, XmlConstants.TAG_VIBRATION);
- int parserFlags = 0;
- if ((flags & FLAG_ALLOW_HIDDEN_APIS) != 0) {
- parserFlags |= XmlConstants.FLAG_ALLOW_HIDDEN_APIS;
- }
-
// Parse root tag as a vibration effect.
- XmlSerializedVibration<VibrationEffect> serializedVibration =
- VibrationEffectXmlParser.parseTag(parser, parserFlags);
+ VibrationEffect effect = parseTag(parser, flags);
// Ensure XML ends after root tag is consumed.
XmlReader.readDocumentEndTag(parser);
- return serializedVibration.deserialize();
- } catch (XmlParserException e) {
+ return effect;
+ } catch (XmlParserException | VibrationXmlParserException e) {
Slog.w(TAG, "Error parsing vibration XML", e);
return null;
}
}
+ /**
+ * Parses XML content from given open {@link TypedXmlPullParser} into a {@link VibrationEffect}.
+ *
+ * <p>The provided parser should be pointing to a start of a valid vibration XML (i.e. to a
+ * start <vibration> tag). No other parser position, including start of document, is considered
+ * valid.
+ *
+ * <p>This method parses as long as it reads a valid vibration XML, and until an end vibration
+ * tag. After a successful parsing, the parser will point to the end vibration tag (i.e. to a
+ * </vibration> tag).
+ *
+ * @throws IOException error parsing from given {@link TypedXmlPullParser}.
+ * @throws VibrationXmlParserException if the XML tag cannot be parsed into a
+ * {@link VibrationEffect}. The given {@code parser} might be pointing to a child XML tag
+ * that caused the parser failure.
+ *
+ * @hide
+ */
+ @NonNull
+ public static VibrationEffect parseTag(@NonNull TypedXmlPullParser parser, @Flags int flags)
+ throws IOException, VibrationXmlParserException {
+ int parserFlags = 0;
+ if ((flags & VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS) != 0) {
+ parserFlags |= XmlConstants.FLAG_ALLOW_HIDDEN_APIS;
+ }
+ try {
+ return VibrationEffectXmlParser.parseTag(parser, parserFlags).deserialize();
+ } catch (XmlParserException e) {
+ throw new VibrationXmlParserException("Error parsing vibration effect.", e);
+ }
+ }
+
+ /**
+ * Represents an error while parsing a vibration XML input.
+ *
+ * @hide
+ */
+ public static final class VibrationXmlParserException extends Exception {
+ private VibrationXmlParserException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
private VibrationXmlParser() {
}
}
diff --git a/core/java/android/print/OWNERS b/core/java/android/print/OWNERS
index 28a24203..0809de2 100644
--- a/core/java/android/print/OWNERS
+++ b/core/java/android/print/OWNERS
@@ -1,4 +1,4 @@
# Bug component: 47273
-svetoslavganov@android.com
-svetoslavganov@google.com
+anothermark@google.com
+kumarashishg@google.com
diff --git a/core/java/android/print/pdf/OWNERS b/core/java/android/print/pdf/OWNERS
deleted file mode 100644
index 28a24203..0000000
--- a/core/java/android/print/pdf/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Bug component: 47273
-
-svetoslavganov@android.com
-svetoslavganov@google.com
diff --git a/core/java/android/printservice/OWNERS b/core/java/android/printservice/OWNERS
index 28a24203..0809de2 100644
--- a/core/java/android/printservice/OWNERS
+++ b/core/java/android/printservice/OWNERS
@@ -1,4 +1,4 @@
# Bug component: 47273
-svetoslavganov@android.com
-svetoslavganov@google.com
+anothermark@google.com
+kumarashishg@google.com
diff --git a/core/java/android/printservice/recommendation/OWNERS b/core/java/android/printservice/recommendation/OWNERS
deleted file mode 100644
index 28a24203..0000000
--- a/core/java/android/printservice/recommendation/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Bug component: 47273
-
-svetoslavganov@android.com
-svetoslavganov@google.com
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index f256210..447c3bc 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -27,6 +27,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Typeface;
import android.icu.lang.UCharacter;
import android.icu.text.CaseMap;
import android.icu.text.Edits;
@@ -2482,12 +2483,28 @@
if (ellipsizeDip == 0) {
return gettingCleaned.toString();
} else {
- // Truncate
- final TextPaint paint = new TextPaint();
- paint.setTextSize(42);
+ final float assumedFontSizePx = 42;
+ if (Typeface.getSystemFontMap().isEmpty()) {
+ // In the system server, the font files may not be loaded, so unable to perform
+ // ellipsize, so use the estimated char count for the ellipsize.
- return TextUtils.ellipsize(gettingCleaned.toString(), paint, ellipsizeDip,
- TextUtils.TruncateAt.END);
+ // The median of glyph widths of the Roboto is 0.57em, so use it as a reference
+ // of the glyph width.
+ final float assumedCharWidthInEm = 0.57f;
+ final float assumedCharWidthInPx = assumedFontSizePx * assumedCharWidthInEm;
+
+ // Even if the argument name is `ellipsizeDip`, the unit of this argument is pixels.
+ final int charCount = (int) ((ellipsizeDip + 0.5f) / assumedCharWidthInPx);
+ return TextUtils.trimToSize(gettingCleaned.toString(), charCount)
+ + getEllipsisString(TruncateAt.END);
+ } else {
+ // Truncate
+ final TextPaint paint = new TextPaint();
+ paint.setTextSize(assumedFontSizePx);
+
+ return TextUtils.ellipsize(gettingCleaned.toString(), paint, ellipsizeDip,
+ TextUtils.TruncateAt.END);
+ }
}
}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 99a4f6b..6aa8506 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -34,6 +34,7 @@
import android.util.Log;
import android.view.inputmethod.InputMethodManager;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastPrintWriter;
import java.io.FileDescriptor;
@@ -184,6 +185,15 @@
}
}
+ /** Overrides the {@link #getWindowManagerService()} for test only. */
+ @VisibleForTesting
+ public static void overrideWindowManagerServiceForTesting(
+ @NonNull IWindowManager windowManager) {
+ synchronized (WindowManagerGlobal.class) {
+ sWindowManagerService = windowManager;
+ }
+ }
+
@UnsupportedAppUsage
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 8ba8b8c..0699bc1 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -32,6 +32,7 @@
import android.util.AttributeSet;
import android.util.TimeUtils;
import android.util.Xml;
+import android.view.Choreographer;
import android.view.InflateException;
import org.xmlpull.v1.XmlPullParser;
@@ -153,7 +154,13 @@
*/
public static long getExpectedPresentationTimeNanos() {
AnimationState state = sAnimationState.get();
- return state.mExpectedPresentationTimeNanos;
+ if (state.animationClockLocked) {
+ return state.mExpectedPresentationTimeNanos;
+ }
+ // When this methoed is called outside of a Choreographer callback,
+ // we obtain the value of expectedPresentTimeNanos from the Choreographer.
+ // This helps avoid returning a time that could potentially be earlier than current time.
+ return Choreographer.getInstance().getLatestExpectedPresentTimeNanos();
}
/**
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index ce2c180..467daa0 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -295,8 +295,8 @@
@AnyThread
static boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
- @Nullable ImeTracker.Token statsToken, int flags, int lastClickToolType,
- @Nullable ResultReceiver resultReceiver,
+ @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+ int lastClickToolType, @Nullable ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
final IInputMethodManager service = getService();
if (service == null) {
@@ -312,7 +312,7 @@
@AnyThread
static boolean hideSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
- @Nullable ImeTracker.Token statsToken, int flags,
+ @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
@Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
final IInputMethodManager service = getService();
if (service == null) {
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 6340388..5b4efd8 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -17,6 +17,7 @@
package android.view.inputmethod;
import android.annotation.DurationMillisLong;
+import android.annotation.IntDef;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -36,6 +37,8 @@
import com.android.internal.inputmethod.InlineSuggestionsRequestInfo;
import com.android.internal.inputmethod.InputMethodNavButtonFlags;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
@@ -269,6 +272,14 @@
*/
@MainThread
public void revokeSession(InputMethodSession session);
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "SHOW_" }, value = {
+ SHOW_EXPLICIT,
+ SHOW_FORCED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ShowFlags {}
/**
* Flag for {@link #showSoftInput}: this show has been explicitly
@@ -292,8 +303,6 @@
/**
* Request that any soft input part of the input method be shown to the user.
*
- * @param flags Provides additional information about the show request.
- * Currently may be 0 or have the bit {@link #SHOW_EXPLICIT} set.
* @param resultReceiver The client requesting the show may wish to
* be told the impact of their request, which should be supplied here.
* The result code should be
@@ -308,7 +317,7 @@
* @hide
*/
@MainThread
- public default void showSoftInputWithToken(int flags, ResultReceiver resultReceiver,
+ public default void showSoftInputWithToken(@ShowFlags int flags, ResultReceiver resultReceiver,
IBinder showInputToken, @Nullable ImeTracker.Token statsToken) {
showSoftInput(flags, resultReceiver);
}
@@ -316,8 +325,6 @@
/**
* Request that any soft input part of the input method be shown to the user.
*
- * @param flags Provides additional information about the show request.
- * Currently may be 0 or have the bit {@link #SHOW_EXPLICIT} set.
* @param resultReceiver The client requesting the show may wish to
* be told the impact of their request, which should be supplied here.
* The result code should be
@@ -327,11 +334,12 @@
* {@link InputMethodManager#RESULT_HIDDEN InputMethodManager.RESULT_HIDDEN}.
*/
@MainThread
- public void showSoftInput(int flags, ResultReceiver resultReceiver);
+ public void showSoftInput(@ShowFlags int flags, ResultReceiver resultReceiver);
/**
* Request that any soft input part of the input method be hidden from the user.
- * @param flags Provides additional information about the show request.
+ *
+ * @param flags Provides additional information about the hide request.
* Currently always 0.
* @param resultReceiver The client requesting the show may wish to
* be told the impact of their request, which should be supplied here.
@@ -354,7 +362,8 @@
/**
* Request that any soft input part of the input method be hidden from the user.
- * @param flags Provides additional information about the show request.
+ *
+ * @param flags Provides additional information about the hide request.
* Currently always 0.
* @param resultReceiver The client requesting the show may wish to
* be told the impact of their request, which should be supplied here.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 48bf973..e4cc6d5 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -39,6 +39,7 @@
import android.annotation.DisplayContext;
import android.annotation.DrawableRes;
import android.annotation.DurationMillisLong;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
@@ -122,6 +123,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Collections;
@@ -2034,6 +2037,14 @@
}
}
+ /** @hide */
+ @IntDef(flag = true, prefix = { "SHOW_" }, value = {
+ SHOW_IMPLICIT,
+ SHOW_FORCED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ShowFlags {}
+
/**
* Flag for {@link #showSoftInput} to indicate that this is an implicit
* request to show the input window, not as the result of a direct request
@@ -2065,10 +2076,8 @@
* {@link View#isFocused view focus}, and its containing window has
* {@link View#hasWindowFocus window focus}. Otherwise the call fails and
* returns {@code false}.
- * @param flags Provides additional operating flags. Currently may be
- * 0 or have the {@link #SHOW_IMPLICIT} bit set.
*/
- public boolean showSoftInput(View view, int flags) {
+ public boolean showSoftInput(View view, @ShowFlags int flags) {
// Re-dispatch if there is a context mismatch.
final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
if (fallbackImm != null) {
@@ -2131,21 +2140,20 @@
* {@link View#isFocused view focus}, and its containing window has
* {@link View#hasWindowFocus window focus}. Otherwise the call fails and
* returns {@code false}.
- * @param flags Provides additional operating flags. Currently may be
- * 0 or have the {@link #SHOW_IMPLICIT} bit set.
* @param resultReceiver If non-null, this will be called by the IME when
* it has processed your request to tell you what it has done. The result
* code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
* {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
* {@link #RESULT_HIDDEN}.
*/
- public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
+ public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) {
return showSoftInput(view, null /* statsToken */, flags, resultReceiver,
SoftInputShowHideReason.SHOW_SOFT_INPUT);
}
- private boolean showSoftInput(View view, @Nullable ImeTracker.Token statsToken, int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ private boolean showSoftInput(View view, @Nullable ImeTracker.Token statsToken,
+ @ShowFlags int flags, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {
if (statsToken == null) {
statsToken = ImeTracker.forLogging().onRequestShow(null /* component */,
Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT, reason);
@@ -2199,7 +2207,7 @@
*/
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768499)
- public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) {
+ public void showSoftInputUnchecked(@ShowFlags int flags, ResultReceiver resultReceiver) {
synchronized (mH) {
final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestShow(
null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT,
@@ -2230,6 +2238,14 @@
}
}
+ /** @hide */
+ @IntDef(flag = true, prefix = { "HIDE_" }, value = {
+ HIDE_IMPLICIT_ONLY,
+ HIDE_NOT_ALWAYS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface HideFlags {}
+
/**
* Flag for {@link #hideSoftInputFromWindow} and {@link InputMethodService#requestHideSelf(int)}
* to indicate that the soft input window should only be hidden if it was not explicitly shown
@@ -2251,10 +2267,8 @@
*
* @param windowToken The token of the window that is making the request,
* as returned by {@link View#getWindowToken() View.getWindowToken()}.
- * @param flags Provides additional operating flags. Currently may be
- * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
*/
- public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) {
+ public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags) {
return hideSoftInputFromWindow(windowToken, flags, null);
}
@@ -2276,21 +2290,19 @@
*
* @param windowToken The token of the window that is making the request,
* as returned by {@link View#getWindowToken() View.getWindowToken()}.
- * @param flags Provides additional operating flags. Currently may be
- * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
* @param resultReceiver If non-null, this will be called by the IME when
* it has processed your request to tell you what it has done. The result
* code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
* {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
* {@link #RESULT_HIDDEN}.
*/
- public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
+ public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags,
ResultReceiver resultReceiver) {
return hideSoftInputFromWindow(windowToken, flags, resultReceiver,
SoftInputShowHideReason.HIDE_SOFT_INPUT);
}
- private boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
+ private boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide(
null /* component */, Process.myUid(),
@@ -2493,12 +2505,6 @@
* If not the input window will be displayed.
* @param windowToken The token of the window that is making the request,
* as returned by {@link View#getWindowToken() View.getWindowToken()}.
- * @param showFlags Provides additional operating flags. May be
- * 0 or have the {@link #SHOW_IMPLICIT},
- * {@link #SHOW_FORCED} bit set.
- * @param hideFlags Provides additional operating flags. May be
- * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
- * {@link #HIDE_NOT_ALWAYS} bit set.
*
* @deprecated Use {@link #showSoftInput(View, int)} or
* {@link #hideSoftInputFromWindow(IBinder, int)} explicitly instead.
@@ -2507,7 +2513,8 @@
* has an effect if the calling app is the current IME focus.
*/
@Deprecated
- public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) {
+ public void toggleSoftInputFromWindow(IBinder windowToken, @ShowFlags int showFlags,
+ @HideFlags int hideFlags) {
ImeTracing.getInstance().triggerClientDump(
"InputMethodManager#toggleSoftInputFromWindow", InputMethodManager.this,
null /* icProto */);
@@ -2525,12 +2532,6 @@
*
* If the input window is already displayed, it gets hidden.
* If not the input window will be displayed.
- * @param showFlags Provides additional operating flags. May be
- * 0 or have the {@link #SHOW_IMPLICIT},
- * {@link #SHOW_FORCED} bit set.
- * @param hideFlags Provides additional operating flags. May be
- * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
- * {@link #HIDE_NOT_ALWAYS} bit set.
*
* @deprecated Use {@link #showSoftInput(View, int)} or
* {@link #hideSoftInputFromWindow(IBinder, int)} explicitly instead.
@@ -2539,7 +2540,7 @@
* has an effect if the calling app is the current IME focus.
*/
@Deprecated
- public void toggleSoftInput(int showFlags, int hideFlags) {
+ public void toggleSoftInput(@ShowFlags int showFlags, @HideFlags int hideFlags) {
ImeTracing.getInstance().triggerClientDump(
"InputMethodManager#toggleSoftInput", InputMethodManager.this,
null /* icProto */);
@@ -3552,15 +3553,12 @@
* @param token Supplies the identifying token given to an input method
* when it was started, which allows it to perform this operation on
* itself.
- * @param flags Provides additional operating flags. Currently may be
- * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
- * {@link #HIDE_NOT_ALWAYS} bit set.
* @deprecated Use {@link InputMethodService#requestHideSelf(int)} instead. This method was
* intended for IME developers who should be accessing APIs through the service. APIs in this
* class are intended for app developers interacting with the IME.
*/
@Deprecated
- public void hideSoftInputFromInputMethod(IBinder token, int flags) {
+ public void hideSoftInputFromInputMethod(IBinder token, @HideFlags int flags) {
InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(
flags, SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION);
}
@@ -3574,15 +3572,12 @@
* @param token Supplies the identifying token given to an input method
* when it was started, which allows it to perform this operation on
* itself.
- * @param flags Provides additional operating flags. Currently may be
- * 0 or have the {@link #SHOW_IMPLICIT} or
- * {@link #SHOW_FORCED} bit set.
* @deprecated Use {@link InputMethodService#requestShowSelf(int)} instead. This method was
* intended for IME developers who should be accessing APIs through the service. APIs in this
* class are intended for app developers interacting with the IME.
*/
@Deprecated
- public void showSoftInputFromInputMethod(IBinder token, int flags) {
+ public void showSoftInputFromInputMethod(IBinder token, @ShowFlags int flags) {
InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(flags);
}
diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java
index af6af14..4f48cb6 100644
--- a/core/java/android/view/inputmethod/InputMethodSession.java
+++ b/core/java/android/view/inputmethod/InputMethodSession.java
@@ -169,12 +169,6 @@
/**
* Toggle the soft input window.
* Applications can toggle the state of the soft input window.
- * @param showFlags Provides additional operating flags. May be
- * 0 or have the {@link InputMethodManager#SHOW_IMPLICIT},
- * {@link InputMethodManager#SHOW_FORCED} bit set.
- * @param hideFlags Provides additional operating flags. May be
- * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY},
- * {@link InputMethodManager#HIDE_NOT_ALWAYS} bit set.
*
* @deprecated Starting in {@link android.os.Build.VERSION_CODES#S} the system no longer invokes
* this method, instead it explicitly shows or hides the IME. An {@code InputMethodService}
@@ -182,7 +176,8 @@
* InputMethodService#requestShowSelf} or {@link InputMethodService#requestHideSelf}
*/
@Deprecated
- public void toggleSoftInput(int showFlags, int hideFlags);
+ public void toggleSoftInput(@InputMethodManager.ShowFlags int showFlags,
+ @InputMethodManager.HideFlags int hideFlags);
/**
* This method is called when the cursor and/or the character position relevant to text input
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index e9d7b9b..a95748c 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -43,7 +43,6 @@
import android.view.View;
import android.view.ViewRootImpl;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.IRemoteInputConnection;
@@ -54,6 +53,7 @@
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -158,18 +158,13 @@
boolean cancellable();
}
- @GuardedBy("mLock")
- @Nullable
- private InputConnection mInputConnection;
+ @NonNull
+ private final AtomicReference<InputConnection> mInputConnectionRef;
@NonNull
private final Looper mLooper;
private final Handler mH;
- private final Object mLock = new Object();
- @GuardedBy("mLock")
- private boolean mFinished = false;
-
private final InputMethodManager mParentInputMethodManager;
private final WeakReference<View> mServedView;
@@ -185,7 +180,7 @@
RemoteInputConnectionImpl(@NonNull Looper looper,
@NonNull InputConnection inputConnection,
@NonNull InputMethodManager inputMethodManager, @Nullable View servedView) {
- mInputConnection = inputConnection;
+ mInputConnectionRef = new AtomicReference<>(inputConnection);
mLooper = looper;
mH = new Handler(mLooper);
mParentInputMethodManager = inputMethodManager;
@@ -197,9 +192,7 @@
*/
@Nullable
public InputConnection getInputConnection() {
- synchronized (mLock) {
- return mInputConnection;
- }
+ return mInputConnectionRef.get();
}
/**
@@ -215,9 +208,7 @@
* {@link InputConnection#closeConnection()} as a result of {@link #deactivate()}.
*/
private boolean isFinished() {
- synchronized (mLock) {
- return mFinished;
- }
+ return mInputConnectionRef.get() == null;
}
private boolean isActive() {
@@ -386,10 +377,7 @@
// TODO(b/199934664): See if we can remove this by providing a default impl.
}
} finally {
- synchronized (mLock) {
- mInputConnection = null;
- mFinished = true;
- }
+ mInputConnectionRef.set(null);
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
}
@@ -441,7 +429,6 @@
public String toString() {
return "RemoteInputConnectionImpl{"
+ "connection=" + getInputConnection()
- + " finished=" + isFinished()
+ " mParentInputMethodManager.isActive()=" + mParentInputMethodManager.isActive()
+ " mServedView=" + mServedView.get()
+ "}";
@@ -455,16 +442,14 @@
* {@link DumpableInputConnection#dumpDebug(ProtoOutputStream, long)}.
*/
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
- synchronized (mLock) {
- // Check that the call is initiated in the target thread of the current InputConnection
- // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
- // executed on this thread. Otherwise the messages are dispatched to the correct thread
- // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance
- // reasons.
- if ((mInputConnection instanceof DumpableInputConnection)
- && mLooper.isCurrentThread()) {
- ((DumpableInputConnection) mInputConnection).dumpDebug(proto, fieldId);
- }
+ final InputConnection ic = mInputConnectionRef.get();
+ // Check that the call is initiated in the target thread of the current InputConnection
+ // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
+ // executed on this thread. Otherwise the messages are dispatched to the correct thread
+ // in IInputConnectionWrapper, but this is not wanted while dumping, for performance
+ // reasons.
+ if ((ic instanceof DumpableInputConnection) && mLooper.isCurrentThread()) {
+ ((DumpableInputConnection) ic).dumpDebug(proto, fieldId);
}
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 66aa66c..b0e5f777 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -786,6 +786,42 @@
}
}
+ /**
+ * @hide
+ * @return True if there is a change
+ */
+ public boolean replaceRemoteCollections(int viewId) {
+ boolean isActionReplaced = false;
+ if (mActions != null) {
+ for (int i = 0; i < mActions.size(); i++) {
+ Action action = mActions.get(i);
+ if (action instanceof SetRemoteCollectionItemListAdapterAction itemsAction
+ && itemsAction.viewId == viewId
+ && itemsAction.mServiceIntent != null) {
+ mActions.set(i, new SetRemoteCollectionItemListAdapterAction(itemsAction.viewId,
+ itemsAction.mServiceIntent));
+ isActionReplaced = true;
+ } else if (action instanceof ViewGroupActionAdd groupAction
+ && groupAction.mNestedViews != null) {
+ isActionReplaced |= groupAction.mNestedViews.replaceRemoteCollections(viewId);
+ }
+ }
+ }
+ if (mSizedRemoteViews != null) {
+ for (int i = 0; i < mSizedRemoteViews.size(); i++) {
+ isActionReplaced |= mSizedRemoteViews.get(i).replaceRemoteCollections(viewId);
+ }
+ }
+ if (mLandscape != null) {
+ isActionReplaced |= mLandscape.replaceRemoteCollections(viewId);
+ }
+ if (mPortrait != null) {
+ isActionReplaced |= mPortrait.replaceRemoteCollections(viewId);
+ }
+
+ return isActionReplaced;
+ }
+
private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) {
if (icon != null && (icon.getType() == Icon.TYPE_URI
|| icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
@@ -1059,18 +1095,21 @@
private class SetRemoteCollectionItemListAdapterAction extends Action {
private @NonNull CompletableFuture<RemoteCollectionItems> mItemsFuture;
+ final Intent mServiceIntent;
SetRemoteCollectionItemListAdapterAction(@IdRes int id,
@NonNull RemoteCollectionItems items) {
viewId = id;
items.setHierarchyRootData(getHierarchyRootData());
mItemsFuture = CompletableFuture.completedFuture(items);
+ mServiceIntent = null;
}
SetRemoteCollectionItemListAdapterAction(@IdRes int id, Intent intent) {
viewId = id;
mItemsFuture = getItemsFutureFromIntentWithTimeout(intent);
setHierarchyRootData(getHierarchyRootData());
+ mServiceIntent = intent;
}
private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
@@ -1119,6 +1158,7 @@
viewId = parcel.readInt();
mItemsFuture = CompletableFuture.completedFuture(
new RemoteCollectionItems(parcel, getHierarchyRootData()));
+ mServiceIntent = parcel.readTypedObject(Intent.CREATOR);
}
@Override
@@ -1148,6 +1188,7 @@
dest.writeInt(viewId);
RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
items.writeToParcel(dest, flags, /* attached= */ true);
+ dest.writeTypedObject(mServiceIntent, flags);
}
@Override
@@ -4768,9 +4809,7 @@
*/
@Deprecated
public void setRemoteAdapter(@IdRes int viewId, Intent intent) {
- if (AppGlobals.getIntCoreSetting(
- SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION,
- SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT ? 1 : 0) == 1) {
+ if (isAdapterConversionEnabled()) {
addAction(new SetRemoteCollectionItemListAdapterAction(viewId, intent));
return;
}
@@ -4778,6 +4817,16 @@
}
/**
+ * @hide
+ * @return True if the remote adapter conversion is enabled
+ */
+ public static boolean isAdapterConversionEnabled() {
+ return AppGlobals.getIntCoreSetting(
+ SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION,
+ SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT ? 1 : 0) == 1;
+ }
+
+ /**
* Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
* ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
* This is a simpler but less flexible approach to populating collection widgets. Its use is
diff --git a/core/java/android/window/WindowInfosListenerForTest.java b/core/java/android/window/WindowInfosListenerForTest.java
index bdbce13..be88e53 100644
--- a/core/java/android/window/WindowInfosListenerForTest.java
+++ b/core/java/android/window/WindowInfosListenerForTest.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.InputConfig;
@@ -78,14 +79,21 @@
*/
public final boolean isVisible;
+ /**
+ * Return the transform to get the bounds from display space into window space.
+ */
+ @NonNull
+ public final Matrix transform;
+
WindowInfo(@NonNull IBinder windowToken, @NonNull String name, int displayId,
- @NonNull Rect bounds, int inputConfig) {
+ @NonNull Rect bounds, int inputConfig, @NonNull Matrix transform) {
this.windowToken = windowToken;
this.name = name;
this.displayId = displayId;
this.bounds = bounds;
this.isTrustedOverlay = (inputConfig & InputConfig.TRUSTED_OVERLAY) != 0;
this.isVisible = (inputConfig & InputConfig.NOT_VISIBLE) == 0;
+ this.transform = transform;
}
@Override
@@ -94,7 +102,8 @@
+ ", frame=" + bounds
+ ", isVisible=" + isVisible
+ ", isTrustedOverlay=" + isTrustedOverlay
- + ", token=" + windowToken;
+ + ", token=" + windowToken
+ + ", transform=" + transform;
}
}
@@ -155,7 +164,7 @@
var bounds = new Rect(handle.frameLeft, handle.frameTop, handle.frameRight,
handle.frameBottom);
windowInfos.add(new WindowInfo(handle.getWindowToken(), handle.name, handle.displayId,
- bounds, handle.inputConfig));
+ bounds, handle.inputConfig, handle.transform));
}
return windowInfos;
}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 632208c..849e0b3 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -23,6 +23,7 @@
import android.content.ContextWrapper;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.res.TypedArray;
import android.os.Handler;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -33,6 +34,7 @@
import androidx.annotation.VisibleForTesting;
+
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -62,6 +64,9 @@
.getInt("persist.wm.debug.predictive_back", 1) != 0;
private static final boolean ALWAYS_ENFORCE_PREDICTIVE_BACK = SystemProperties
.getInt("persist.wm.debug.predictive_back_always_enforce", 0) != 0;
+ private static final boolean PREDICTIVE_BACK_FALLBACK_WINDOW_ATTRIBUTE =
+ SystemProperties.getInt("persist.wm.debug.predictive_back_fallback_window_attribute", 0)
+ != 0;
@Nullable
private ImeOnBackInvokedDispatcher mImeDispatcher;
@@ -500,6 +505,31 @@
applicationInfo.packageName,
requestsPredictiveBack));
}
+
+ if (PREDICTIVE_BACK_FALLBACK_WINDOW_ATTRIBUTE && !requestsPredictiveBack) {
+ // Compatibility check for legacy window style flag used by Wear OS.
+ // Note on compatibility behavior:
+ // 1. windowSwipeToDismiss should be respected for all apps not opted in.
+ // 2. windowSwipeToDismiss should be true for all apps not opted in, which
+ // enables the PB animation for them.
+ // 3. windowSwipeToDismiss=false should be respected for apps not opted in,
+ // which disables PB & onBackPressed caused by BackAnimController's
+ // setTrigger(true)
+ TypedArray windowAttr =
+ context.obtainStyledAttributes(
+ new int[] {android.R.attr.windowSwipeToDismiss});
+ boolean windowSwipeToDismiss = true;
+ if (windowAttr.getIndexCount() > 0) {
+ windowSwipeToDismiss = windowAttr.getBoolean(0, true);
+ }
+ windowAttr.recycle();
+
+ if (DEBUG) {
+ Log.i(TAG, "falling back to windowSwipeToDismiss: " + windowSwipeToDismiss);
+ }
+
+ requestsPredictiveBack = windowSwipeToDismiss;
+ }
}
return requestsPredictiveBack;
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 55b823b..47d3df87 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -20,7 +20,6 @@
import static android.window.ConfigurationHelper.shouldUpdateResources;
import android.annotation.AnyThread;
-import android.annotation.BinderThread;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.app.ActivityThread;
@@ -97,9 +96,10 @@
* @param newConfig the updated {@link Configuration}
* @param newDisplayId the updated {@link android.view.Display} ID
*/
- @BinderThread
+ @AnyThread
@Override
public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
+ // TODO(b/290876897): No need to post on mHandler after migrating to ClientTransaction
mHandler.post(PooledLambda.obtainRunnable(this::onConfigurationChanged, newConfig,
newDisplayId, true /* shouldReportConfigChange */).recycleOnUse());
}
@@ -188,9 +188,10 @@
}
}
- @BinderThread
+ @AnyThread
@Override
public void onWindowTokenRemoved() {
+ // TODO(b/290876897): No need to post on mHandler after migrating to ClientTransaction
mHandler.post(PooledLambda.obtainRunnable(
WindowTokenClient::onWindowTokenRemovedInner, this).recycleOnUse());
}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index 81cd280..10336bd 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -66,10 +66,6 @@
public static final Flag SHOW_STICKY_HUN_FOR_DENIED_FSI =
releasedFlag("persist.sysui.notification.show_sticky_hun_for_denied_fsi");
- /** Gating the ability for users to dismiss ongoing event notifications */
- public static final Flag ALLOW_DISMISS_ONGOING =
- releasedFlag("persist.sysui.notification.ongoing_dismissal");
-
/** Gating the redaction of OTP notifications on the lockscreen */
public static final Flag OTP_REDACTION =
devFlag("persist.sysui.notification.otp_redaction");
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 66e3333..8a5c7ef 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -26,6 +26,7 @@
import android.util.Log;
import android.view.View;
import android.view.inputmethod.ImeTracker;
+import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.annotations.GuardedBy;
@@ -253,13 +254,11 @@
/**
* Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput(int, int, AndroidFuture)}
*
- * @param flags additional operating flags
* @param reason the reason to hide soft input
- * @see android.view.inputmethod.InputMethodManager#HIDE_IMPLICIT_ONLY
- * @see android.view.inputmethod.InputMethodManager#HIDE_NOT_ALWAYS
*/
@AnyThread
- public void hideMySoftInput(int flags, @SoftInputShowHideReason int reason) {
+ public void hideMySoftInput(@InputMethodManager.HideFlags int flags,
+ @SoftInputShowHideReason int reason) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
return;
@@ -275,13 +274,9 @@
/**
* Calls {@link IInputMethodPrivilegedOperations#showMySoftInput(int, AndroidFuture)}
- *
- * @param flags additional operating flags
- * @see android.view.inputmethod.InputMethodManager#SHOW_IMPLICIT
- * @see android.view.inputmethod.InputMethodManager#SHOW_FORCED
*/
@AnyThread
- public void showMySoftInput(int flags) {
+ public void showMySoftInput(@InputMethodManager.ShowFlags int flags) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
return;
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index c2cfcd6..66b0158 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -1565,6 +1565,13 @@
mInStealthMode = ss.isInStealthMode();
}
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ setSystemGestureExclusionRects(List.of(new Rect(left, top, right, bottom)));
+ }
+
/**
* The parecelable for saving and restoring a lock pattern view.
*/
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 15270ef..061f669 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -18,10 +18,10 @@
//#define LOG_NDEBUG 0
+#include <android-base/logging.h>
#include <android_runtime/AndroidRuntime.h>
#include <input/InputTransport.h>
#include <inttypes.h>
-#include <log/log.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
#include <utils/Looper.h>
@@ -91,7 +91,8 @@
mMessageQueue(messageQueue),
mNextPublishedSeq(1) {
if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Initializing input event sender.", getInputChannelName().c_str());
+ LOG(DEBUG) << "channel '" << getInputChannelName()
+ << "' ~ Initializing input event sender.";
}
}
@@ -108,7 +109,7 @@
void NativeInputEventSender::dispose() {
if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Disposing input event sender.", getInputChannelName().c_str());
+ LOG(DEBUG) << "channel '" << getInputChannelName() << "' ~ Disposing input event sender.";
}
mMessageQueue->getLooper()->removeFd(mInputPublisher.getChannel()->getFd());
@@ -116,7 +117,7 @@
status_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* event) {
if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Sending key event, seq=%u.", getInputChannelName().c_str(), seq);
+ LOG(DEBUG) << "channel '" << getInputChannelName() << "' ~ Sending key event, seq=" << seq;
}
uint32_t publishedSeq = mNextPublishedSeq++;
@@ -128,8 +129,8 @@
event->getMetaState(), event->getRepeatCount(),
event->getDownTime(), event->getEventTime());
if (status) {
- ALOGW("Failed to send key event on channel '%s'. status=%d",
- getInputChannelName().c_str(), status);
+ LOG(WARNING) << "Failed to send key event on channel '" << getInputChannelName()
+ << "'. status=" << statusToString(status);
return status;
}
mPublishedSeqMap.emplace(publishedSeq, seq);
@@ -138,7 +139,8 @@
status_t NativeInputEventSender::sendMotionEvent(uint32_t seq, const MotionEvent* event) {
if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Sending motion event, seq=%u.", getInputChannelName().c_str(), seq);
+ LOG(DEBUG) << "channel '" << getInputChannelName()
+ << "' ~ Sending motion event, seq=" << seq;
}
uint32_t publishedSeq;
@@ -162,8 +164,8 @@
event->getPointerProperties(),
event->getHistoricalRawPointerCoords(0, i));
if (status) {
- ALOGW("Failed to send motion event sample on channel '%s'. status=%d",
- getInputChannelName().c_str(), status);
+ LOG(WARNING) << "Failed to send motion event sample on channel '"
+ << getInputChannelName() << "'. status=" << statusToString(status);
return status;
}
// mPublishedSeqMap tracks all sequences published from this sender. Only the last
@@ -183,16 +185,18 @@
// as part of finishing an IME session, in which case the publisher will
// soon be disposed as well.
if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Consumer closed input channel or an error occurred. events=0x%x",
- getInputChannelName().c_str(), events);
+ LOG(DEBUG) << "channel '" << getInputChannelName()
+ << "' ~ Consumer closed input channel or an error occurred. events=0x"
+ << std::hex << events;
}
return 0; // remove the callback
}
if (!(events & ALOOPER_EVENT_INPUT)) {
- ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. events=0x%x",
- getInputChannelName().c_str(), events);
+ LOG(WARNING) << "channel '" << getInputChannelName()
+ << "' ~ Received spurious callback for unhandled poll event. events=0x"
+ << std::hex << events;
return 1;
}
@@ -204,13 +208,13 @@
status_t NativeInputEventSender::processConsumerResponse(JNIEnv* env) {
if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Receiving finished signals.", getInputChannelName().c_str());
+ LOG(DEBUG) << "channel '" << getInputChannelName() << "' ~ Receiving finished signals.";
}
ScopedLocalRef<jobject> senderObj(env, GetReferent(env, mSenderWeakGlobal));
if (!senderObj.get()) {
- ALOGW("channel '%s' ~ Sender object was finalized without being disposed.",
- getInputChannelName().c_str());
+ LOG(WARNING) << "channel '" << getInputChannelName()
+ << "' ~ Sender object was finalized without being disposed.";
return DEAD_OBJECT;
}
bool skipCallbacks = false; // stop calling Java functions after an exception occurs
@@ -221,8 +225,9 @@
if (status == WOULD_BLOCK) {
return OK;
}
- ALOGE("channel '%s' ~ Failed to process consumer response. status=%d",
- getInputChannelName().c_str(), status);
+ LOG(ERROR) << "channel '" << getInputChannelName()
+ << "' ~ Failed to process consumer response. status="
+ << statusToString(status);
return status;
}
@@ -250,24 +255,25 @@
const InputPublisher::Timeline& timeline = std::get<InputPublisher::Timeline>(response);
if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Received timeline, inputEventId=%" PRId32
- ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64,
- getInputChannelName().c_str(), timeline.inputEventId,
- timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME],
- timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]);
+ LOG(DEBUG) << "channel '" << getInputChannelName()
+ << "' ~ Received timeline, inputEventId=" << timeline.inputEventId
+ << ", gpuCompletedTime="
+ << timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]
+ << ", presentTime="
+ << timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
}
if (skipCallbacks) {
- ALOGW("Java exception occurred. Skipping dispatchTimelineReported for "
- "inputEventId=%" PRId32,
- timeline.inputEventId);
+ LOG(WARNING) << "Java exception occurred. Skipping dispatchTimelineReported for "
+ "inputEventId="
+ << timeline.inputEventId;
return true;
}
env->CallVoidMethod(sender, gInputEventSenderClassInfo.dispatchTimelineReported,
timeline.inputEventId, timeline.graphicsTimeline);
if (env->ExceptionCheck()) {
- ALOGE("Exception dispatching timeline, inputEventId=%" PRId32, timeline.inputEventId);
+ LOG(ERROR) << "Exception dispatching timeline, inputEventId=" << timeline.inputEventId;
return false;
}
@@ -279,7 +285,7 @@
auto it = mPublishedSeqMap.find(finished.seq);
if (it == mPublishedSeqMap.end()) {
- ALOGW("Received 'finished' signal for unknown seq number = %" PRIu32, finished.seq);
+ LOG(WARNING) << "Received 'finished' signal for unknown seq number = " << finished.seq;
// Since this is coming from the receiver (typically app), it's possible that an app
// does something wrong and sends bad data. Just ignore and process other events.
return true;
@@ -296,9 +302,9 @@
const uint32_t seq = seqOptional.value();
if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, pendingEvents=%zu.",
- getInputChannelName().c_str(), seq, finished.handled ? "true" : "false",
- mPublishedSeqMap.size());
+ LOG(DEBUG) << "channel '" << getInputChannelName()
+ << "' ~ Received finished signal, seq=" << seq << ", handled=" << std::boolalpha
+ << finished.handled << ", pendingEvents=" << mPublishedSeqMap.size();
}
if (skipCallbacks) {
return true;
@@ -307,7 +313,7 @@
env->CallVoidMethod(sender, gInputEventSenderClassInfo.dispatchInputEventFinished,
static_cast<jint>(seq), static_cast<jboolean>(finished.handled));
if (env->ExceptionCheck()) {
- ALOGE("Exception dispatching finished signal for seq=%" PRIu32, seq);
+ LOG(ERROR) << "Exception dispatching finished signal for seq=" << seq;
return false;
}
return true;
diff --git a/core/res/res/values-watch/config_material.xml b/core/res/res/values-watch/config_material.xml
index 03d3637..529f18b 100644
--- a/core/res/res/values-watch/config_material.xml
+++ b/core/res/res/values-watch/config_material.xml
@@ -30,9 +30,6 @@
<!-- Always overscan by default to ensure onApplyWindowInsets will always be called. -->
<bool name="config_windowOverscanByDefault">true</bool>
- <!-- Enable windowSwipeToDismiss. -->
- <bool name="config_windowSwipeToDismiss">true</bool>
-
<!-- Style the scrollbars accoridngly. -->
<drawable name="config_scrollbarThumbVertical">@drawable/scrollbar_vertical_thumb</drawable>
<drawable name="config_scrollbarTrackVertical">@drawable/scrollbar_vertical_track</drawable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 71da8b1..7602f69 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2624,6 +2624,17 @@
assistant activities (ACTIVITY_TYPE_ASSISTANT) -->
<bool name="config_dismissDreamOnActivityStart">false</bool>
+ <!-- Whether to send a user activity event to PowerManager when a dream quits unexpectedly so
+ that the screen won't immediately shut off.
+
+ When a dream stops unexpectedly, such as due to an app update, if the device has been
+ inactive less than the user's screen timeout, the device goes to keyguard and times out
+ back to dreaming after a few seconds. If the device has been inactive longer, the screen
+ will immediately turn off. With this flag on, the device will go back to keyguard in all
+ scenarios rather than turning off, which gives the device a chance to start dreaming
+ again. -->
+ <bool name="config_resetScreenTimeoutOnUnexpectedDreamExit">false</bool>
+
<!-- The prefixes of dream component names that are loggable.
Matched against ComponentName#flattenToString() for dream components.
If empty, logs "other" for all. -->
@@ -5606,7 +5617,7 @@
the Option 3 is selected for R.integer.config_letterboxBackgroundType.
Values < 0 or >= 1 are ignored and 0.0 (transparent) is used instead. -->
<item name="config_letterboxBackgroundWallaperDarkScrimAlpha" format="float" type="dimen">
- 0.68
+ 0.75
</item>
<!-- Corners appearance of the letterbox background.
@@ -5631,7 +5642,7 @@
but isn't supported on the device or both dark scrim alpha and blur radius aren't
provided.
-->
- <color name="config_letterboxBackgroundColor">@color/system_on_secondary_fixed</color>
+ <color name="config_letterboxBackgroundColor">@color/system_neutral1_1000</color>
<!-- Horizontal position of a center of the letterboxed app window.
0 corresponds to the left side of the screen and 1 to the right side. If given value < 0
@@ -6545,4 +6556,12 @@
serialization, a default vibration will be used.
Note that, indefinitely repeating vibrations are not allowed as shutdown vibrations. -->
<string name="config_defaultShutdownVibrationFile" />
+ <!-- The file path in which custom vibrations are provided for haptic feedbacks.
+ If the device does not specify any such file path here, if the file path specified here
+ does not exist, or if the contents of the file does not make up a valid customization
+ serialization, the system default vibrations for haptic feedback will be used.
+ If the content of the customization file is valid, the system will use the provided
+ vibrations for the customized haptic feedback IDs, and continue to use the system defaults
+ for the non-customized ones. -->
+ <string name="config_hapticFeedbackCustomizationFile" />
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 804154d..978849d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2219,6 +2219,7 @@
<java-symbol type="array" name="config_supportedDreamComplications" />
<java-symbol type="array" name="config_disabledDreamComponents" />
<java-symbol type="bool" name="config_dismissDreamOnActivityStart" />
+ <java-symbol type="bool" name="config_resetScreenTimeoutOnUnexpectedDreamExit" />
<java-symbol type="integer" name="config_dreamOverlayReconnectTimeoutMs" />
<java-symbol type="integer" name="config_dreamOverlayMaxReconnectAttempts" />
<java-symbol type="integer" name="config_minDreamOverlayDurationMs" />
@@ -5177,4 +5178,5 @@
<java-symbol type="drawable" name="focus_event_pressed_key_background" />
<java-symbol type="string" name="config_defaultShutdownVibrationFile" />
<java-symbol type="string" name="lockscreen_too_many_failed_attempts_countdown" />
+ <java-symbol type="string" name="config_hapticFeedbackCustomizationFile" />
</resources>
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 4857741..c1b55cd 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -21,6 +21,7 @@
import static android.content.Intent.ACTION_VIEW;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
@@ -29,6 +30,8 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import android.annotation.Nullable;
import android.app.Activity;
@@ -46,6 +49,7 @@
import android.app.servertransaction.NewIntentItem;
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.StopActivityItem;
+import android.app.servertransaction.WindowTokenClientController;
import android.content.Context;
import android.content.Intent;
import android.content.res.CompatibilityInfo;
@@ -70,6 +74,7 @@
import com.android.internal.content.ReferrerIntent;
import org.junit.After;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -83,7 +88,7 @@
/**
* Test for verifying {@link android.app.ActivityThread} class.
* Build/Install/Run:
- * atest FrameworksCoreTests:android.app.activity.ActivityThreadTest
+ * atest FrameworksCoreTests:ActivityThreadTest
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
@@ -100,14 +105,24 @@
new ActivityTestRule<>(TestActivity.class, true /* initialTouchMode */,
false /* launchActivity */);
+ private WindowTokenClientController mOriginalWindowTokenClientController;
+
private ArrayList<VirtualDisplay> mCreatedVirtualDisplays;
+ @Before
+ public void setup() {
+ // Keep track of the original controller, so that it can be used to restore in tearDown()
+ // when there is override in some test cases.
+ mOriginalWindowTokenClientController = WindowTokenClientController.getInstance();
+ }
+
@After
public void tearDown() {
if (mCreatedVirtualDisplays != null) {
mCreatedVirtualDisplays.forEach(VirtualDisplay::release);
mCreatedVirtualDisplays = null;
}
+ WindowTokenClientController.overrideForTesting(mOriginalWindowTokenClientController);
}
@Test
@@ -730,6 +745,39 @@
assertFalse(activity.enterPipSkipped());
}
+ @Test
+ public void testHandleWindowContextConfigurationChanged() {
+ final Activity activity = mActivityTestRule.launchActivity(new Intent());
+ final ActivityThread activityThread = activity.getActivityThread();
+ final WindowTokenClientController windowTokenClientController =
+ mock(WindowTokenClientController.class);
+ WindowTokenClientController.overrideForTesting(windowTokenClientController);
+ final IBinder clientToken = mock(IBinder.class);
+ final Configuration configuration = new Configuration();
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> activityThread
+ .handleWindowContextConfigurationChanged(
+ clientToken, configuration, DEFAULT_DISPLAY));
+
+ verify(windowTokenClientController).onWindowContextConfigurationChanged(
+ clientToken, configuration, DEFAULT_DISPLAY);
+ }
+
+ @Test
+ public void testHandleWindowContextWindowRemoval() {
+ final Activity activity = mActivityTestRule.launchActivity(new Intent());
+ final ActivityThread activityThread = activity.getActivityThread();
+ final WindowTokenClientController windowTokenClientController =
+ mock(WindowTokenClientController.class);
+ WindowTokenClientController.overrideForTesting(windowTokenClientController);
+ final IBinder clientToken = mock(IBinder.class);
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> activityThread
+ .handleWindowContextWindowRemoval(clientToken));
+
+ verify(windowTokenClientController).onWindowContextWindowRemoved(clientToken);
+ }
+
/**
* Calls {@link ActivityThread#handleActivityConfigurationChanged(ActivityClientRecord,
* Configuration, int)} to try to push activity configuration to the activity for the given
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java
new file mode 100644
index 0000000..7811e1a
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.ClientTransactionHandler;
+import android.content.res.Configuration;
+import android.os.IBinder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowContextConfigurationChangeItem}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:WindowContextConfigurationChangeItemTest
+ */
+public class WindowContextConfigurationChangeItemTest {
+
+ @Mock
+ private ClientTransactionHandler mHandler;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private PendingTransactionActions mPendingActions;
+ @Mock
+ private IBinder mClientToken;
+ // Can't mock final class.
+ private final Configuration mConfiguration = new Configuration();
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testExecute() {
+ final WindowContextConfigurationChangeItem item = WindowContextConfigurationChangeItem
+ .obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
+ item.execute(mHandler, mToken, mPendingActions);
+
+ verify(mHandler).handleWindowContextConfigurationChanged(mClientToken, mConfiguration,
+ DEFAULT_DISPLAY);
+ }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
new file mode 100644
index 0000000..2c83c70
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowContextWindowRemovalItem}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:WindowContextWindowRemovalItemTest
+ */
+public class WindowContextWindowRemovalItemTest {
+
+ @Mock
+ private ClientTransactionHandler mHandler;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private PendingTransactionActions mPendingActions;
+ @Mock
+ private IBinder mClientToken;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testExecute() {
+ final WindowContextWindowRemovalItem item = WindowContextWindowRemovalItem.obtain(
+ mClientToken);
+ item.execute(mHandler, mToken, mPendingActions);
+
+ verify(mHandler).handleWindowContextWindowRemoval(mClientToken);
+ }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowTokenClientControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowTokenClientControllerTest.java
new file mode 100644
index 0000000..3b2fe58
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowTokenClientControllerTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+import android.window.WindowTokenClient;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowTokenClientController}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:WindowTokenClientControllerTest
+ */
+@SmallTest
+@Presubmit
+public class WindowTokenClientControllerTest {
+
+ @Mock
+ private IWindowManager mWindowManagerService;
+ @Mock
+ private WindowTokenClient mWindowTokenClient;
+ @Mock
+ private IBinder mClientToken;
+ @Mock
+ private IBinder mWindowToken;
+ // Can't mock final class.
+ private final Configuration mConfiguration = new Configuration();
+
+ private IWindowManager mOriginalWindowManagerService;
+
+ private WindowTokenClientController mController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mOriginalWindowManagerService = WindowManagerGlobal.getWindowManagerService();
+ WindowManagerGlobal.overrideWindowManagerServiceForTesting(mWindowManagerService);
+ doReturn(mClientToken).when(mWindowTokenClient).asBinder();
+ mController = spy(WindowTokenClientController.getInstance());
+ }
+
+ @After
+ public void tearDown() {
+ WindowManagerGlobal.overrideWindowManagerServiceForTesting(mOriginalWindowManagerService);
+ }
+
+ @Test
+ public void testAttachToDisplayArea() throws RemoteException {
+ doReturn(null).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ any(), anyInt(), anyInt(), any());
+
+ assertFalse(mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+ DEFAULT_DISPLAY, null /* options */));
+ verify(mWindowManagerService).attachWindowContextToDisplayArea(mWindowTokenClient,
+ TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY, null /* options */);
+ verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
+
+ doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ any(), anyInt(), anyInt(), any());
+
+ assertTrue(mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+ DEFAULT_DISPLAY, null /* options */));
+ verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY,
+ false /* shouldReportConfigChange */);
+ }
+
+ @Test
+ public void testAttachToDisplayArea_detachIfNeeded() throws RemoteException {
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ doReturn(null).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ any(), anyInt(), anyInt(), any());
+ mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+ DEFAULT_DISPLAY, null /* options */);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ any(), anyInt(), anyInt(), any());
+ mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+ DEFAULT_DISPLAY, null /* options */);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService).detachWindowContextFromWindowContainer(any());
+ }
+
+ @Test
+ public void testAttachToDisplayContent() throws RemoteException {
+ doReturn(null).when(mWindowManagerService).attachToDisplayContent(
+ any(), anyInt());
+
+ assertFalse(mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY));
+ verify(mWindowManagerService).attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
+ verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
+
+ doReturn(mConfiguration).when(mWindowManagerService).attachToDisplayContent(
+ any(), anyInt());
+
+ assertTrue(mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY));
+ verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY,
+ false /* shouldReportConfigChange */);
+ }
+
+ @Test
+ public void testAttachToDisplayContent_detachIfNeeded() throws RemoteException {
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ doReturn(null).when(mWindowManagerService).attachToDisplayContent(
+ any(), anyInt());
+ mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ doReturn(mConfiguration).when(mWindowManagerService).attachToDisplayContent(
+ any(), anyInt());
+ mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService).detachWindowContextFromWindowContainer(any());
+ }
+
+ @Test
+ public void testAttachToWindowToken() throws RemoteException {
+ mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+
+ verify(mWindowManagerService).attachWindowContextToWindowToken(mWindowTokenClient,
+ mWindowToken);
+ }
+
+ @Test
+ public void testAttachToWindowToken_detachIfNeeded() throws RemoteException {
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService).detachWindowContextFromWindowContainer(any());
+ }
+
+ @Test
+ public void testOnWindowContextConfigurationChanged() {
+ mController.onWindowContextConfigurationChanged(
+ mClientToken, mConfiguration, DEFAULT_DISPLAY);
+
+ verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt());
+
+ mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+
+ mController.onWindowContextConfigurationChanged(
+ mClientToken, mConfiguration, DEFAULT_DISPLAY);
+
+ verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY);
+ }
+
+ @Test
+ public void testOnWindowContextWindowRemoved() {
+ mController.onWindowContextWindowRemoved(mClientToken);
+
+ verify(mWindowTokenClient, never()).onWindowTokenRemoved();
+
+ mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+
+ mController.onWindowContextWindowRemoved(mClientToken);
+
+ verify(mWindowTokenClient).onWindowTokenRemoved();
+ }
+}
diff --git a/core/tests/coretests/src/android/content/TEST_MAPPING b/core/tests/coretests/src/android/content/TEST_MAPPING
new file mode 100644
index 0000000..bbc2458
--- /dev/null
+++ b/core/tests/coretests/src/android/content/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.content.ContentCaptureOptionsTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
new file mode 100644
index 0000000..f8beac2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.view.contentcapture"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
new file mode 100644
index 0000000..3cd4e17
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.view.contentprotection"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
index 467d555..5f2aecc 100644
--- a/core/tests/coretests/src/android/window/WindowContextControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
@@ -37,6 +37,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -62,16 +63,24 @@
@Mock
private WindowTokenClient mMockToken;
+ private WindowTokenClientController mOriginalController;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mController = new WindowContextController(mMockToken);
doNothing().when(mMockToken).onConfigurationChanged(any(), anyInt(), anyBoolean());
- WindowTokenClientController.overrideInstance(mWindowTokenClientController);
+ mOriginalController = WindowTokenClientController.getInstance();
+ WindowTokenClientController.overrideForTesting(mWindowTokenClientController);
doReturn(true).when(mWindowTokenClientController).attachToDisplayArea(
eq(mMockToken), anyInt(), anyInt(), any());
}
+ @After
+ public void tearDown() {
+ WindowTokenClientController.overrideForTesting(mOriginalController);
+ }
+
@Test(expected = IllegalStateException.class)
public void testAttachToDisplayAreaTwiceThrowException() {
mController.attachToDisplayArea(TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY,
diff --git a/core/tests/vibrator/Android.bp b/core/tests/vibrator/Android.bp
new file mode 100644
index 0000000..829409a
--- /dev/null
+++ b/core/tests/vibrator/Android.bp
@@ -0,0 +1,40 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "FrameworksVibratorCoreTests",
+
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ static_libs: [
+ "frameworks-base-testutils",
+ "guava",
+ "androidx.core_core",
+ "androidx.test.ext.junit",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "mockito-target-minus-junit4",
+ "truth-prebuilt",
+ "testng",
+ ],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ "framework",
+ "framework-res",
+ ],
+
+ sdk_version: "core_platform",
+ test_suites: [
+ "device-tests",
+ "automotive-tests",
+ ],
+
+ certificate: "platform",
+}
diff --git a/core/tests/vibrator/AndroidManifest.xml b/core/tests/vibrator/AndroidManifest.xml
new file mode 100644
index 0000000..1ce6071
--- /dev/null
+++ b/core/tests/vibrator/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.core.tests.vibrator">
+
+ <!-- vibrator test permissions -->
+ <uses-permission android:name="android.permission.VIBRATE" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.core.tests.vibrator"
+ android:label="Frameworks Vibrator Core Tests" />
+</manifest>
diff --git a/core/tests/coretests/src/android/os/vibrator/OWNERS b/core/tests/vibrator/OWNERS
similarity index 78%
rename from core/tests/coretests/src/android/os/vibrator/OWNERS
rename to core/tests/vibrator/OWNERS
index b54d6bf..00446f2 100644
--- a/core/tests/coretests/src/android/os/vibrator/OWNERS
+++ b/core/tests/vibrator/OWNERS
@@ -1 +1,2 @@
+# Bug component: 345036
include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
\ No newline at end of file
diff --git a/core/tests/vibrator/TEST_MAPPING b/core/tests/vibrator/TEST_MAPPING
new file mode 100644
index 0000000..f3333d8
--- /dev/null
+++ b/core/tests/vibrator/TEST_MAPPING
@@ -0,0 +1,22 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksVibratorCoreTests",
+ "options": [
+ {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksVibratorCoreTests",
+ "options": [
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ }
+ ]
+}
+
diff --git a/core/tests/coretests/src/android/os/CombinedVibrationTest.java b/core/tests/vibrator/src/android/os/CombinedVibrationTest.java
similarity index 99%
rename from core/tests/coretests/src/android/os/CombinedVibrationTest.java
rename to core/tests/vibrator/src/android/os/CombinedVibrationTest.java
index 508856b..244fcff 100644
--- a/core/tests/coretests/src/android/os/CombinedVibrationTest.java
+++ b/core/tests/vibrator/src/android/os/CombinedVibrationTest.java
@@ -22,8 +22,6 @@
import static org.testng.Assert.assertThrows;
-import android.platform.test.annotations.Presubmit;
-
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -31,7 +29,6 @@
import java.util.ArrayList;
import java.util.Arrays;
-@Presubmit
@RunWith(JUnit4.class)
public class CombinedVibrationTest {
private static final VibrationEffect VALID_EFFECT = VibrationEffect.createOneShot(10, 255);
diff --git a/core/tests/coretests/src/android/os/ExternalVibrationTest.java b/core/tests/vibrator/src/android/os/ExternalVibrationTest.java
similarity index 96%
rename from core/tests/coretests/src/android/os/ExternalVibrationTest.java
rename to core/tests/vibrator/src/android/os/ExternalVibrationTest.java
index 3b872d5..587594d 100644
--- a/core/tests/coretests/src/android/os/ExternalVibrationTest.java
+++ b/core/tests/vibrator/src/android/os/ExternalVibrationTest.java
@@ -22,12 +22,14 @@
import android.media.AudioAttributes;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ExternalVibrationTest {
+ @Ignore("b/291713224")
@Test
public void testSerialization() {
AudioAttributes audio = new AudioAttributes.Builder().build();
diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
similarity index 99%
rename from core/tests/coretests/src/android/os/VibrationEffectTest.java
rename to core/tests/vibrator/src/android/os/VibrationEffectTest.java
index 73954da..8be489e 100644
--- a/core/tests/coretests/src/android/os/VibrationEffectTest.java
+++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
@@ -40,7 +40,6 @@
import android.os.VibrationEffect.Composition.UnreachableAfterRepeatingIndefinitelyException;
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.StepSegment;
-import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
@@ -54,7 +53,6 @@
import java.time.Duration;
import java.util.Arrays;
-@Presubmit
@RunWith(MockitoJUnitRunner.class)
public class VibrationEffectTest {
diff --git a/core/tests/coretests/src/android/os/VibratorInfoTest.java b/core/tests/vibrator/src/android/os/VibratorInfoTest.java
similarity index 99%
rename from core/tests/coretests/src/android/os/VibratorInfoTest.java
rename to core/tests/vibrator/src/android/os/VibratorInfoTest.java
index 88766e2..ff917aa 100644
--- a/core/tests/coretests/src/android/os/VibratorInfoTest.java
+++ b/core/tests/vibrator/src/android/os/VibratorInfoTest.java
@@ -24,13 +24,11 @@
import android.hardware.vibrator.Braking;
import android.hardware.vibrator.IVibrator;
-import android.platform.test.annotations.Presubmit;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-@Presubmit
@RunWith(JUnit4.class)
public class VibratorInfoTest {
private static final float TEST_TOLERANCE = 1e-5f;
diff --git a/core/tests/coretests/src/android/os/VibratorTest.java b/core/tests/vibrator/src/android/os/VibratorTest.java
similarity index 99%
rename from core/tests/coretests/src/android/os/VibratorTest.java
rename to core/tests/vibrator/src/android/os/VibratorTest.java
index 375fdac..c559e34 100644
--- a/core/tests/coretests/src/android/os/VibratorTest.java
+++ b/core/tests/vibrator/src/android/os/VibratorTest.java
@@ -40,7 +40,6 @@
import android.hardware.vibrator.IVibrator;
import android.media.AudioAttributes;
import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
@@ -55,13 +54,6 @@
import org.mockito.InOrder;
import org.mockito.junit.MockitoJUnitRunner;
-/**
- * Tests for {@link Vibrator}.
- *
- * Build/Install/Run:
- * atest FrameworksCoreTests:VibratorTest
- */
-@Presubmit
@RunWith(MockitoJUnitRunner.class)
public class VibratorTest {
diff --git a/core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
similarity index 99%
rename from core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java
rename to core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
index 9099274..3231192 100644
--- a/core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
@@ -29,7 +29,6 @@
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorInfo;
-import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
@@ -37,7 +36,6 @@
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
-@Presubmit
@RunWith(MockitoJUnitRunner.class)
public class PrebakedSegmentTest {
diff --git a/core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
similarity index 99%
rename from core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java
rename to core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
index 298438f..955d6ac 100644
--- a/core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
@@ -29,7 +29,6 @@
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorInfo;
-import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
@@ -37,7 +36,6 @@
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
-@Presubmit
@RunWith(MockitoJUnitRunner.class)
public class PrimitiveSegmentTest {
private static final float TOLERANCE = 1e-2f;
diff --git a/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
similarity index 99%
rename from core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java
rename to core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
index 6f8c205..dcbb56e 100644
--- a/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
@@ -29,7 +29,6 @@
import android.os.Parcel;
import android.os.VibrationEffect;
import android.os.Vibrator;
-import android.platform.test.annotations.Presubmit;
import org.junit.Rule;
import org.junit.Test;
@@ -39,7 +38,6 @@
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.junit.MockitoRule;
-@Presubmit
@RunWith(MockitoJUnitRunner.class)
public class RampSegmentTest {
private static final float TOLERANCE = 1e-2f;
diff --git a/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
similarity index 99%
rename from core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java
rename to core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
index ade2161..f9f1c08 100644
--- a/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
@@ -27,7 +27,6 @@
import android.os.Parcel;
import android.os.VibrationEffect;
import android.os.Vibrator;
-import android.platform.test.annotations.Presubmit;
import org.junit.Rule;
import org.junit.Test;
@@ -37,7 +36,6 @@
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.junit.MockitoRule;
-@Presubmit
@RunWith(MockitoJUnitRunner.class)
public class StepSegmentTest {
private static final float TOLERANCE = 1e-2f;
diff --git a/core/tests/coretests/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java b/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
similarity index 70%
rename from core/tests/coretests/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
rename to core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
index c0fdf9f..ce17170 100644
--- a/core/tests/coretests/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
@@ -28,11 +28,14 @@
import android.os.VibrationEffect;
import android.os.vibrator.PrebakedSegment;
-import android.platform.test.annotations.Presubmit;
+import android.util.Xml;
+
+import com.android.modules.utils.TypedXmlPullParser;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.xmlpull.v1.XmlPullParser;
import java.io.IOException;
import java.io.StringReader;
@@ -46,7 +49,6 @@
* <p>The {@link VibrationEffect} public APIs are covered by CTS to enforce the schema defined at
* services/core/xsd/vibrator/vibration/vibration.xsd.
*/
-@Presubmit
@RunWith(JUnit4.class)
public class VibrationEffectXmlSerializationTest {
@@ -65,6 +67,61 @@
}
@Test
+ public void testParseTag_succeedAndParserPointsToEndVibrationTag() throws Exception {
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(PRIMITIVE_CLICK)
+ .addPrimitive(PRIMITIVE_TICK, 0.2497f)
+ .compose();
+ String xml = "<vibration>"
+ + "<primitive-effect name=\"click\"/>"
+ + "<primitive-effect name=\"tick\" scale=\"0.2497\"/>"
+ + "</vibration>";
+ VibrationEffect effect2 = VibrationEffect.startComposition()
+ .addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356)
+ .addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7)
+ .compose();
+ String xml2 = "<vibration>"
+ + "<primitive-effect name=\"low_tick\" delayMs=\"356\"/>"
+ + "<primitive-effect name=\"spin\" scale=\"0.6364\" delayMs=\"7\"/>"
+ + "</vibration>";
+
+ TypedXmlPullParser parser = createXmlPullParser(xml);
+ assertParseTagSucceeds(parser, effect);
+ parser.next();
+ assertEndOfDocument(parser);
+
+ // Test no-issues when an end-tag follows the vibration XML.
+ // To test this, starting with the corresponding "start-tag" is necessary.
+ parser = createXmlPullParser("<next-tag>" + xml + "</next-tag>");
+ // Move the parser once to point to the "<vibration> tag.
+ parser.next();
+ assertParseTagSucceeds(parser, effect);
+ parser.next();
+ assertEndTag(parser, "next-tag");
+
+ parser = createXmlPullParser(xml + "<next-tag>");
+ assertParseTagSucceeds(parser, effect);
+ parser.next();
+ assertStartTag(parser, "next-tag");
+
+ parser = createXmlPullParser(xml + xml2);
+ assertParseTagSucceeds(parser, effect);
+ parser.next();
+ assertParseTagSucceeds(parser, effect2);
+ parser.next();
+ assertEndOfDocument(parser);
+ }
+
+ @Test
+ public void testParseTag_badXml_throwsException() throws Exception {
+ assertParseTagFails(
+ "<vibration>random text<primitive-effect name=\"click\"/></vibration>");
+ assertParseTagFails("<bad-tag><primitive-effect name=\"click\"/></vibration>");
+ assertParseTagFails("<primitive-effect name=\"click\"/></vibration>");
+ assertParseTagFails("<vibration><primitive-effect name=\"click\"/>");
+ }
+
+ @Test
public void testPrimitives_allSucceed() throws IOException {
VibrationEffect effect = VibrationEffect.startComposition()
.addPrimitive(PRIMITIVE_CLICK)
@@ -172,6 +229,41 @@
assertThat(parse(xml, /* flags= */ 0)).isEqualTo(effect);
}
+ private TypedXmlPullParser createXmlPullParser(String xml) throws Exception {
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ parser.setInput(new StringReader(xml));
+ parser.next(); // read START_DOCUMENT
+ return parser;
+ }
+
+ /**
+ * Asserts parsing vibration from an open TypedXmlPullParser succeeds, and that the parser
+ * points to the end "vibration" tag.
+ */
+ private void assertParseTagSucceeds(
+ TypedXmlPullParser parser, VibrationEffect effect) throws Exception {
+ assertThat(parseTag(parser)).isEqualTo(effect);
+
+ assertThat(parser.getEventType()).isEqualTo(XmlPullParser.END_TAG);
+ assertThat(parser.getName()).isEqualTo("vibration");
+ }
+
+ private void assertEndTag(TypedXmlPullParser parser, String expectedTagName) throws Exception {
+ assertThat(parser.getName()).isEqualTo(expectedTagName);
+ assertThat(parser.getEventType()).isEqualTo(parser.END_TAG);
+ }
+
+ private void assertStartTag(TypedXmlPullParser parser, String expectedTagName)
+ throws Exception {
+ assertThat(parser.getName()).isEqualTo(expectedTagName);
+ assertThat(parser.getEventType()).isEqualTo(parser.START_TAG);
+ }
+
+ private void assertEndOfDocument(TypedXmlPullParser parser) throws Exception {
+ assertThat(parser.getEventType()).isEqualTo(parser.END_DOCUMENT);
+ }
+
private void assertHiddenApisParserSucceeds(String xml, VibrationEffect effect)
throws IOException {
assertThat(parse(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS)).isEqualTo(effect);
@@ -183,6 +275,12 @@
() -> serialize(effect, /* flags= */ 0));
}
+ private void assertParseTagFails(String xml) {
+ assertThrows("Expected parsing to fail for " + xml,
+ VibrationXmlParser.VibrationXmlParserException.class,
+ () -> parseTag(createXmlPullParser(xml)));
+ }
+
private void assertPublicApisSerializerSucceeds(VibrationEffect effect,
String... expectedSegments) throws IOException {
assertSerializationContainsSegments(serialize(effect, /* flags= */ 0), expectedSegments);
@@ -214,6 +312,11 @@
return VibrationXmlParser.parse(new StringReader(xml), flags);
}
+ private static VibrationEffect parseTag(TypedXmlPullParser parser)
+ throws IOException, VibrationXmlParser.VibrationXmlParserException {
+ return VibrationXmlParser.parseTag(parser, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS);
+ }
+
private static String serialize(VibrationEffect effect, @VibrationXmlSerializer.Flags int flags)
throws IOException {
StringWriter writer = new StringWriter();
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index aa0a8d9..3206dd2 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -257,6 +257,7 @@
<permission name="android.permission.CLEAR_APP_CACHE"/>
<permission name="android.permission.ACCESS_INSTANT_APPS" />
<permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+ <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
<permission name="android.permission.DELETE_CACHE_FILES"/>
<permission name="android.permission.DELETE_PACKAGES"/>
<permission name="android.permission.DUMP"/>
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
index 2b1515a..3bb2564 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.pm.PackageManager;
import android.hardware.security.keymint.KeyParameter;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;
@@ -300,12 +299,6 @@
return false;
}
- private static boolean hasKeyMintV2() {
- PackageManager pm = android.app.AppGlobals.getInitialApplication().getPackageManager();
- return pm.hasSystemFeature(PackageManager.FEATURE_HARDWARE_KEYSTORE, 200)
- && !pm.hasSystemFeature(PackageManager.FEATURE_HARDWARE_KEYSTORE, 300);
- }
-
@Override
protected final void addAlgorithmSpecificParametersToBegin(
@NonNull List<KeyParameter> parameters, Authorization[] keyCharacteristics) {
@@ -314,12 +307,11 @@
KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest
));
// Only add the KM_TAG_RSA_OAEP_MGF_DIGEST tag to begin() if the MGF Digest is
- // present in the key properties or KeyMint version is 200. Keys generated prior to
- // Android 14 did not have this tag (Keystore didn't add it) and hence not present in
- // imported key as well, so specifying any MGF digest tag would cause a begin()
- // operation (on an Android 14 device) to fail (with a key that was generated on
- // Android 13 or below).
- if (isMgfDigestTagPresentInKeyProperties(keyCharacteristics) || hasKeyMintV2()) {
+ // present in the key properties. Keys generated prior to Android 14 did not have
+ // this tag (Keystore didn't add it) so specifying any MGF digest tag would cause
+ // a begin() operation (on an Android 14 device) to fail (with a key that was generated
+ // on Android 13 or below).
+ if (isMgfDigestTagPresentInKeyProperties(keyCharacteristics)) {
parameters.add(KeyStore2ParameterUtils.makeEnum(
KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, mKeymasterMgf1Digest
));
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index a605e2b..8bd500e 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -166,7 +166,7 @@
// *.kt sources are inside a filegroup.
"kotlin-annotations",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
manifest: "AndroidManifest.xml",
plugins: ["dagger2-compiler"],
}
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
index 0ca912e..d93e9ba 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
@@ -25,9 +25,8 @@
<ImageButton
android:id="@+id/caption_handle"
- android:layout_width="176dp"
+ android:layout_width="128dp"
android:layout_height="42dp"
- android:paddingHorizontal="24dp"
android:paddingVertical="19dp"
android:contentDescription="@string/handle_text"
android:src="@drawable/decor_handle_dark"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt
index d5d072a..122dcbb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt
@@ -94,7 +94,6 @@
* non-overlapping.
* @return The new bounds for this content.
*/
- @JvmDefault
fun calculateNewBoundsOnOverlap(
overlappingContentBounds: Rect,
otherContentBounds: List<Rect>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 20c3bd2..f5c6a03 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -203,8 +203,10 @@
Context context,
@ShellMainThread Handler mainHandler,
@ShellMainThread Choreographer mainChoreographer,
+ ShellInit shellInit,
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
+ ShellController shellController,
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopModeController> desktopModeController,
@@ -214,8 +216,10 @@
context,
mainHandler,
mainChoreographer,
+ shellInit,
taskOrganizer,
displayController,
+ shellController,
syncQueue,
transitions,
desktopModeController,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 711df0d..c05af73 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -368,7 +368,6 @@
/**
* Called when the active tasks change in desktop mode.
*/
- @JvmDefault
fun onActiveTasksChanged(displayId: Int) {}
}
@@ -379,17 +378,15 @@
/**
* Called when the desktop starts or stops showing freeform tasks.
*/
- @JvmDefault
fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {}
/**
* Called when the desktop stashed status changes.
*/
- @JvmDefault
fun onStashedChanged(displayId: Int, stashed: Boolean) {}
}
}
private fun <T> Iterable<T>.toDumpString(): String {
return joinToString(separator = ", ", prefix = "[", postfix = "]")
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
index 1acf783..22929c76 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
@@ -88,7 +88,7 @@
}
/**
- * Starts Transition of type TRANSIT_START_MOVE_TO_DESKTOP_MODE
+ * Starts Transition of type TRANSIT_START_DRAG_TO_DESKTOP_MODE
* @param wct WindowContainerTransaction for transition
* @param moveToDesktopAnimator Animator that shrinks and positions task during two part move
* to desktop animation
@@ -98,18 +98,18 @@
@NonNull MoveToDesktopAnimator moveToDesktopAnimator,
Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
mMoveToDesktopAnimator = moveToDesktopAnimator;
- startTransition(Transitions.TRANSIT_START_MOVE_TO_DESKTOP_MODE, wct,
+ startTransition(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE, wct,
onAnimationEndCallback);
}
/**
- * Starts Transition of type TRANSIT_FINALIZE_MOVE_TO_DESKTOP_MODE
+ * Starts Transition of type TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE
* @param wct WindowContainerTransaction for transition
* @param onAnimationEndCallback to be called after animation
*/
public void finalizeMoveToDesktop(@NonNull WindowContainerTransaction wct,
Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
- startTransition(Transitions.TRANSIT_FINALIZE_MOVE_TO_DESKTOP_MODE, wct,
+ startTransition(Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE, wct,
onAnimationEndCallback);
}
@@ -124,7 +124,7 @@
MoveToDesktopAnimator moveToDesktopAnimator,
Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
mMoveToDesktopAnimator = moveToDesktopAnimator;
- startTransition(Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE, wct,
+ startTransition(Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE, wct,
onAnimationEndCallback);
}
@@ -167,7 +167,7 @@
}
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (type == Transitions.TRANSIT_START_MOVE_TO_DESKTOP_MODE
+ if (type == Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
// Transitioning to freeform but keeping fullscreen bounds, so the crop is set
// to null and we don't require an animation
@@ -194,7 +194,7 @@
}
Rect endBounds = change.getEndAbsBounds();
- if (type == Transitions.TRANSIT_FINALIZE_MOVE_TO_DESKTOP_MODE
+ if (type == Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
&& !endBounds.isEmpty()) {
// This Transition animates a task to freeform bounds after being dragged into freeform
@@ -246,7 +246,7 @@
return true;
}
- if (type == Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE
+ if (type == Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
// This Transition animates a task to fullscreen after being dragged from the status
// bar and then released back into the status bar area
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index af8ef17..7699b4b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -737,12 +737,23 @@
Intent fillInIntent2 = null;
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
+ final ActivityOptions activityOptions1 = options1 != null
+ ? ActivityOptions.fromBundle(options1) : ActivityOptions.makeBasic();
+ final ActivityOptions activityOptions2 = options2 != null
+ ? ActivityOptions.fromBundle(options2) : ActivityOptions.makeBasic();
if (samePackage(packageName1, packageName2, userId1, userId2)) {
if (supportMultiInstancesSplit(packageName1)) {
fillInIntent1 = new Intent();
fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
fillInIntent2 = new Intent();
fillInIntent2.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+
+ if (shortcutInfo1 != null) {
+ activityOptions1.setApplyMultipleTaskFlagForShortcut(true);
+ }
+ if (shortcutInfo2 != null) {
+ activityOptions2.setApplyMultipleTaskFlagForShortcut(true);
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
pendingIntent2 = null;
@@ -754,9 +765,10 @@
Toast.LENGTH_SHORT).show();
}
}
- mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1, options1,
- pendingIntent2, fillInIntent2, shortcutInfo2, options2, splitPosition, splitRatio,
- remoteTransition, instanceId);
+ mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1,
+ activityOptions1.toBundle(), pendingIntent2, fillInIntent2, shortcutInfo2,
+ activityOptions2.toBundle(), splitPosition, splitRatio, remoteTransition,
+ instanceId);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index e52fd00..dc78c9b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -407,7 +407,7 @@
change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
}
// Rotation change of independent non display window container.
- if (change.getParent() == null
+ if (change.getParent() == null && !change.hasFlags(FLAG_IS_DISPLAY)
&& change.getStartRotation() != change.getEndRotation()) {
startRotationAnimation(startTransaction, change, info,
ROTATION_ANIMATION_ROTATE, animations, onAnimFinish);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index a242c72..c22cc6f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -186,9 +186,12 @@
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
final RemoteTransition remoteTransition = mRequestedRemotes.get(mergeTarget);
- final IRemoteTransition remote = remoteTransition.getRemoteTransition();
+ if (remoteTransition == null) return;
+
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Merge into remote: %s",
remoteTransition);
+
+ final IRemoteTransition remote = remoteTransition.getRemoteTransition();
if (remote == null) return;
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 4ca383f..e45dacf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -150,18 +150,18 @@
public static final int TRANSIT_RESTORE_FROM_MAXIMIZE = WindowManager.TRANSIT_FIRST_CUSTOM + 9;
/** Transition type for starting the move to desktop mode. */
- public static final int TRANSIT_START_MOVE_TO_DESKTOP_MODE =
+ public static final int TRANSIT_START_DRAG_TO_DESKTOP_MODE =
WindowManager.TRANSIT_FIRST_CUSTOM + 10;
/** Transition type for finalizing the move to desktop mode. */
- public static final int TRANSIT_FINALIZE_MOVE_TO_DESKTOP_MODE =
+ public static final int TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE =
WindowManager.TRANSIT_FIRST_CUSTOM + 11;
/** Transition type to fullscreen from desktop mode. */
public static final int TRANSIT_EXIT_DESKTOP_MODE = WindowManager.TRANSIT_FIRST_CUSTOM + 12;
/** Transition type to animate back to fullscreen when drag to freeform is cancelled. */
- public static final int TRANSIT_CANCEL_ENTERING_DESKTOP_MODE =
+ public static final int TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE =
WindowManager.TRANSIT_FIRST_CUSTOM + 13;
/** Transition type to animate the toggle resize between the max and default desktop sizes. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 80cf96a..2d7e6a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -72,6 +72,9 @@
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.sysui.KeyguardChangeListener;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.TaskCornersListener;
@@ -89,6 +92,7 @@
private final DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory;
private final ActivityTaskManager mActivityTaskManager;
private final ShellTaskOrganizer mTaskOrganizer;
+ private final ShellController mShellController;
private final Context mContext;
private final Handler mMainHandler;
private final Choreographer mMainChoreographer;
@@ -114,30 +118,37 @@
private MoveToDesktopAnimator mMoveToDesktopAnimator;
private final Rect mDragToDesktopAnimationStartBounds = new Rect();
+ private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener;
public DesktopModeWindowDecorViewModel(
Context context,
Handler mainHandler,
Choreographer mainChoreographer,
+ ShellInit shellInit,
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
+ ShellController shellController,
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopModeController> desktopModeController,
- Optional<DesktopTasksController> desktopTasksController) {
+ Optional<DesktopTasksController> desktopTasksController
+ ) {
this(
context,
mainHandler,
mainChoreographer,
+ shellInit,
taskOrganizer,
displayController,
+ shellController,
syncQueue,
transitions,
desktopModeController,
desktopTasksController,
new DesktopModeWindowDecoration.Factory(),
new InputMonitorFactory(),
- SurfaceControl.Transaction::new);
+ SurfaceControl.Transaction::new,
+ new DesktopModeKeyguardChangeListener());
}
@VisibleForTesting
@@ -145,20 +156,24 @@
Context context,
Handler mainHandler,
Choreographer mainChoreographer,
+ ShellInit shellInit,
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
+ ShellController shellController,
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopModeController> desktopModeController,
Optional<DesktopTasksController> desktopTasksController,
DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
InputMonitorFactory inputMonitorFactory,
- Supplier<SurfaceControl.Transaction> transactionFactory) {
+ Supplier<SurfaceControl.Transaction> transactionFactory,
+ DesktopModeKeyguardChangeListener desktopModeKeyguardChangeListener) {
mContext = context;
mMainHandler = mainHandler;
mMainChoreographer = mainChoreographer;
mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
mTaskOrganizer = taskOrganizer;
+ mShellController = shellController;
mDisplayController = displayController;
mSyncQueue = syncQueue;
mTransitions = transitions;
@@ -168,6 +183,13 @@
mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory;
mInputMonitorFactory = inputMonitorFactory;
mTransactionFactory = transactionFactory;
+ mDesktopModeKeyguardChangeListener = desktopModeKeyguardChangeListener;
+
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ private void onInit() {
+ mShellController.addKeyguardChangeListener(mDesktopModeKeyguardChangeListener);
}
@Override
@@ -197,8 +219,8 @@
@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change) {
if (change.getMode() == WindowManager.TRANSIT_CHANGE
- && (info.getType() == Transitions.TRANSIT_FINALIZE_MOVE_TO_DESKTOP_MODE
- || info.getType() == Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE
+ && (info.getType() == Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE
+ || info.getType() == Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE
|| info.getType() == Transitions.TRANSIT_EXIT_DESKTOP_MODE
|| info.getType() == Transitions.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE)) {
mWindowDecorByTaskId.get(change.getTaskInfo().taskId)
@@ -796,6 +818,10 @@
&& mSplitScreenController.isTaskRootOrStageRoot(taskInfo.taskId)) {
return false;
}
+ if (mDesktopModeKeyguardChangeListener.isKeyguardVisibleAndOccluded()
+ && taskInfo.isFocused) {
+ return false;
+ }
return DesktopModeStatus.isProto2Enabled()
&& taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED
&& taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
@@ -884,6 +910,22 @@
mDesktopTasksController.ifPresent(d -> d.removeCornersForTask(taskId));
}
}
+
+ static class DesktopModeKeyguardChangeListener implements KeyguardChangeListener {
+ private boolean mIsKeyguardVisible;
+ private boolean mIsKeyguardOccluded;
+
+ @Override
+ public void onKeyguardVisibilityChanged(boolean visible, boolean occluded,
+ boolean animatingDismiss) {
+ mIsKeyguardVisible = visible;
+ mIsKeyguardOccluded = occluded;
+ }
+
+ public boolean isKeyguardVisibleAndOccluded() {
+ return mIsKeyguardVisible && mIsKeyguardOccluded;
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt
index fd56a6e..8a3c2c9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt
@@ -42,7 +42,7 @@
import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary
import org.junit.Assert.assertNotNull
-internal object SplitScreenUtils {
+object SplitScreenUtils {
private const val TIMEOUT_MS = 3_000L
private const val DRAG_DURATION_MS = 1_000L
private const val NOTIFICATION_SCROLLER = "notification_stack_scroller"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
index 0f9579d..69c8ecd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.appcompat
import android.content.Context
-import android.system.helpers.CommandsHelper
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTestData
@@ -29,15 +28,18 @@
import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
import com.android.wm.shell.flicker.appWindowKeepVisible
import com.android.wm.shell.flicker.layerKeepVisible
-import org.junit.After
+
import org.junit.Assume
import org.junit.Before
+import org.junit.Rule
abstract class BaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) {
protected val context: Context = instrumentation.context
protected val letterboxApp = LetterboxAppHelper(instrumentation)
- lateinit var cmdHelper: CommandsHelper
- private lateinit var letterboxStyle: HashMap<String, String>
+
+ @JvmField
+ @Rule
+ val letterboxRule: LetterboxRule = LetterboxRule()
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
@@ -52,50 +54,7 @@
@Before
fun before() {
- cmdHelper = CommandsHelper.getInstance(instrumentation)
- Assume.assumeTrue(tapl.isTablet && isIgnoreOrientationRequest())
- letterboxStyle = mapLetterboxStyle()
- resetLetterboxStyle()
- setLetterboxEducationEnabled(false)
- }
-
- @After
- fun after() {
- resetLetterboxStyle()
- }
-
- private fun mapLetterboxStyle(): HashMap<String, String> {
- val res = cmdHelper.executeShellCommand("wm get-letterbox-style")
- val lines = res.lines()
- val map = HashMap<String, String>()
- for (line in lines) {
- val keyValuePair = line.split(":")
- if (keyValuePair.size == 2) {
- val key = keyValuePair[0].trim()
- map[key] = keyValuePair[1].trim()
- }
- }
- return map
- }
-
- private fun getLetterboxStyle(): HashMap<String, String> {
- if (!::letterboxStyle.isInitialized) {
- letterboxStyle = mapLetterboxStyle()
- }
- return letterboxStyle
- }
-
- private fun resetLetterboxStyle() {
- cmdHelper.executeShellCommand("wm reset-letterbox-style")
- }
-
- private fun setLetterboxEducationEnabled(enabled: Boolean) {
- cmdHelper.executeShellCommand("wm set-letterbox-style --isEducationEnabled $enabled")
- }
-
- private fun isIgnoreOrientationRequest(): Boolean {
- val res = cmdHelper.executeShellCommand("wm get-ignore-orientation-request")
- return res != null && res.contains("true")
+ Assume.assumeTrue(tapl.isTablet && letterboxRule.isIgnoreOrientationRequest)
}
fun FlickerTestData.setStartRotation() = setRotation(flicker.scenario.startRotation)
@@ -115,7 +74,7 @@
/** Only run on tests with config_letterboxActivityCornersRadius != 0 in devices */
private fun assumeLetterboxRoundedCornersEnabled() {
- Assume.assumeTrue(getLetterboxStyle().getValue("Corner radius") != "0")
+ Assume.assumeTrue(letterboxRule.hasCornerRadius)
}
fun assertLetterboxAppVisibleAtStartAndEnd() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
new file mode 100644
index 0000000..5a1136f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.appcompat
+
+import android.app.Instrumentation
+import android.system.helpers.CommandsHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * JUnit Rule to handle letterboxStyles and states
+ */
+class LetterboxRule(
+ private val withLetterboxEducationEnabled: Boolean = false,
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(),
+ private val cmdHelper: CommandsHelper = CommandsHelper.getInstance(instrumentation)
+) : TestRule {
+
+ private val execAdb: (String) -> String = {cmd -> cmdHelper.executeShellCommand(cmd)}
+ private lateinit var _letterboxStyle: MutableMap<String, String>
+
+ val letterboxStyle: Map<String, String>
+ get() {
+ if (!::_letterboxStyle.isInitialized) {
+ _letterboxStyle = mapLetterboxStyle()
+ }
+ return _letterboxStyle
+ }
+
+ val cornerRadius: Int?
+ get() = asInt(letterboxStyle["Corner radius"])
+
+ val hasCornerRadius: Boolean
+ get() {
+ val radius = cornerRadius
+ return radius != null && radius > 0
+ }
+
+ val isIgnoreOrientationRequest: Boolean
+ get() = execAdb("wm get-ignore-orientation-request")?.contains("true") ?: false
+
+ override fun apply(base: Statement?, description: Description?): Statement {
+ resetLetterboxStyle()
+ _letterboxStyle = mapLetterboxStyle()
+ val isLetterboxEducationEnabled = _letterboxStyle.getValue("Is education enabled")
+ var hasLetterboxEducationStateChanged = false
+ if ("$withLetterboxEducationEnabled" != isLetterboxEducationEnabled) {
+ hasLetterboxEducationStateChanged = true
+ execAdb("wm set-letterbox-style --isEducationEnabled " +
+ withLetterboxEducationEnabled)
+ }
+ return try {
+ object : Statement() {
+ @Throws(Throwable::class)
+ override fun evaluate() {
+ base!!.evaluate()
+ }
+ }
+ } finally {
+ if (hasLetterboxEducationStateChanged) {
+ execAdb("wm set-letterbox-style --isEducationEnabled " +
+ isLetterboxEducationEnabled
+ )
+ }
+ resetLetterboxStyle()
+ }
+ }
+
+ private fun mapLetterboxStyle(): HashMap<String, String> {
+ val res = execAdb("wm get-letterbox-style")
+ val lines = res.lines()
+ val map = HashMap<String, String>()
+ for (line in lines) {
+ val keyValuePair = line.split(":")
+ if (keyValuePair.size == 2) {
+ val key = keyValuePair[0].trim()
+ map[key] = keyValuePair[1].trim()
+ }
+ }
+ return map
+ }
+
+ private fun resetLetterboxStyle() {
+ execAdb("wm reset-letterbox-style")
+ }
+
+ private fun asInt(str: String?): Int? = try {
+ str?.toInt()
+ } catch (e: NumberFormatException) {
+ null
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
index a7bd258..67d5718 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
@@ -31,7 +31,7 @@
/**
* Test launching app in size compat mode.
*
- * To run this test: `atest WMShellFlickerTests:OpenAppInSizeCompatModeTest`
+ * To run this test: `atest WMShellFlickerTestsOther:OpenAppInSizeCompatModeTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
new file mode 100644
index 0000000..e6ca261
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.appcompat
+
+import android.platform.test.annotations.Postsubmit
+import android.tools.common.Rotation
+import android.tools.common.flicker.assertions.FlickerTest
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching app in size compat mode.
+ *
+ * To run this test: `atest WMShellFlickerTestsOther:OpenTransparentActivityTest`
+ *
+ * Actions:
+ * ```
+ * Launch a letteboxed app and then a transparent activity from it. We test the bounds
+ * are the same.
+ * ```
+ *
+ * Notes:
+ * ```
+ * Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [BaseTest]
+ * ```
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+class OpenTransparentActivityTest(flicker: LegacyFlickerTest) : TransparentBaseAppCompat(flicker) {
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ letterboxTranslucentLauncherApp.launchViaIntent(wmHelper)
+ }
+ transitions {
+ waitAndGetLaunchTransparent()?.click() ?: error("Launch Transparent not found")
+ }
+ teardown {
+ letterboxTranslucentApp.exit(wmHelper)
+ letterboxTranslucentLauncherApp.exit(wmHelper)
+ }
+ }
+
+ /**
+ * Checks the transparent activity is launched on top of the opaque one
+ */
+ @Postsubmit
+ @Test
+ fun translucentActivityIsLaunchedOnTopOfOpaqueActivity() {
+ flicker.assertWm {
+ this.isAppWindowOnTop(letterboxTranslucentLauncherApp)
+ .then()
+ .isAppWindowOnTop(letterboxTranslucentApp)
+ }
+ }
+
+ /**
+ * Checks that the activity is letterboxed
+ */
+ @Postsubmit
+ @Test
+ fun translucentActivityIsLetterboxed() {
+ flicker.assertLayers { isVisible(ComponentNameMatcher.LETTERBOX) }
+ }
+
+ /**
+ * Checks that the translucent activity inherits bounds from the opaque one.
+ */
+ @Postsubmit
+ @Test
+ fun translucentActivityInheritsBoundsFromOpaqueActivity() {
+ flicker.assertLayersEnd {
+ this.visibleRegion(letterboxTranslucentApp)
+ .coversExactly(visibleRegion(letterboxTranslucentLauncherApp).region)
+ }
+ }
+
+ /**
+ * Checks that the translucent activity has rounded corners
+ */
+ @Postsubmit
+ @Test
+ fun translucentActivityHasRoundedCorners() {
+ flicker.assertLayersEnd {
+ this.hasRoundedCorners(letterboxTranslucentApp)
+ }
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.rotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return LegacyFlickerTestFactory
+ .nonRotationTests(supportedRotations = listOf(Rotation.ROTATION_90))
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
index e875aae..68fa8d2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
@@ -32,7 +32,9 @@
/**
* Test launching a fixed portrait letterboxed app in landscape and repositioning to the right.
*
- * To run this test: `atest WMShellFlickerTests:RepositionFixedPortraitAppTest` Actions:
+ * To run this test: `atest WMShellFlickerTestsOther:RepositionFixedPortraitAppTest`
+ *
+ * Actions:
*
* ```
* Launch a fixed portrait app in landscape to letterbox app
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
index a18a144..fcb6931a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
@@ -31,7 +31,7 @@
/**
* Test restarting app in size compat mode.
*
- * To run this test: `atest WMShellFlickerTests:RestartAppInSizeCompatModeTest`
+ * To run this test: `atest WMShellFlickerTestsOther:RestartAppInSizeCompatModeTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt
new file mode 100644
index 0000000..ea0392c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.appcompat
+
+import android.content.Context
+import android.tools.device.flicker.legacy.FlickerTestData
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.toFlickerComponent
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.helpers.LetterboxAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.wm.shell.flicker.BaseTest
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+
+abstract class TransparentBaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+ protected val context: Context = instrumentation.context
+ protected val letterboxTranslucentLauncherApp = LetterboxAppHelper(
+ instrumentation,
+ launcherName = ActivityOptions.LaunchTransparentActivity.LABEL,
+ component = ActivityOptions.LaunchTransparentActivity.COMPONENT.toFlickerComponent()
+ )
+ protected val letterboxTranslucentApp = LetterboxAppHelper(
+ instrumentation,
+ launcherName = ActivityOptions.TransparentActivity.LABEL,
+ component = ActivityOptions.TransparentActivity.COMPONENT.toFlickerComponent()
+ )
+
+ @JvmField
+ @Rule
+ val letterboxRule: LetterboxRule = LetterboxRule()
+
+ @Before
+ fun before() {
+ Assume.assumeTrue(tapl.isTablet && letterboxRule.isIgnoreOrientationRequest)
+ }
+
+ protected fun FlickerTestData.waitAndGetLaunchTransparent(): UiObject2? =
+ device.wait(
+ Until.findObject(By.text("Launch Transparent")),
+ FIND_TIMEOUT
+ )
+
+ protected fun FlickerTestData.goBack() = device.pressBack()
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index 2f7a25e..0492a18 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
@@ -84,7 +85,7 @@
}
/** Checks that [pipApp] window is animated towards default position in right bottom corner */
- @Presubmit
+ @FlakyTest(bugId = 255578530)
@Test
fun pipLayerMovesTowardsRightBottomCorner() {
// in gestural nav the swipe makes PiP first go upwards
@@ -107,4 +108,10 @@
Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
super.focusChanges()
}
+
+ @FlakyTest(bugId = 289943985)
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavPortraitBenchmark.kt
deleted file mode 100644
index 92b6227..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavPortraitBenchmark.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
-import org.junit.Test
-
-@RequiresDevice
-class CopyContentInSplitGesturalNavPortraitBenchmark : CopyContentInSplit(Rotation.ROTATION_0) {
- @PlatinumTest(focusArea = "sysui")
- @Presubmit
- @Test
- override fun copyContentInSplit() = super.copyContentInSplit()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavLandscapeBenchmark.kt
deleted file mode 100644
index 0495079..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavLandscapeBenchmark.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
-import org.junit.Test
-
-@RequiresDevice
-class DragDividerToResizeGesturalNavLandscapeBenchmark : DragDividerToResize(Rotation.ROTATION_90) {
- @PlatinumTest(focusArea = "sysui")
- @Presubmit
- @Test
- override fun dragDividerToResize() = super.dragDividerToResize()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark.kt
deleted file mode 100644
index aab18a6..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
-import org.junit.Test
-
-@RequiresDevice
-class SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark :
- SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
- @PlatinumTest(focusArea = "sysui")
- @Presubmit
- @Test
- override fun switchBetweenSplitPairs() = super.switchBetweenSplitPairs()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark.kt
deleted file mode 100644
index 840401c..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-@RequiresDevice
-@RunWith(BlockJUnit4ClassRunner::class)
-class UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark : UnlockKeyguardToSplitScreen() {
- @PlatinumTest(focusArea = "sysui")
- @Presubmit
- @Test
- override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
similarity index 81%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt
copy to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
index 566adec..2494054 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
import org.junit.Test
-@RequiresDevice
-class CopyContentInSplitGesturalNavLandscapeBenchmark : CopyContentInSplit(Rotation.ROTATION_90) {
+class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
similarity index 81%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
index 566adec..57943ec 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
import org.junit.Test
-@RequiresDevice
-class CopyContentInSplitGesturalNavLandscapeBenchmark : CopyContentInSplit(Rotation.ROTATION_90) {
+class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
index e6d56b5..6f0e202 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
import org.junit.Test
-@RequiresDevice
-class DismissSplitScreenByDividerGesturalNavLandscapeBenchmark :
+class DismissSplitScreenByDividerGesturalNavLandscape :
DismissSplitScreenByDivider(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
index 6752c58..dac8fa2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
import org.junit.Test
-@RequiresDevice
-class DismissSplitScreenByDividerGesturalNavPortraitBenchmark :
+class DismissSplitScreenByDividerGesturalNavPortrait :
DismissSplitScreenByDivider(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
index 7c9ab99..baecc16 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
import org.junit.Test
-@RequiresDevice
-class DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark :
+class DismissSplitScreenByGoHomeGesturalNavLandscape :
DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
index 4b79571..3063ea5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
import org.junit.Test
-@RequiresDevice
-class DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark :
+class DismissSplitScreenByGoHomeGesturalNavPortrait :
DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
similarity index 81%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt
copy to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
index 71ef48b..41660ba 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
import org.junit.Test
-@RequiresDevice
-class DragDividerToResizeGesturalNavPortraitBenchmark : DragDividerToResize(Rotation.ROTATION_0) {
+class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
similarity index 81%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
index 71ef48b..c41ffb7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
import org.junit.Test
-@RequiresDevice
-class DragDividerToResizeGesturalNavPortraitBenchmark : DragDividerToResize(Rotation.ROTATION_0) {
+class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
index c78729c..afde55b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark :
+class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
index 30bce2f6..3765fc4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark :
+class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
similarity index 88%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
index b33ea7c..1e128fd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark :
+class EnterSplitScreenByDragFromNotificationGesturalNavLandscape :
EnterSplitScreenByDragFromNotification(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
index 07a86a5..7767872 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark :
+class EnterSplitScreenByDragFromNotificationGesturalNavPortrait :
EnterSplitScreenByDragFromNotification(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
index 9a1d127..4ca4bd1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark :
+class EnterSplitScreenByDragFromShortcutGesturalNavLandscape :
EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
index 266e268..2d9d258 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark :
+class EnterSplitScreenByDragFromShortcutGesturalNavPortrait :
EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
index 83fc30b..c9282ac 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark :
+class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape :
EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
index b2f1929..68c6d6c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark :
+class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait :
EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
index dae92dd..304529e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark :
+class EnterSplitScreenFromOverviewGesturalNavLandscape :
EnterSplitScreenFromOverview(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
index 732047b..53a6b44 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark :
+class EnterSplitScreenFromOverviewGesturalNavPortrait :
EnterSplitScreenFromOverview(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
index 1de7efd..a467830 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
import org.junit.Test
-@RequiresDevice
-class SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark :
+class SwitchAppByDoubleTapDividerGesturalNavLandscape :
SwitchAppByDoubleTapDivider(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
index 1a046aa..1524233 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
import org.junit.Test
-@RequiresDevice
-class SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark :
+class SwitchAppByDoubleTapDividerGesturalNavPortrait :
SwitchAppByDoubleTapDivider(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
index 6e88f0e..0389659 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark :
+class SwitchBackToSplitFromAnotherAppGesturalNavLandscape :
SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
index d26a29c..7fadf18 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark :
+class SwitchBackToSplitFromAnotherAppGesturalNavPortrait :
SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
index 4a552b0..148cc52 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark :
+class SwitchBackToSplitFromHomeGesturalNavLandscape :
SwitchBackToSplitFromHome(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
index b7376ea..0641fb0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark :
+class SwitchBackToSplitFromHomeGesturalNavPortrait :
SwitchBackToSplitFromHome(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
index b2d05e4..741f871 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark :
+class SwitchBackToSplitFromRecentGesturalNavLandscape :
SwitchBackToSplitFromRecent(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
index 6de31b1..778e2d6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark :
+class SwitchBackToSplitFromRecentGesturalNavPortrait :
SwitchBackToSplitFromRecent(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
similarity index 80%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt
copy to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
index b074f2c..dcdca70 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
@@ -14,18 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
import org.junit.Test
-@RequiresDevice
-class SwitchBetweenSplitPairsGesturalNavPortraitBenchmark :
- SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
+class SwitchBetweenSplitPairsGesturalNavLandscape : SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
similarity index 80%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
index b074f2c..3c69311 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
@@ -14,18 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
import org.junit.Test
-@RequiresDevice
-class SwitchBetweenSplitPairsGesturalNavPortraitBenchmark :
- SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
+class SwitchBetweenSplitPairsGesturalNavPortrait : SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
similarity index 82%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
index c402aa4..c6566f5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
@@ -14,19 +14,17 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
-@RequiresDevice
@RunWith(BlockJUnit4ClassRunner::class)
-class UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark : UnlockKeyguardToSplitScreen() {
+class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() {
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
similarity index 82%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt
copy to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
index c402aa4..bb1a502 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
@@ -14,19 +14,17 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
-@RequiresDevice
@RunWith(BlockJUnit4ClassRunner::class)
-class UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark : UnlockKeyguardToSplitScreen() {
+class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() {
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
index d44d177..9f9d4bb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
@@ -47,6 +47,7 @@
fun setup() {
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
+ tapl.workspace.switchToOverview().dismissAllTasks()
SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index ad4d97f..38e9f39 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -61,7 +61,7 @@
"libstaticjvmtiagent",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
plugins: ["dagger2-compiler"],
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java
index 885ae38..772d97d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java
@@ -99,7 +99,7 @@
final int taskId = 1;
WindowContainerTransaction wct = new WindowContainerTransaction();
doReturn(mToken).when(mTransitions)
- .startTransition(Transitions.TRANSIT_START_MOVE_TO_DESKTOP_MODE, wct,
+ .startTransition(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE, wct,
mEnterDesktopTaskTransitionHandler);
doReturn(taskId).when(mMoveToDesktopAnimator).getTaskId();
@@ -108,7 +108,7 @@
TransitionInfo.Change change =
createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FREEFORM);
- TransitionInfo info = createTransitionInfo(Transitions.TRANSIT_START_MOVE_TO_DESKTOP_MODE,
+ TransitionInfo info = createTransitionInfo(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE,
change);
@@ -121,7 +121,7 @@
@Test
public void testTransitEnterDesktopModeAnimation() throws Throwable {
- final int transitionType = Transitions.TRANSIT_FINALIZE_MOVE_TO_DESKTOP_MODE;
+ final int transitionType = Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE;
final int taskId = 1;
WindowContainerTransaction wct = new WindowContainerTransaction();
doReturn(mToken).when(mTransitions)
@@ -132,7 +132,7 @@
createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FREEFORM);
change.setEndAbsBounds(new Rect(0, 0, 1, 1));
TransitionInfo info = createTransitionInfo(
- Transitions.TRANSIT_FINALIZE_MOVE_TO_DESKTOP_MODE, change);
+ Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE, change);
runOnUiThread(() -> {
try {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
index adc2a6f..596d6dd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
@@ -18,12 +18,15 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -53,6 +56,8 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.desktopmode.DesktopModeController;
import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -91,6 +96,10 @@
@Mock private Supplier<SurfaceControl.Transaction> mTransactionFactory;
@Mock private SurfaceControl.Transaction mTransaction;
@Mock private Display mDisplay;
+ @Mock private ShellController mShellController;
+ @Mock private ShellInit mShellInit;
+ @Mock private DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener
+ mDesktopModeKeyguardChangeListener;
private final List<InputManager> mMockInputManagers = new ArrayList<>();
private DesktopModeWindowDecorViewModel mDesktopModeWindowDecorViewModel;
@@ -104,15 +113,18 @@
mContext,
mMainHandler,
mMainChoreographer,
+ mShellInit,
mTaskOrganizer,
mDisplayController,
+ mShellController,
mSyncQueue,
mTransitions,
Optional.of(mDesktopModeController),
Optional.of(mDesktopTasksController),
mDesktopModeWindowDecorFactory,
mMockInputMonitorFactory,
- mTransactionFactory
+ mTransactionFactory,
+ mDesktopModeKeyguardChangeListener
);
doReturn(mDesktopModeWindowDecoration)
@@ -121,6 +133,7 @@
doReturn(mTransaction).when(mTransactionFactory).get();
doReturn(mDisplayLayout).when(mDisplayController).getDisplayLayout(anyInt());
doReturn(STABLE_INSETS).when(mDisplayLayout).stableInsets();
+ doNothing().when(mShellController).addKeyguardChangeListener(any());
when(mMockInputMonitorFactory.create(any(), any())).thenReturn(mInputMonitor);
// InputChannel cannot be mocked because it passes to InputEventReceiver.
@@ -255,6 +268,32 @@
verify(mInputMonitor, times(1)).dispose();
}
+ @Test
+ public void testCaptionIsNotCreatedWhenKeyguardIsVisible() throws Exception {
+ doReturn(true).when(
+ mDesktopModeKeyguardChangeListener).isKeyguardVisibleAndOccluded();
+
+ final int taskId = 1;
+ final ActivityManager.RunningTaskInfo taskInfo =
+ createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FULLSCREEN);
+ taskInfo.isFocused = true;
+ SurfaceControl surfaceControl = mock(SurfaceControl.class);
+ runOnMainThread(() -> {
+ final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+
+ mDesktopModeWindowDecorViewModel.onTaskOpening(
+ taskInfo, surfaceControl, startT, finishT);
+
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+ mDesktopModeWindowDecorViewModel.onTaskChanging(
+ taskInfo, surfaceControl, startT, finishT);
+ });
+ verify(mDesktopModeWindowDecorFactory, never())
+ .create(any(), any(), any(), any(), any(), any(), any(), any());
+ }
+
private void runOnMainThread(Runnable r) throws Exception {
final Handler mainHandler = new Handler(Looper.getMainLooper());
final CountDownLatch latch = new CountDownLatch(1);
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 7ef82a7..ffc664c 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -61,18 +61,10 @@
ADD_FAILURE() << "ClipState not a rect"; \
}
-#define INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, functionCall) \
- TEST(test_case_name, test_name##_##pipeline) { \
- RenderPipelineType oldType = Properties::getRenderPipelineType(); \
- Properties::overrideRenderPipelineType(RenderPipelineType::pipeline); \
- functionCall; \
- Properties::overrideRenderPipelineType(oldType); \
- };
-
-#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, pipeline) \
- INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, \
- TestUtils::runOnRenderThread( \
- test_case_name##_##test_name##_RenderThreadTest::doTheThing))
+#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name) \
+ TEST(test_case_name, test_name) { \
+ TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing); \
+ }
/**
* Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope
@@ -83,21 +75,7 @@
public: \
static void doTheThing(renderthread::RenderThread& renderThread); \
}; \
- INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \
- /* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \
- /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \
- void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \
- renderthread::RenderThread& renderThread)
-
-/**
- * Like RENDERTHREAD_TEST, but only runs with the Skia RenderPipelineTypes
- */
-#define RENDERTHREAD_SKIA_PIPELINE_TEST(test_case_name, test_name) \
- class test_case_name##_##test_name##_RenderThreadTest { \
- public: \
- static void doTheThing(renderthread::RenderThread& renderThread); \
- }; \
- INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \
+ INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name); \
/* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \
/* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \
void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index f3f32eb..e227999 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -14,30 +14,32 @@
* limitations under the License.
*/
-#include "tests/common/LeakChecker.h"
-#include "tests/common/TestScene.h"
-
-#include "Properties.h"
-#include "hwui/Typeface.h"
-#include "HardwareBitmapUploader.h"
-#include "renderthread/RenderProxy.h"
-
+#include <android-base/parsebool.h>
#include <benchmark/benchmark.h>
+#include <errno.h>
+#include <fcntl.h>
#include <fnmatch.h>
#include <getopt.h>
#include <pthread.h>
#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
+
+#include <regex>
#include <string>
#include <unordered_map>
#include <vector>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
+#include "HardwareBitmapUploader.h"
+#include "Properties.h"
+#include "hwui/Typeface.h"
+#include "renderthread/RenderProxy.h"
+#include "tests/common/LeakChecker.h"
+#include "tests/common/TestScene.h"
using namespace android;
+using namespace android::base;
using namespace android::uirenderer;
using namespace android::uirenderer::test;
@@ -69,6 +71,9 @@
--onscreen Render tests on device screen. By default tests
are offscreen rendered
--benchmark_format Set output format. Possible values are tabular, json, csv
+ --benchmark_list_tests Lists the tests that would run but does not run them
+ --benchmark_filter=<regex> Filters the test set to the given regex. If prefixed with `-` and test
+ that doesn't match the given regex is run
--renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk
--skip-leak-check Skips the memory leak check
--report-gpu-memory[=verbose] Dumps the GPU memory usage after each test run
@@ -140,6 +145,9 @@
if (!strcmp(format, "tabular")) {
gBenchmarkReporter.reset(new benchmark::ConsoleReporter());
} else if (!strcmp(format, "json")) {
+ // We cannot print the leak check if outputing to JSON as that will break
+ // JSON parsers since it's not JSON-formatted
+ gRunLeakCheck = false;
gBenchmarkReporter.reset(new benchmark::JSONReporter());
} else {
fprintf(stderr, "Unknown format '%s'\n", format);
@@ -160,6 +168,24 @@
return true;
}
+static void addTestsThatMatchFilter(std::string spec) {
+ if (spec.empty() || spec == "all") {
+ spec = "."; // Regexp that matches all benchmarks
+ }
+ bool isNegativeFilter = false;
+ if (spec[0] == '-') {
+ spec.replace(0, 1, "");
+ isNegativeFilter = true;
+ }
+ std::regex re(spec, std::regex_constants::extended);
+ for (auto& iter : TestScene::testMap()) {
+ if ((isNegativeFilter && !std::regex_search(iter.first, re)) ||
+ (!isNegativeFilter && std::regex_search(iter.first, re))) {
+ gRunTests.push_back(iter.second);
+ }
+ }
+}
+
// For options that only exist in long-form. Anything in the
// 0-255 range is reserved for short options (which just use their ASCII value)
namespace LongOpts {
@@ -170,6 +196,8 @@
ReportFrametime,
CpuSet,
BenchmarkFormat,
+ BenchmarkListTests,
+ BenchmarkFilter,
Onscreen,
Offscreen,
Renderer,
@@ -179,14 +207,16 @@
}
static const struct option LONG_OPTIONS[] = {
- {"frames", required_argument, nullptr, 'f'},
- {"repeat", required_argument, nullptr, 'r'},
+ {"count", required_argument, nullptr, 'c'},
+ {"runs", required_argument, nullptr, 'r'},
{"help", no_argument, nullptr, 'h'},
{"list", no_argument, nullptr, LongOpts::List},
{"wait-for-gpu", no_argument, nullptr, LongOpts::WaitForGpu},
{"report-frametime", optional_argument, nullptr, LongOpts::ReportFrametime},
{"cpuset", required_argument, nullptr, LongOpts::CpuSet},
{"benchmark_format", required_argument, nullptr, LongOpts::BenchmarkFormat},
+ {"benchmark_list_tests", optional_argument, nullptr, LongOpts::BenchmarkListTests},
+ {"benchmark_filter", required_argument, nullptr, LongOpts::BenchmarkFilter},
{"onscreen", no_argument, nullptr, LongOpts::Onscreen},
{"offscreen", no_argument, nullptr, LongOpts::Offscreen},
{"renderer", required_argument, nullptr, LongOpts::Renderer},
@@ -197,8 +227,12 @@
static const char* SHORT_OPTIONS = "c:r:h";
void parseOptions(int argc, char* argv[]) {
+ benchmark::BenchmarkReporter::Context::executable_name = (argc > 0) ? argv[0] : "unknown";
+
int c;
bool error = false;
+ bool listTestsOnly = false;
+ bool testsAreFiltered = false;
opterr = 0;
while (true) {
@@ -272,6 +306,21 @@
}
break;
+ case LongOpts::BenchmarkListTests:
+ if (!optarg || ParseBool(optarg) == ParseBoolResult::kTrue) {
+ listTestsOnly = true;
+ }
+ break;
+
+ case LongOpts::BenchmarkFilter:
+ if (!optarg) {
+ error = true;
+ break;
+ }
+ addTestsThatMatchFilter(optarg);
+ testsAreFiltered = true;
+ break;
+
case LongOpts::Renderer:
if (!optarg) {
error = true;
@@ -346,11 +395,18 @@
}
}
} while (optind < argc);
- } else {
+ } else if (gRunTests.empty() && !testsAreFiltered) {
for (auto& iter : TestScene::testMap()) {
gRunTests.push_back(iter.second);
}
}
+
+ if (listTestsOnly) {
+ for (auto& iter : gRunTests) {
+ std::cout << iter.name << std::endl;
+ }
+ exit(EXIT_SUCCESS);
+ }
}
int main(int argc, char* argv[]) {
diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
index cc7d34b..89d00d3 100644
--- a/libs/hwui/tests/unit/CacheManagerTests.cpp
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -35,7 +35,7 @@
}
// TOOD(258700630): fix this test and re-enable
-RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, DISABLED_trimMemory) {
+RENDERTHREAD_TEST(CacheManager, DISABLED_trimMemory) {
int32_t width = DeviceInfo::get()->getWidth();
int32_t height = DeviceInfo::get()->getHeight();
GrDirectContext* grContext = renderThread.getGrContext();
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 1e055c2..073a835 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -355,7 +355,7 @@
EXPECT_EQ(3, canvas.getIndex());
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
+RENDERTHREAD_TEST(RenderNodeDrawable, emptyReceiver) {
class ProjectionTestCanvas : public SkCanvas {
public:
ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
@@ -419,7 +419,7 @@
EXPECT_EQ(2, canvas.getDrawCounter());
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) {
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
/* R is backward projected on B and C is a layer.
A
/ \
@@ -1052,7 +1052,7 @@
}
// Verify that layers are composed with linear filtering.
-RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) {
+RENDERTHREAD_TEST(RenderNodeDrawable, layerComposeQuality) {
static const int CANVAS_WIDTH = 1;
static const int CANVAS_HEIGHT = 1;
static const int LAYER_WIDTH = 1;
@@ -1170,7 +1170,7 @@
}
// Draw a vector drawable twice but with different bounds and verify correct bounds are used.
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
+RENDERTHREAD_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
static const int CANVAS_WIDTH = 100;
static const int CANVAS_HEIGHT = 200;
class VectorDrawableTestCanvas : public TestCanvasBase {
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 9aa2e1d..0f8bd13 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -370,9 +370,9 @@
}
using namespace android::uirenderer;
-RENDERTHREAD_SKIA_PIPELINE_TEST(ShaderCacheTest, testOnVkFrameFlushed) {
+RENDERTHREAD_TEST(ShaderCacheTest, testOnVkFrameFlushed) {
if (Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan) {
- // RENDERTHREAD_SKIA_PIPELINE_TEST declares both SkiaVK and SkiaGL variants.
+ // RENDERTHREAD_TEST declares both SkiaVK and SkiaGL variants.
GTEST_SKIP() << "This test is only applicable to RenderPipelineType::SkiaVulkan";
}
if (!folderExist(getExternalStorageFolder())) {
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 1a1ce1e..f6be7b2 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -136,7 +136,7 @@
}
};
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) {
+RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren) {
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
@@ -195,7 +195,7 @@
canvasContext->destroy();
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) {
+RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) {
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 6f180e7..3ded540 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -42,7 +42,7 @@
using namespace android::uirenderer::renderthread;
using namespace android::uirenderer::skiapipeline;
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) {
+RENDERTHREAD_TEST(SkiaPipeline, renderFrame) {
auto redNode = TestUtils::createSkiaNode(
0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -62,7 +62,7 @@
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
+RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckOpaque) {
auto halfGreenNode = TestUtils::createSkiaNode(
0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) {
Paint greenPaint;
@@ -89,7 +89,7 @@
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
+RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
auto redNode = TestUtils::createSkiaNode(
0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -111,7 +111,7 @@
ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) {
+RENDERTHREAD_TEST(SkiaPipeline, renderLayer) {
auto redNode = TestUtils::createSkiaNode(
0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -154,7 +154,7 @@
blueNode->setLayerSurface(sk_sp<SkSurface>());
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) {
+RENDERTHREAD_TEST(SkiaPipeline, renderOverdraw) {
ScopedProperty<bool> prop(Properties::debugOverdraw, true);
auto whiteNode = TestUtils::createSkiaNode(
@@ -227,7 +227,7 @@
};
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) {
+RENDERTHREAD_TEST(SkiaPipeline, deferRenderNodeScene) {
class DeferTestCanvas : public SkCanvas {
public:
DeferTestCanvas() : SkCanvas(800, 600) {}
@@ -297,7 +297,7 @@
EXPECT_EQ(4, surface->canvas()->mDrawCounter);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) {
+RENDERTHREAD_TEST(SkiaPipeline, clipped) {
static const int CANVAS_WIDTH = 200;
static const int CANVAS_HEIGHT = 200;
class ClippedTestCanvas : public SkCanvas {
@@ -330,7 +330,7 @@
}
// Test renderFrame with a dirty clip and a pre-transform matrix.
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped_rotated) {
+RENDERTHREAD_TEST(SkiaPipeline, clipped_rotated) {
static const int CANVAS_WIDTH = 200;
static const int CANVAS_HEIGHT = 100;
static const SkMatrix rotateMatrix = SkMatrix::MakeAll(0, -1, CANVAS_HEIGHT, 1, 0, 0, 0, 0, 1);
@@ -366,7 +366,7 @@
EXPECT_EQ(1, surface->canvas()->mDrawCounter);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) {
+RENDERTHREAD_TEST(SkiaPipeline, clip_replace) {
static const int CANVAS_WIDTH = 50;
static const int CANVAS_HEIGHT = 50;
class ClipReplaceTestCanvas : public SkCanvas {
@@ -396,7 +396,7 @@
EXPECT_EQ(1, surface->canvas()->mDrawCounter);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) {
+RENDERTHREAD_TEST(SkiaPipeline, context_lost) {
test::TestContext context;
auto surface = context.surface();
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
@@ -410,7 +410,7 @@
EXPECT_TRUE(pipeline->isSurfaceReady());
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) {
+RENDERTHREAD_TEST(SkiaPipeline, pictureCallback) {
// create a pipeline and add a picture callback
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
int callbackCount = 0;
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
index 10c874e..76cbc8a 100644
--- a/libs/hwui/tests/unit/main.cpp
+++ b/libs/hwui/tests/unit/main.cpp
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
+#include <getopt.h>
+#include <signal.h>
#include "Properties.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
#include "hwui/Typeface.h"
#include "tests/common/LeakChecker.h"
-#include <signal.h>
-
using namespace std;
using namespace android;
using namespace android::uirenderer;
@@ -45,6 +45,57 @@
raise(sig);
}
+// For options that only exist in long-form. Anything in the
+// 0-255 range is reserved for short options (which just use their ASCII value)
+namespace LongOpts {
+enum {
+ Reserved = 255,
+ Renderer,
+};
+}
+
+static const struct option LONG_OPTIONS[] = {
+ {"renderer", required_argument, nullptr, LongOpts::Renderer}, {0, 0, 0, 0}};
+
+static RenderPipelineType parseRenderer(const char* renderer) {
+ // Anything that's not skiavk is skiagl
+ if (!strcmp(renderer, "skiavk")) {
+ return RenderPipelineType::SkiaVulkan;
+ }
+ return RenderPipelineType::SkiaGL;
+}
+
+struct Options {
+ RenderPipelineType renderer = RenderPipelineType::SkiaGL;
+};
+
+Options parseOptions(int argc, char* argv[]) {
+ int c;
+ opterr = 0;
+ Options opts;
+
+ while (true) {
+ /* getopt_long stores the option index here. */
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "", LONG_OPTIONS, &option_index);
+
+ if (c == -1) break;
+
+ switch (c) {
+ case 0:
+ // Option set a flag, don't need to do anything
+ // (although none of the current LONG_OPTIONS do this...)
+ break;
+
+ case LongOpts::Renderer:
+ opts.renderer = parseRenderer(optarg);
+ break;
+ }
+ }
+ return opts;
+}
+
class TypefaceEnvironment : public testing::Environment {
public:
virtual void SetUp() { Typeface::setRobotoTypefaceForTest(); }
@@ -64,8 +115,9 @@
// Avoid talking to SF
Properties::isolatedProcess = true;
- // Default to GLES (Vulkan-aware tests will override this)
- Properties::overrideRenderPipelineType(RenderPipelineType::SkiaGL);
+
+ auto opts = parseOptions(argc, argv);
+ Properties::overrideRenderPipelineType(opts.renderer);
// Run the tests
testing::InitGoogleTest(&argc, argv);
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 1393660..91fa873 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -450,27 +450,27 @@
public static final String FEATURE_REMOTE_GROUP_PLAYBACK =
"android.media.route.feature.REMOTE_GROUP_PLAYBACK";
- final String mId;
- final CharSequence mName;
- final List<String> mFeatures;
+ private final String mId;
+ private final CharSequence mName;
+ private final List<String> mFeatures;
@Type
- final int mType;
- final boolean mIsSystem;
- final Uri mIconUri;
- final CharSequence mDescription;
+ private final int mType;
+ private final boolean mIsSystem;
+ private final Uri mIconUri;
+ private final CharSequence mDescription;
@ConnectionState
- final int mConnectionState;
- final String mClientPackageName;
- final String mPackageName;
- final int mVolumeHandling;
- final int mVolumeMax;
- final int mVolume;
- final String mAddress;
- final Set<String> mDeduplicationIds;
- final Bundle mExtras;
- final String mProviderId;
- final boolean mIsVisibilityRestricted;
- final Set<String> mAllowedPackages;
+ private final int mConnectionState;
+ private final String mClientPackageName;
+ private final String mPackageName;
+ private final int mVolumeHandling;
+ private final int mVolumeMax;
+ private final int mVolume;
+ private final String mAddress;
+ private final Set<String> mDeduplicationIds;
+ private final Bundle mExtras;
+ private final String mProviderId;
+ private final boolean mIsVisibilityRestricted;
+ private final Set<String> mAllowedPackages;
MediaRoute2Info(@NonNull Builder builder) {
mId = builder.mId;
diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS
index 6d6a9f8..bbe5e06 100644
--- a/media/java/android/media/OWNERS
+++ b/media/java/android/media/OWNERS
@@ -10,5 +10,8 @@
per-file *Image* = file:/graphics/java/android/graphics/OWNERS
+per-file ExifInterface.java,ExifInterfaceUtils.java,IMediaHTTPConnection.aidl,IMediaHTTPService.aidl,JetPlayer.java,MediaDataSource.java,MediaExtractor.java,MediaHTTPConnection.java,MediaHTTPService.java,MediaPlayer.java=set noparent
+per-file ExifInterface.java,ExifInterfaceUtils.java,IMediaHTTPConnection.aidl,IMediaHTTPService.aidl,JetPlayer.java,MediaDataSource.java,MediaExtractor.java,MediaHTTPConnection.java,MediaHTTPService.java,MediaPlayer.java=file:platform/frameworks/av:/media/janitors/media_solutions_OWNERS
+
# Haptics team also works on Ringtone
per-file *Ringtone* = file:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/media/jni/OWNERS b/media/jni/OWNERS
index 96894d1..e12d828 100644
--- a/media/jni/OWNERS
+++ b/media/jni/OWNERS
@@ -3,3 +3,6 @@
# extra for TV related files
per-file android_media_tv_*=hgchen@google.com,quxiangfang@google.com
+
+per-file android_media_JetPlayer.cpp,android_media_MediaDataSource.cpp,android_media_MediaDataSource.h,android_media_MediaPlayer.java=set noparent
+per-file android_media_JetPlayer.cpp,android_media_MediaDataSource.cpp,android_media_MediaDataSource.h,android_media_MediaPlayer.java=file:platform/frameworks/av:/media/janitors/media_solutions_OWNERS
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index dea7f03..5ea98c0c 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -2615,7 +2615,7 @@
return;
}
auto cryptoInfo =
- cryptoInfoObj ? NativeCryptoInfo{size} : NativeCryptoInfo{env, cryptoInfoObj};
+ cryptoInfoObj ? NativeCryptoInfo{env, cryptoInfoObj} : NativeCryptoInfo{size};
if (env->ExceptionCheck()) {
// Creation of cryptoInfo failed. Let the exception bubble up.
return;
diff --git a/packages/CredentialManager/Android.bp b/packages/CredentialManager/Android.bp
index 28b9bc0..fe26dc3 100644
--- a/packages/CredentialManager/Android.bp
+++ b/packages/CredentialManager/Android.bp
@@ -44,7 +44,7 @@
platform_apis: true,
privileged: true,
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
optimize: {
proguard_compatibility: false,
diff --git a/packages/EasterEgg/Android.bp b/packages/EasterEgg/Android.bp
index 8699f59..0caf505 100644
--- a/packages/EasterEgg/Android.bp
+++ b/packages/EasterEgg/Android.bp
@@ -70,5 +70,5 @@
manifest: "AndroidManifest.xml",
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
index 3505cfb..74f04e0 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
@@ -33,8 +33,6 @@
import androidx.annotation.Nullable;
-import java.io.File;
-
/**
* Installation failed: Return status code to the caller or display failure UI to user
*/
@@ -101,14 +99,8 @@
// Set header icon and title
PackageUtil.AppSnippet as;
PackageManager pm = getPackageManager();
-
- if ("package".equals(packageURI.getScheme())) {
- as = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
- pm.getApplicationIcon(appInfo));
- } else {
- final File sourceFile = new File(packageURI.getPath());
- as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
- }
+ as = intent.getParcelableExtra(PackageInstallerActivity.EXTRA_APP_SNIPPET,
+ PackageUtil.AppSnippet.class);
// Store label for dialog
mLabel = as.label;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index 7bea339..1088ace 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -16,6 +16,7 @@
package com.android.packageinstaller;
+import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_APP_SNIPPET;
import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;
import android.app.PendingIntent;
@@ -86,7 +87,8 @@
// ContentResolver.SCHEME_FILE
// STAGED_SESSION_ID extra contains an ID of a previously staged install session.
final File sourceFile = new File(mPackageURI.getPath());
- PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
+ PackageUtil.AppSnippet as = getIntent()
+ .getParcelableExtra(EXTRA_APP_SNIPPET, PackageUtil.AppSnippet.class);
mAlert.setIcon(as.icon);
mAlert.setTitle(as.label);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
index ff991d2..fbc9525 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
@@ -22,7 +22,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
@@ -30,7 +29,6 @@
import androidx.annotation.Nullable;
-import java.io.File;
import java.util.List;
/**
@@ -65,18 +63,8 @@
ApplicationInfo appInfo =
intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mAppPackageName = appInfo.packageName;
- Uri packageURI = intent.getData();
-
- // Set header icon and title
- PackageManager pm = getPackageManager();
-
- if ("package".equals(packageURI.getScheme())) {
- mAppSnippet = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
- pm.getApplicationIcon(appInfo));
- } else {
- File sourceFile = new File(packageURI.getPath());
- mAppSnippet = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
- }
+ mAppSnippet = intent.getParcelableExtra(PackageInstallerActivity.EXTRA_APP_SNIPPET,
+ PackageUtil.AppSnippet.class);
mLaunchIntent = getPackageManager().getLaunchIntentForPackage(mAppPackageName);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledReceiver.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledReceiver.java
index 2278f7c..d29765e 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledReceiver.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledReceiver.java
@@ -19,6 +19,8 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.InstallSourceInfo;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.util.Log;
@@ -61,11 +63,19 @@
return;
}
- // TODO: Make sure the installer information here is accurate
- String installer =
- context.getPackageManager().getInstallerPackageName(packageName);
- new PackageInstalledNotificationUtils(context, installer,
- packageName).postAppInstalledNotification();
+ try {
+ InstallSourceInfo installerInfo =
+ context.getPackageManager().getInstallSourceInfo(packageName);
+ String installer = installerInfo.getInstallingPackageName();
+ if (installer == null) {
+ Log.e(TAG, "No installer package name for: " + packageName);
+ return;
+ }
+ new PackageInstalledNotificationUtils(context, installer,
+ packageName).postAppInstalledNotification();
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Cannot get source info for: " + packageName);
+ }
}
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index bc7fa02..b66679a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -84,6 +84,7 @@
static final String EXTRA_CALLING_ATTRIBUTION_TAG = "EXTRA_CALLING_ATTRIBUTION_TAG";
static final String EXTRA_ORIGINAL_SOURCE_INFO = "EXTRA_ORIGINAL_SOURCE_INFO";
static final String EXTRA_STAGED_SESSION_ID = "EXTRA_STAGED_SESSION_ID";
+ static final String EXTRA_APP_SNIPPET = "EXTRA_APP_SNIPPET";
private static final String ALLOW_UNKNOWN_SOURCES_KEY =
PackageInstallerActivity.class.getName() + "ALLOW_UNKNOWN_SOURCES_KEY";
@@ -708,6 +709,9 @@
if (stagedSessionId > 0) {
newIntent.putExtra(EXTRA_STAGED_SESSION_ID, stagedSessionId);
}
+ if (mAppSnippet != null) {
+ newIntent.putExtra(EXTRA_APP_SNIPPET, mAppSnippet);
+ }
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
if (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
startActivity(newIntent);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
index c2d4f18..5880a29 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
@@ -28,8 +28,13 @@
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.UserHandle;
import android.util.Log;
import android.view.View;
@@ -117,7 +122,7 @@
icon);
}
- static final class AppSnippet {
+ static final class AppSnippet implements Parcelable {
@NonNull public CharSequence label;
@Nullable public Drawable icon;
public AppSnippet(@NonNull CharSequence label, @Nullable Drawable icon) {
@@ -125,10 +130,52 @@
this.icon = icon;
}
+ private AppSnippet(Parcel in) {
+ label = in.readString();
+ Bitmap bmp = in.readParcelable(getClass().getClassLoader(), Bitmap.class);
+ icon = new BitmapDrawable(Resources.getSystem(), bmp);
+ }
+
@Override
public String toString() {
return "AppSnippet[" + label + (icon != null ? "(has" : "(no ") + " icon)]";
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(label.toString());
+ Bitmap bmp = getBitmapFromDrawable(icon);
+ dest.writeParcelable(bmp, 0);
+ }
+
+ private Bitmap getBitmapFromDrawable(Drawable drawable) {
+ // Create an empty bitmap with the dimensions of our drawable
+ final Bitmap bmp = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight(),
+ Bitmap.Config.ARGB_8888);
+ // Associate it with a canvas. This canvas will draw the icon on the bitmap
+ final Canvas canvas = new Canvas(bmp);
+ // Draw the drawable in the canvas. The canvas will ultimately paint the drawable in the
+ // bitmap held within
+ drawable.draw(canvas);
+
+ return bmp;
+ }
+
+ public static final Parcelable.Creator<AppSnippet> CREATOR = new Parcelable.Creator<>() {
+ public AppSnippet createFromParcel(Parcel in) {
+ return new AppSnippet(in);
+ }
+
+ public AppSnippet[] newArray(int size) {
+ return new AppSnippet[size];
+ }
+ };
}
/**
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt
index c75f41b..081e668 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt
@@ -45,11 +45,10 @@
BaseLayout(
title = title,
subTitle = {
- if (singleLineSummary) {
- SettingsBody(body = summary, maxLines = 1)
- } else {
- SettingsBody(body = summary)
- }
+ SettingsBody(
+ body = summary.value,
+ maxLines = if (singleLineSummary) 1 else Int.MAX_VALUE,
+ )
},
modifier = modifier,
icon = icon,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
index 01ba8f8..57319e7 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
@@ -24,7 +24,6 @@
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
@@ -35,11 +34,6 @@
import com.android.settingslib.spa.framework.theme.toMediumWeight
@Composable
-fun SettingsTitle(title: State<String>, useMediumWeight: Boolean = false) {
- SettingsTitle(title.value, useMediumWeight)
-}
-
-@Composable
fun SettingsTitle(title: String, useMediumWeight: Boolean = false) {
Text(
text = title,
@@ -55,14 +49,6 @@
@Composable
fun SettingsBody(
- body: State<String>,
- maxLines: Int = Int.MAX_VALUE,
-) {
- SettingsBody(body = body.value, maxLines = maxLines)
-}
-
-@Composable
-fun SettingsBody(
body: String,
maxLines: Int = Int.MAX_VALUE,
) {
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/TextTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/TextTest.kt
index 7e5b4f8..f9c1f94 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/TextTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/TextTest.kt
@@ -20,7 +20,6 @@
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settingslib.spa.framework.compose.toState
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -31,14 +30,17 @@
val composeTestRule = createComposeRule()
@Test
- fun testTitle() {
+ fun settingsTitle() {
composeTestRule.setContent {
SettingsTitle(title = "myTitleValue")
- SettingsTitle(title = "myTitleState".toState())
+ }
+ composeTestRule.onNodeWithText("myTitleValue").assertIsDisplayed()
+ }
+
+ fun placeholderTitle() {
+ composeTestRule.setContent {
PlaceholderTitle(title = "myTitlePlaceholder")
}
- composeTestRule.onNodeWithText("myTitleState").assertIsDisplayed()
- composeTestRule.onNodeWithText("myTitleValue").assertIsDisplayed()
composeTestRule.onNodeWithText("myTitlePlaceholder").assertIsDisplayed()
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
index a0ff216..b43210f 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -27,14 +27,17 @@
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
-import androidx.compose.material3.Divider
+import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
+import com.android.settingslib.development.DevelopmentSettingsEnabler
import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.ui.SettingsBody
@@ -80,11 +83,22 @@
}
@Composable
- fun FooterAppVersion() {
+ fun FooterAppVersion(showPackageName: Boolean = rememberIsDevelopmentSettingsEnabled()) {
if (packageInfo.versionName == null) return
- Divider()
- Box(modifier = Modifier.padding(SettingsDimension.itemPadding)) {
+ HorizontalDivider()
+ Column(modifier = Modifier.padding(SettingsDimension.itemPadding)) {
SettingsBody(stringResource(R.string.version_text, packageInfo.versionNameBidiWrapped))
+ if (showPackageName) {
+ SettingsBody(packageInfo.packageName)
+ }
+ }
+ }
+
+ @Composable
+ private fun rememberIsDevelopmentSettingsEnabled(): Boolean {
+ val context = LocalContext.current
+ return remember {
+ DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context)
}
}
@@ -108,5 +122,5 @@
@Composable
internal fun AppLabel(app: ApplicationInfo, isClonedAppPage: Boolean = false) {
val appRepository = rememberAppRepository()
- SettingsTitle(title = appRepository.produceLabel(app, isClonedAppPage), useMediumWeight = true)
+ SettingsTitle(appRepository.produceLabel(app, isClonedAppPage).value, useMediumWeight = true)
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt
index e846687..155905b 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,26 +19,26 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
-import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
import com.android.settingslib.spaprivileged.model.app.AppRecord
@Composable
fun <T : AppRecord> AppListItemModel<T>.AppListSwitchItem(
- onClick: () -> Unit,
checked: State<Boolean?>,
changeable: State<Boolean>,
onCheckedChange: ((newChecked: Boolean) -> Unit)?,
) {
- TwoTargetSwitchPreference(
+ SwitchPreference(
model = object : SwitchPreferenceModel {
override val title = label
override val summary = this@AppListSwitchItem.summary
+ override val icon = @Composable {
+ AppIcon(record.app, SettingsDimension.appIconItemSize)
+ }
override val checked = checked
override val changeable = changeable
override val onCheckedChange = onCheckedChange
},
- icon = { AppIcon(record.app, SettingsDimension.appIconItemSize) },
- onClick = onClick,
)
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt
new file mode 100644
index 0000000..99d3840
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.template.app
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+
+@Composable
+fun <T : AppRecord> AppListItemModel<T>.AppListTwoTargetSwitchItem(
+ onClick: () -> Unit,
+ checked: State<Boolean?>,
+ changeable: State<Boolean>,
+ onCheckedChange: ((newChecked: Boolean) -> Unit)?,
+) {
+ TwoTargetSwitchPreference(
+ model = object : SwitchPreferenceModel {
+ override val title = label
+ override val summary = this@AppListTwoTargetSwitchItem.summary
+ override val checked = checked
+ override val changeable = changeable
+ override val onCheckedChange = onCheckedChange
+ },
+ icon = { AppIcon(record.app, SettingsDimension.appIconItemSize) },
+ onClick = onClick,
+ )
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppInfoTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppInfoTest.kt
index bb56c10..6831e56 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppInfoTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppInfoTest.kt
@@ -93,6 +93,7 @@
val packageInfo = PackageInfo().apply {
applicationInfo = APP
versionName = VERSION_NAME
+ packageName = PACKAGE_NAME
}
val appInfoProvider = AppInfoProvider(packageInfo)
@@ -105,9 +106,45 @@
composeTestRule.onNodeWithText("version $VERSION_NAME").assertIsDisplayed()
}
+ @Test
+ fun footerAppVersion_developmentEnabled_packageNameIsDisplayed() {
+ val packageInfo = PackageInfo().apply {
+ applicationInfo = APP
+ versionName = VERSION_NAME
+ packageName = PACKAGE_NAME
+ }
+ val appInfoProvider = AppInfoProvider(packageInfo)
+
+ composeTestRule.setContent {
+ CompositionLocalProvider(LocalContext provides context) {
+ appInfoProvider.FooterAppVersion(true)
+ }
+ }
+ composeTestRule.onNodeWithText(PACKAGE_NAME).assertIsDisplayed()
+ }
+
+
+ @Test
+ fun footerAppVersion_developmentDisabled_packageNameDoesNotExist() {
+ val packageInfo = PackageInfo().apply {
+ applicationInfo = APP
+ versionName = VERSION_NAME
+ packageName = PACKAGE_NAME
+ }
+ val appInfoProvider = AppInfoProvider(packageInfo)
+
+ composeTestRule.setContent {
+ CompositionLocalProvider(LocalContext provides context) {
+ appInfoProvider.FooterAppVersion(false)
+ }
+ }
+ composeTestRule.onNodeWithText(PACKAGE_NAME).assertDoesNotExist()
+ }
+
private companion object {
const val LABEL = "Label"
const val VERSION_NAME = "VersionName"
+ const val PACKAGE_NAME = "package.name"
val APP = object : ApplicationInfo() {
override fun loadLabel(pm: PackageManager) = LABEL
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt
index abdcd3b..2fd1b10 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,7 +43,6 @@
fun appLabel_displayed() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- onClick = {},
checked = stateOf(null),
changeable = stateOf(false),
onCheckedChange = {},
@@ -57,7 +56,6 @@
fun summary_displayed() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- onClick = {},
checked = stateOf(null),
changeable = stateOf(false),
onCheckedChange = {},
@@ -68,27 +66,9 @@
}
@Test
- fun title_onClick() {
- var titleClicked = false
- composeTestRule.setContent {
- ITEM_MODEL.AppListSwitchItem(
- onClick = { titleClicked = true },
- checked = stateOf(false),
- changeable = stateOf(false),
- onCheckedChange = {},
- )
- }
-
- composeTestRule.onNodeWithText(LABEL).performClick()
-
- assertThat(titleClicked).isTrue()
- }
-
- @Test
fun switch_checkIsNull() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- onClick = {},
checked = stateOf(null),
changeable = stateOf(false),
onCheckedChange = {},
@@ -102,7 +82,6 @@
fun switch_checked() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- onClick = {},
checked = stateOf(true),
changeable = stateOf(false),
onCheckedChange = {},
@@ -116,7 +95,6 @@
fun switch_notChecked() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- onClick = {},
checked = stateOf(false),
changeable = stateOf(false),
onCheckedChange = {},
@@ -130,7 +108,6 @@
fun switch_changeable() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- onClick = {},
checked = stateOf(false),
changeable = stateOf(true),
onCheckedChange = {},
@@ -144,7 +121,6 @@
fun switch_notChangeable() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- onClick = {},
checked = stateOf(false),
changeable = stateOf(false),
onCheckedChange = {},
@@ -159,7 +135,6 @@
var switchClicked = false
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- onClick = {},
checked = stateOf(false),
changeable = stateOf(true),
onCheckedChange = { switchClicked = true },
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt
new file mode 100644
index 0000000..6e7fc8e
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.template.app
+
+import android.content.pm.ApplicationInfo
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.assertIsOff
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.isToggleable
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class AppListTwoTargetSwitchItemTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun appLabel_displayed() {
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = {},
+ checked = stateOf(null),
+ changeable = stateOf(false),
+ onCheckedChange = {},
+ )
+ }
+
+ composeTestRule.onNodeWithText(LABEL).assertIsDisplayed()
+ }
+
+ @Test
+ fun summary_displayed() {
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = {},
+ checked = stateOf(null),
+ changeable = stateOf(false),
+ onCheckedChange = {},
+ )
+ }
+
+ composeTestRule.onNodeWithText(SUMMARY).assertIsDisplayed()
+ }
+
+ @Test
+ fun title_onClick() {
+ var titleClicked = false
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = { titleClicked = true },
+ checked = stateOf(false),
+ changeable = stateOf(false),
+ onCheckedChange = {},
+ )
+ }
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+
+ assertThat(titleClicked).isTrue()
+ }
+
+ @Test
+ fun switch_checkIsNull() {
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = {},
+ checked = stateOf(null),
+ changeable = stateOf(false),
+ onCheckedChange = {},
+ )
+ }
+
+ composeTestRule.onNode(isToggleable()).assertDoesNotExist()
+ }
+
+ @Test
+ fun switch_checked() {
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = {},
+ checked = stateOf(true),
+ changeable = stateOf(false),
+ onCheckedChange = {},
+ )
+ }
+
+ composeTestRule.onNode(isToggleable()).assertIsOn()
+ }
+
+ @Test
+ fun switch_notChecked() {
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = {},
+ checked = stateOf(false),
+ changeable = stateOf(false),
+ onCheckedChange = {},
+ )
+ }
+
+ composeTestRule.onNode(isToggleable()).assertIsOff()
+ }
+
+ @Test
+ fun switch_changeable() {
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = {},
+ checked = stateOf(false),
+ changeable = stateOf(true),
+ onCheckedChange = {},
+ )
+ }
+
+ composeTestRule.onNode(isToggleable()).assertIsEnabled()
+ }
+
+ @Test
+ fun switch_notChangeable() {
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = {},
+ checked = stateOf(false),
+ changeable = stateOf(false),
+ onCheckedChange = {},
+ )
+ }
+
+ composeTestRule.onNode(isToggleable()).assertIsNotEnabled()
+ }
+
+ @Test
+ fun switch_onClick() {
+ var switchClicked = false
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = {},
+ checked = stateOf(false),
+ changeable = stateOf(true),
+ onCheckedChange = { switchClicked = true },
+ )
+ }
+
+ composeTestRule.onNode(isToggleable()).performClick()
+
+ assertThat(switchClicked).isTrue()
+ }
+
+ private companion object {
+ const val PACKAGE_NAME = "package.name"
+ const val LABEL = "Label"
+ const val SUMMARY = "Summary"
+ val APP = ApplicationInfo().apply {
+ packageName = PACKAGE_NAME
+ }
+ val ITEM_MODEL = AppListItemModel(
+ record = object : AppRecord {
+ override val app = APP
+ },
+ label = LABEL,
+ summary = stateOf(SUMMARY),
+ )
+ }
+}
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index 612a928..1a938d6 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -179,7 +179,8 @@
* Check whether tile has order.
*/
public boolean hasOrder() {
- return mMetaData.containsKey(META_DATA_KEY_ORDER)
+ return mMetaData != null
+ && mMetaData.containsKey(META_DATA_KEY_ORDER)
&& mMetaData.get(META_DATA_KEY_ORDER) instanceof Integer;
}
@@ -204,7 +205,7 @@
CharSequence title = null;
ensureMetadataNotStale(context);
final PackageManager packageManager = context.getPackageManager();
- if (mMetaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
+ if (mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
if (mMetaData.containsKey(META_DATA_PREFERENCE_TITLE_URI)) {
// If has as uri to provide dynamic title, skip loading here. UI will later load
// at tile binding time.
diff --git a/packages/SettingsLib/res/drawable/ic_hotspot_auto.xml b/packages/SettingsLib/res/drawable/ic_hotspot_auto.xml
new file mode 100644
index 0000000..ddd526a
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_hotspot_auto.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M240,760L240,800Q240,817 228.5,828.5Q217,840 200,840L160,840Q143,840 131.5,828.5Q120,817 120,800L120,480L204,240Q210,222 225.5,211Q241,200 260,200L700,200Q719,200 734.5,211Q750,222 756,240L840,480L840,800Q840,817 828.5,828.5Q817,840 800,840L760,840Q743,840 731.5,828.5Q720,817 720,800L720,760L240,760ZM232,400L728,400L686,280L274,280L232,400ZM200,480L200,480L200,680L200,680L200,480ZM300,640Q325,640 342.5,622.5Q360,605 360,580Q360,555 342.5,537.5Q325,520 300,520Q275,520 257.5,537.5Q240,555 240,580Q240,605 257.5,622.5Q275,640 300,640ZM660,640Q685,640 702.5,622.5Q720,605 720,580Q720,555 702.5,537.5Q685,520 660,520Q635,520 617.5,537.5Q600,555 600,580Q600,605 617.5,622.5Q635,640 660,640ZM200,680L760,680L760,480L200,480L200,680Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_hotspot_laptop.xml b/packages/SettingsLib/res/drawable/ic_hotspot_laptop.xml
new file mode 100644
index 0000000..5e1b184
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_hotspot_laptop.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M0,800L0,720L80,720L80,120L880,120L880,720L960,720L960,800L0,800ZM400,720L560,720L560,680L400,680L400,720ZM160,600L800,600L800,200L160,200L160,600ZM160,600L160,200L160,200L160,600L160,600Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_hotspot_phone.xml b/packages/SettingsLib/res/drawable/ic_hotspot_phone.xml
new file mode 100644
index 0000000..baa793c
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_hotspot_phone.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M280,920Q247,920 223.5,896.5Q200,873 200,840L200,120Q200,87 223.5,63.5Q247,40 280,40L680,40Q713,40 736.5,63.5Q760,87 760,120L760,840Q760,873 736.5,896.5Q713,920 680,920L280,920ZM280,800L280,840Q280,840 280,840Q280,840 280,840L680,840Q680,840 680,840Q680,840 680,840L680,800L280,800ZM280,720L680,720L680,240L280,240L280,720ZM280,160L680,160L680,120Q680,120 680,120Q680,120 680,120L280,120Q280,120 280,120Q280,120 280,120L280,160ZM280,160L280,120Q280,120 280,120Q280,120 280,120L280,120Q280,120 280,120Q280,120 280,120L280,160L280,160ZM280,800L280,800L280,840Q280,840 280,840Q280,840 280,840L280,840Q280,840 280,840Q280,840 280,840L280,800Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_hotspot_tablet.xml b/packages/SettingsLib/res/drawable/ic_hotspot_tablet.xml
new file mode 100644
index 0000000..cf67cd9
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_hotspot_tablet.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M120,800Q87,800 63.5,776.5Q40,753 40,720L40,240Q40,207 63.5,183.5Q87,160 120,160L840,160Q873,160 896.5,183.5Q920,207 920,240L920,720Q920,753 896.5,776.5Q873,800 840,800L120,800ZM160,240L120,240Q120,240 120,240Q120,240 120,240L120,720Q120,720 120,720Q120,720 120,720L160,720L160,240ZM240,720L720,720L720,240L240,240L240,720ZM800,240L800,720L840,720Q840,720 840,720Q840,720 840,720L840,240Q840,240 840,240Q840,240 840,240L800,240ZM800,240L840,240Q840,240 840,240Q840,240 840,240L840,240Q840,240 840,240Q840,240 840,240L800,240L800,240ZM160,240L160,240L120,240Q120,240 120,240Q120,240 120,240L120,240Q120,240 120,240Q120,240 120,240L160,240Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_hotspot_watch.xml b/packages/SettingsLib/res/drawable/ic_hotspot_watch.xml
new file mode 100644
index 0000000..252a0db
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_hotspot_watch.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M360,880L306,698Q258,660 229,603Q200,546 200,480Q200,414 229,357Q258,300 306,262L360,80L600,80L654,262Q702,300 731,357Q760,414 760,480Q760,546 731,603Q702,660 654,698L600,880L360,880ZM480,680Q563,680 621.5,621.5Q680,563 680,480Q680,397 621.5,338.5Q563,280 480,280Q397,280 338.5,338.5Q280,397 280,480Q280,563 338.5,621.5Q397,680 480,680ZM404,210Q424,205 442.5,202Q461,199 480,199Q499,199 517.5,202Q536,205 556,210L540,160L420,160L404,210ZM420,800L540,800L556,750Q536,755 517.5,757.5Q499,760 480,760Q461,760 442.5,757.5Q424,755 404,750L420,800ZM404,160L420,160L540,160L556,160Q536,160 517.5,160Q499,160 480,160Q461,160 442.5,160Q424,160 404,160ZM420,800L404,800Q424,800 442.5,800Q461,800 480,800Q499,800 517.5,800Q536,800 556,800L540,800L420,800Z"/>
+</vector>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index f522fd1..2118d2c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -356,11 +356,7 @@
connectDevice();
}
- public HearingAidInfo getHearingAidInfo() {
- return mHearingAidInfo;
- }
-
- public void setHearingAidInfo(HearingAidInfo hearingAidInfo) {
+ void setHearingAidInfo(HearingAidInfo hearingAidInfo) {
mHearingAidInfo = hearingAidInfo;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 0c1b793..441d3a5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -20,6 +20,7 @@
import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.le.ScanFilter;
import android.content.Context;
import android.util.Log;
@@ -114,10 +115,21 @@
/**
* Create and return a new {@link CachedBluetoothDevice}. This assumes
* that {@link #findDevice} has already been called and returned null.
- * @param device the address of the new Bluetooth device
+ * @param device the new Bluetooth device
* @return the newly created CachedBluetoothDevice object
*/
public CachedBluetoothDevice addDevice(BluetoothDevice device) {
+ return addDevice(device, /*leScanFilters=*/null);
+ }
+
+ /**
+ * Create and return a new {@link CachedBluetoothDevice}. This assumes
+ * that {@link #findDevice} has already been called and returned null.
+ * @param device the new Bluetooth device
+ * @param leScanFilters the BLE scan filters which the device matched
+ * @return the newly created CachedBluetoothDevice object
+ */
+ public CachedBluetoothDevice addDevice(BluetoothDevice device, List<ScanFilter> leScanFilters) {
CachedBluetoothDevice newDevice;
final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
synchronized (this) {
@@ -125,7 +137,7 @@
if (newDevice == null) {
newDevice = new CachedBluetoothDevice(mContext, profileManager, device);
mCsipDeviceManager.initCsipDeviceIfNeeded(newDevice);
- mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(newDevice);
+ mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(newDevice, leScanFilters);
if (!mCsipDeviceManager.setMemberDeviceIfNeeded(newDevice)
&& !mHearingAidDeviceManager.setSubDeviceIfNeeded(newDevice)) {
mCachedDevices.add(newDevice);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
index e5e5782..efba953 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
@@ -18,10 +18,13 @@
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.le.ScanFilter;
import android.content.ContentResolver;
import android.content.Context;
import android.media.AudioDeviceAttributes;
import android.media.audiopolicy.AudioProductStrategy;
+import android.os.ParcelUuid;
import android.provider.Settings;
import android.util.Log;
@@ -59,7 +62,8 @@
mRoutingHelper = routingHelper;
}
- void initHearingAidDeviceIfNeeded(CachedBluetoothDevice newDevice) {
+ void initHearingAidDeviceIfNeeded(CachedBluetoothDevice newDevice,
+ List<ScanFilter> leScanFilters) {
long hiSyncId = getHiSyncId(newDevice.getDevice());
if (isValidHiSyncId(hiSyncId)) {
// Once hiSyncId is valid, assign hearing aid info
@@ -68,6 +72,21 @@
.setAshaDeviceMode(getDeviceMode(newDevice.getDevice()))
.setHiSyncId(hiSyncId);
newDevice.setHearingAidInfo(infoBuilder.build());
+ } else if (leScanFilters != null && !newDevice.isHearingAidDevice()) {
+ // If the device is added with hearing aid scan filter during pairing, set an empty
+ // hearing aid info to indicate it's a hearing aid device. The info will be updated
+ // when corresponding profiles connected.
+ for (ScanFilter leScanFilter: leScanFilters) {
+ final ParcelUuid serviceUuid = leScanFilter.getServiceUuid();
+ final ParcelUuid serviceDataUuid = leScanFilter.getServiceDataUuid();
+ if (BluetoothUuid.HEARING_AID.equals(serviceUuid)
+ || BluetoothUuid.HAS.equals(serviceUuid)
+ || BluetoothUuid.HEARING_AID.equals(serviceDataUuid)
+ || BluetoothUuid.HAS.equals(serviceDataUuid)) {
+ newDevice.setHearingAidInfo(new HearingAidInfo.Builder().build());
+ break;
+ }
+ }
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
index 1d433e7..5bc27195 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
@@ -59,7 +59,7 @@
@Override
public boolean isAvailable() {
- return true;
+ return mWifiManager != null;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index f911d35..a617bf3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -28,6 +28,7 @@
import android.graphics.drawable.Drawable;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
@@ -35,6 +36,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -114,6 +116,26 @@
private static final int SCREENSAVER_HOME_CONTROLS_ENABLED_DEFAULT = 1;
private static final int LOCKSCREEN_SHOW_CONTROLS_DEFAULT = 0;
+ private static final int DS_TYPE_ENABLED = FrameworkStatsLog
+ .DREAM_SETTING_CHANGED__DREAM_SETTING_TYPE__DREAM_SETTING_TYPE_ENABLED;
+ private static final int DS_TYPE_WHEN_TO_DREAM = FrameworkStatsLog
+ .DREAM_SETTING_CHANGED__DREAM_SETTING_TYPE__DREAM_SETTING_TYPE_WHEN_TO_DREAM;
+ private static final int DS_TYPE_DREAM_COMPONENT = FrameworkStatsLog
+ .DREAM_SETTING_CHANGED__DREAM_SETTING_TYPE__DREAM_SETTING_TYPE_DREAM_COMPONENT;
+ private static final int DS_TYPE_SHOW_ADDITIONAL_INFO = FrameworkStatsLog
+ .DREAM_SETTING_CHANGED__DREAM_SETTING_TYPE__DREAM_SETTING_TYPE_SHOW_ADDITIONAL_INFO;
+ private static final int DS_TYPE_SHOW_HOME_CONTROLS = FrameworkStatsLog
+ .DREAM_SETTING_CHANGED__DREAM_SETTING_TYPE__DREAM_SETTING_TYPE_SHOW_HOME_CONTROLS;
+
+ private static final int WHEN_TO_DREAM_UNSPECIFIED = FrameworkStatsLog
+ .DREAM_SETTING_CHANGED__WHEN_TO_DREAM__WHEN_TO_DREAM_UNSPECIFIED;
+ private static final int WHEN_TO_DREAM_CHARGING = FrameworkStatsLog
+ .DREAM_SETTING_CHANGED__WHEN_TO_DREAM__WHEN_TO_DREAM_WHILE_CHARGING_ONLY;
+ private static final int WHEN_TO_DREAM_DOCKED = FrameworkStatsLog
+ .DREAM_SETTING_CHANGED__WHEN_TO_DREAM__WHEN_TO_DREAM_WHILE_DOCKED_ONLY;
+ private static final int WHEN_TO_DREAM_CHARGING_OR_DOCKED = FrameworkStatsLog
+ .DREAM_SETTING_CHANGED__WHEN_TO_DREAM__WHEN_TO_DREAM_EITHER_CHARGING_OR_DOCKED;
+
private final Context mContext;
private final IDreamManager mDreamManager;
private final DreamInfoComparator mComparator;
@@ -121,6 +143,7 @@
private final boolean mDreamsActivatedOnSleepByDefault;
private final boolean mDreamsActivatedOnDockByDefault;
private final Set<ComponentName> mDisabledDreams;
+ private final List<String> mLoggableDreamPrefixes;
private Set<Integer> mSupportedComplications;
private static DreamBackend sInstance;
@@ -148,6 +171,8 @@
com.android.internal.R.array.config_disabledDreamComponents))
.map(ComponentName::unflattenFromString)
.collect(Collectors.toSet());
+ mLoggableDreamPrefixes = Arrays.stream(resources.getStringArray(
+ com.android.internal.R.array.config_loggable_dream_prefixes)).toList();
mSupportedComplications = Arrays.stream(resources.getIntArray(
com.android.internal.R.array.config_supportedDreamComplications))
@@ -282,6 +307,8 @@
default:
break;
}
+
+ logDreamSettingChangeToStatsd(DS_TYPE_WHEN_TO_DREAM);
}
/** Gets all complications which have been enabled by the user. */
@@ -304,12 +331,14 @@
public void setComplicationsEnabled(boolean enabled) {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.SCREENSAVER_COMPLICATIONS_ENABLED, enabled ? 1 : 0);
+ logDreamSettingChangeToStatsd(DS_TYPE_SHOW_ADDITIONAL_INFO);
}
/** Sets whether home controls are enabled by the user on the dream */
public void setHomeControlsEnabled(boolean enabled) {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.SCREENSAVER_HOME_CONTROLS_ENABLED, enabled ? 1 : 0);
+ logDreamSettingChangeToStatsd(DS_TYPE_SHOW_HOME_CONTROLS);
}
/** Gets whether home controls button is enabled on the dream */
@@ -353,6 +382,7 @@
public void setEnabled(boolean value) {
logd("setEnabled(%s)", value);
setBoolean(Settings.Secure.SCREENSAVER_ENABLED, value);
+ logDreamSettingChangeToStatsd(DS_TYPE_ENABLED);
}
public boolean isActivatedOnDock() {
@@ -391,6 +421,7 @@
try {
ComponentName[] dreams = {dream};
mDreamManager.setDreamComponents(dream == null ? null : dreams);
+ logDreamSettingChangeToStatsd(DS_TYPE_DREAM_COMPONENT);
} catch (RemoteException e) {
Log.w(TAG, "Failed to set active dream to " + dream, e);
}
@@ -461,6 +492,68 @@
}
}
+ private void logDreamSettingChangeToStatsd(int dreamSettingType) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.DREAM_SETTING_CHANGED, /*atom_tag*/
+ UserHandle.myUserId(), /*uid*/
+ isEnabled(), /*enabled*/
+ getActiveDreamComponentForStatsd(), /*dream_component*/
+ getWhenToDreamForStatsd(), /*when_to_dream*/
+ getComplicationsEnabled(), /*show_additional_info*/
+ getHomeControlsEnabled(), /*show_home_controls*/
+ dreamSettingType /*dream_setting_type*/
+ );
+ }
+
+ /**
+ * Returns the user selected dream component in string format for stats logging. If the dream
+ * component is not loggable, returns "other".
+ */
+ private String getActiveDreamComponentForStatsd() {
+ final ComponentName activeDream = getActiveDream();
+ if (activeDream == null) {
+ return "";
+ }
+
+ final String component = activeDream.flattenToShortString();
+ if (isLoggableDreamComponentForStatsd(component)) {
+ return component;
+ } else {
+ return "other";
+ }
+ }
+
+ /**
+ * Whether the dream component is loggable. Only components from the predefined packages are
+ * allowed to be logged for privacy.
+ */
+ private boolean isLoggableDreamComponentForStatsd(String component) {
+ for (int i = 0; i < mLoggableDreamPrefixes.size(); i++) {
+ if (component.startsWith(mLoggableDreamPrefixes.get(i))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the enum of "when to dream" setting for statsd logging.
+ */
+ private int getWhenToDreamForStatsd() {
+ switch (getWhenToDreamSetting()) {
+ case WHILE_CHARGING:
+ return WHEN_TO_DREAM_CHARGING;
+ case WHILE_DOCKED:
+ return WHEN_TO_DREAM_DOCKED;
+ case EITHER:
+ return WHEN_TO_DREAM_CHARGING_OR_DOCKED;
+ case NEVER:
+ default:
+ return WHEN_TO_DREAM_UNSPECIFIED;
+ }
+ }
+
private static class DreamInfoComparator implements Comparator<DreamInfo> {
private final ComponentName mDefaultDream;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 7a48838..7e27560 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -154,15 +154,17 @@
protected abstract RouteListingPreference getRouteListingPreference();
/**
- * Returns the list of currently active {@link RoutingSessionInfo routing sessions} known to the
- * system.
+ * Returns the list of remote {@link RoutingSessionInfo routing sessions} known to the system.
*/
@NonNull
- protected abstract List<RoutingSessionInfo> getActiveRoutingSessions();
+ protected abstract List<RoutingSessionInfo> getRemoteSessions();
@NonNull
protected abstract List<RoutingSessionInfo> getRoutingSessionsForPackage();
+ @Nullable
+ protected abstract RoutingSessionInfo getRoutingSessionById(@NonNull String sessionId);
+
@NonNull
protected abstract List<MediaRoute2Info> getAllRoutes();
@@ -496,16 +498,6 @@
return info.getName();
}
- boolean shouldDisableMediaOutput(String packageName) {
- if (TextUtils.isEmpty(packageName)) {
- Log.w(TAG, "shouldDisableMediaOutput() package name is null or empty!");
- return true;
- }
-
- // Disable when there is no transferable route
- return getTransferableRoutes(packageName).isEmpty();
- }
-
@TargetApi(Build.VERSION_CODES.R)
boolean shouldEnableVolumeSeekBar(RoutingSessionInfo sessionInfo) {
return sessionInfo.isSystemSession() // System sessions are not remote
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 8479df1..4fb0487 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -388,14 +388,12 @@
* @param volume the value of volume
*/
public void adjustSessionVolume(String sessionId, int volume) {
- final List<RoutingSessionInfo> infos = getActiveMediaSession();
- for (RoutingSessionInfo info : infos) {
- if (TextUtils.equals(sessionId, info.getId())) {
- mInfoMediaManager.adjustSessionVolume(info, volume);
- return;
- }
+ RoutingSessionInfo session = mInfoMediaManager.getRoutingSessionById(sessionId);
+ if (session != null) {
+ mInfoMediaManager.adjustSessionVolume(session, volume);
+ } else {
+ Log.w(TAG, "adjustSessionVolume: Unable to find session: " + sessionId);
}
- Log.w(TAG, "adjustSessionVolume: Unable to find session: " + sessionId);
}
/**
@@ -435,12 +433,12 @@
}
/**
- * Gets the current active session.
+ * Gets the list of remote {@link RoutingSessionInfo routing sessions} known to the system.
*
- * @return current active session list{@link android.media.RoutingSessionInfo}
+ * <p>This list does not include any system routing sessions.
*/
- public List<RoutingSessionInfo> getActiveMediaSession() {
- return mInfoMediaManager.getActiveRoutingSessions();
+ public List<RoutingSessionInfo> getRemoteRoutingSessions() {
+ return mInfoMediaManager.getRemoteSessions();
}
/**
@@ -453,13 +451,6 @@
}
/**
- * Returns {@code true} if needed to disable media output, otherwise returns {@code false}.
- */
- public boolean shouldDisableMediaOutput(String packageName) {
- return mInfoMediaManager.shouldDisableMediaOutput(packageName);
- }
-
- /**
* Returns {@code true} if needed to enable volume seekbar, otherwise returns {@code false}.
*/
public boolean shouldEnableVolumeSeekBar(RoutingSessionInfo sessionInfo) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
index b7ac1dce..0be2e0e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
@@ -30,7 +30,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -151,11 +150,22 @@
@Override
@NonNull
- protected List<RoutingSessionInfo> getActiveRoutingSessions() {
- List<RoutingSessionInfo> infos = new ArrayList<>();
- infos.add(mRouterManager.getSystemRoutingSession(null));
- infos.addAll(mRouterManager.getRemoteSessions());
- return infos;
+ protected List<RoutingSessionInfo> getRemoteSessions() {
+ return mRouterManager.getRemoteSessions();
+ }
+
+ @Nullable
+ @Override
+ protected RoutingSessionInfo getRoutingSessionById(@NonNull String sessionId) {
+ for (RoutingSessionInfo sessionInfo : getRemoteSessions()) {
+ if (TextUtils.equals(sessionInfo.getId(), sessionId)) {
+ return sessionInfo;
+ }
+ }
+
+ RoutingSessionInfo systemSession = mRouterManager.getSystemRoutingSession(null);
+
+ return TextUtils.equals(systemSession.getId(), sessionId) ? systemSession : null;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index afab046..b9a4647 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -27,6 +27,7 @@
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import android.net.wifi.WifiInfo;
+import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
@@ -331,6 +332,22 @@
}
/**
+ * Returns the Hotspot network icon resource.
+ *
+ * @param deviceType The device type of Hotspot network
+ */
+ public static int getHotspotIconResource(int deviceType) {
+ return switch (deviceType) {
+ case NetworkProviderInfo.DEVICE_TYPE_PHONE -> R.drawable.ic_hotspot_phone;
+ case NetworkProviderInfo.DEVICE_TYPE_TABLET -> R.drawable.ic_hotspot_tablet;
+ case NetworkProviderInfo.DEVICE_TYPE_LAPTOP -> R.drawable.ic_hotspot_laptop;
+ case NetworkProviderInfo.DEVICE_TYPE_WATCH -> R.drawable.ic_hotspot_watch;
+ case NetworkProviderInfo.DEVICE_TYPE_AUTO -> R.drawable.ic_hotspot_auto;
+ default -> R.drawable.ic_hotspot_phone; // Return phone icon as default.
+ };
+ }
+
+ /**
* Wrapper the {@link #getInternetIconResource} for testing compatibility.
*/
public static class InternetIconInjector {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index 0d5de88..ea10944 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -33,6 +33,8 @@
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.le.ScanFilter;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
@@ -147,7 +149,7 @@
HearingAidProfile.DeviceSide.SIDE_RIGHT);
assertThat(mCachedDevice1.getHiSyncId()).isNotEqualTo(HISYNCID1);
- mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1);
+ mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, null);
assertThat(mCachedDevice1.getHiSyncId()).isEqualTo(HISYNCID1);
assertThat(mCachedDevice1.getDeviceMode()).isEqualTo(
@@ -164,12 +166,43 @@
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(
BluetoothHearingAid.HI_SYNC_ID_INVALID);
- mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1);
+ mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, null);
verify(mCachedDevice1, never()).setHearingAidInfo(any(HearingAidInfo.class));
}
/**
+ * Test initHearingAidDeviceIfNeeded, an invalid HiSyncId and hearing aid scan filter, set an
+ * empty hearing aid info on the device.
+ */
+ @Test
+ public void initHearingAidDeviceIfNeeded_hearingAidScanFilter_setHearingAidInfo() {
+ when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(
+ BluetoothHearingAid.HI_SYNC_ID_INVALID);
+ final ScanFilter scanFilter = new ScanFilter.Builder()
+ .setServiceData(BluetoothUuid.HEARING_AID, new byte[]{0}).build();
+
+ mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, List.of(scanFilter));
+
+ assertThat(mCachedDevice1.isHearingAidDevice()).isTrue();
+ }
+
+ /**
+ * Test initHearingAidDeviceIfNeeded, an invalid HiSyncId and random scan filter, not to set
+ * hearing aid info on the device.
+ */
+ @Test
+ public void initHearingAidDeviceIfNeeded_randomScanFilter_setHearingAidInfo() {
+ when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(
+ BluetoothHearingAid.HI_SYNC_ID_INVALID);
+ final ScanFilter scanFilter = new ScanFilter.Builder().build();
+
+ mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, List.of(scanFilter));
+
+ assertThat(mCachedDevice1.isHearingAidDevice()).isFalse();
+ }
+
+ /**
* Test setSubDeviceIfNeeded, a device with same HiSyncId will be set as sub device
*/
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
index 2edf403..00ae96c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
@@ -75,6 +75,9 @@
when(res.getStringArray(
com.android.internal.R.array.config_disabledDreamComponents)).thenReturn(
new String[]{});
+ when(res.getStringArray(
+ com.android.internal.R.array.config_loggable_dream_prefixes)).thenReturn(
+ new String[]{});
mBackend = new DreamBackend(mContext);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 45b5de4..2252b69 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -832,19 +832,12 @@
}
@Test
- public void getActiveMediaSession_returnActiveSession() {
- RoutingSessionInfo sysSessionInfo = mock(RoutingSessionInfo.class);
+ public void getRemoteSessions_returnsRemoteSessions() {
final List<RoutingSessionInfo> infos = new ArrayList<>();
infos.add(mock(RoutingSessionInfo.class));
- final List<RoutingSessionInfo> activeSessionInfos = new ArrayList<>();
- activeSessionInfos.add(sysSessionInfo);
- activeSessionInfos.addAll(infos);
-
- mShadowRouter2Manager.setSystemRoutingSession(sysSessionInfo);
mShadowRouter2Manager.setRemoteSessions(infos);
- assertThat(mInfoMediaManager.getActiveRoutingSessions())
- .containsExactlyElementsIn(activeSessionInfos);
+ assertThat(mInfoMediaManager.getRemoteSessions()).containsExactlyElementsIn(infos);
}
@Test
@@ -1083,51 +1076,4 @@
assertThat(device.getState()).isEqualTo(STATE_SELECTED);
assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(device);
}
-
- @Test
- public void shouldDisableMediaOutput_infosIsEmpty_returnsTrue() {
- mShadowRouter2Manager.setTransferableRoutes(new ArrayList<>());
-
- assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isTrue();
- }
-
- @Test
- public void shouldDisableMediaOutput_infosSizeEqual1_returnsFalse() {
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
- final List<MediaRoute2Info> infos = new ArrayList<>();
- infos.add(info);
- mShadowRouter2Manager.setTransferableRoutes(infos);
-
- when(info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
-
- assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isFalse();
- }
-
- @Test
- public void shouldDisableMediaOutput_infosSizeEqual1AndNotCastDevice_returnsFalse() {
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
- final List<MediaRoute2Info> infos = new ArrayList<>();
- infos.add(info);
- mShadowRouter2Manager.setTransferableRoutes(infos);
-
- when(info.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
-
- assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isFalse();
- }
-
-
- @Test
- public void shouldDisableMediaOutput_infosSizeOverThan1_returnsFalse() {
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
- final MediaRoute2Info info2 = mock(MediaRoute2Info.class);
- final List<MediaRoute2Info> infos = new ArrayList<>();
- infos.add(info);
- infos.add(info2);
- mShadowRouter2Manager.setTransferableRoutes(infos);
-
- when(info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
- when(info2.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
-
- assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isFalse();
- }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index d6c33ff..926b41a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -433,9 +433,9 @@
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
when(info.getId()).thenReturn(TEST_SESSION_ID);
routingSessionInfos.add(info);
- when(mInfoMediaManager.getActiveRoutingSessions()).thenReturn(routingSessionInfos);
+ when(mInfoMediaManager.getRemoteSessions()).thenReturn(routingSessionInfos);
- assertThat(mLocalMediaManager.getActiveMediaSession().get(0).getId())
+ assertThat(mLocalMediaManager.getRemoteRoutingSessions().get(0).getId())
.matches(TEST_SESSION_ID);
}
@@ -544,7 +544,7 @@
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
when(info.getId()).thenReturn(TEST_SESSION_ID);
routingSessionInfos.add(info);
- when(mInfoMediaManager.getActiveRoutingSessions()).thenReturn(routingSessionInfos);
+ when(mInfoMediaManager.getRoutingSessionById(TEST_SESSION_ID)).thenReturn(info);
mLocalMediaManager.adjustSessionVolume(TEST_SESSION_ID, 10);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index b60dc6a..5293011 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -35,6 +35,7 @@
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiNetworkScoreCache;
+import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -201,6 +202,25 @@
}
@Test
+ public void getHotspotIconResource_deviceTypeUnknown_shouldNotCrash() {
+ WifiUtils.getHotspotIconResource(NetworkProviderInfo.DEVICE_TYPE_UNKNOWN);
+ }
+
+ @Test
+ public void getHotspotIconResource_deviceTypeExists_shouldNotNull() {
+ assertThat(WifiUtils.getHotspotIconResource(NetworkProviderInfo.DEVICE_TYPE_PHONE))
+ .isNotNull();
+ assertThat(WifiUtils.getHotspotIconResource(NetworkProviderInfo.DEVICE_TYPE_TABLET))
+ .isNotNull();
+ assertThat(WifiUtils.getHotspotIconResource(NetworkProviderInfo.DEVICE_TYPE_LAPTOP))
+ .isNotNull();
+ assertThat(WifiUtils.getHotspotIconResource(NetworkProviderInfo.DEVICE_TYPE_WATCH))
+ .isNotNull();
+ assertThat(WifiUtils.getHotspotIconResource(NetworkProviderInfo.DEVICE_TYPE_AUTO))
+ .isNotNull();
+ }
+
+ @Test
public void testInternetIconInjector_getIcon_returnsCorrectValues() {
WifiUtils.InternetIconInjector iconInjector = new WifiUtils.InternetIconInjector(mContext);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 7d8b066..368115b 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -320,6 +320,7 @@
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
+ <uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
<uses-permission android:name="android.permission.SUSPEND_APPS" />
<uses-permission android:name="android.permission.OBSERVE_APP_USAGE" />
<uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 7be6043..0f16d93 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -203,7 +203,7 @@
manifest: "AndroidManifest.xml",
javacflags: ["-Adagger.fastInit=enabled"],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
plugins: ["dagger2-compiler"],
@@ -394,7 +394,7 @@
"android.test.base",
"android.test.mock",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
aaptflags: [
"--extra-packages",
"com.android.systemui",
@@ -516,7 +516,7 @@
certificate: "platform",
privileged: true,
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
dxflags: ["--multi-dex"],
optimize: {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 4fd4723..58c9f77 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -549,26 +549,6 @@
</activity>
<!-- started from UsbDeviceSettingsManager -->
- <activity android:name=".usb.tv.TvUsbConfirmActivity"
- android:exported="true"
- android:launchMode="singleTop"
- android:permission="android.permission.MANAGE_USB"
- android:theme="@style/BottomSheet"
- android:finishOnCloseSystemDialogs="true"
- android:excludeFromRecents="true">
- </activity>
-
- <!-- started from UsbDeviceSettingsManager -->
- <activity android:name=".usb.tv.TvUsbPermissionActivity"
- android:exported="true"
- android:launchMode="singleTop"
- android:permission="android.permission.MANAGE_USB"
- android:theme="@style/BottomSheet"
- android:finishOnCloseSystemDialogs="true"
- android:excludeFromRecents="true">
- </activity>
-
- <!-- started from UsbDeviceSettingsManager -->
<activity android:name=".usb.UsbResolverActivity"
android:exported="true"
android:permission="android.permission.MANAGE_USB"
@@ -577,16 +557,6 @@
android:excludeFromRecents="true">
</activity>
- <!-- started from HdmiCecLocalDevicePlayback -->
- <activity android:name=".hdmi.HdmiCecSetMenuLanguageActivity"
- android:exported="true"
- android:launchMode="singleTop"
- android:permission="android.permission.CHANGE_CONFIGURATION"
- android:theme="@style/BottomSheet"
- android:finishOnCloseSystemDialogs="true"
- android:excludeFromRecents="true">
- </activity>
-
<!-- started from SensoryPrivacyService -->
<activity android:name=".sensorprivacy.SensorUseStartedActivity"
android:exported="true"
@@ -597,27 +567,6 @@
android:showForAllUsers="true">
</activity>
- <!-- started from SensoryPrivacyService -->
- <activity android:name=".sensorprivacy.television.TvUnblockSensorActivity"
- android:exported="true"
- android:launchMode="singleTop"
- android:permission="android.permission.MANAGE_SENSOR_PRIVACY"
- android:theme="@style/BottomSheet"
- android:finishOnCloseSystemDialogs="true"
- android:showForAllUsers="true">
- </activity>
-
- <!-- started from SensoryPrivacyService -->
- <activity android:name=".sensorprivacy.television.TvSensorPrivacyChangedActivity"
- android:exported="true"
- android:launchMode="singleTop"
- android:permission="android.permission.MANAGE_SENSOR_PRIVACY"
- android:theme="@style/BottomSheet"
- android:finishOnCloseSystemDialogs="true"
- android:showForAllUsers="true">
- </activity>
-
-
<!-- started from UsbDeviceSettingsManager -->
<activity android:name=".usb.UsbAccessoryUriActivity"
android:exported="true"
@@ -703,14 +652,6 @@
android:exported="false"
android:permission="android.permission.MANAGE_MEDIA_PROJECTION"/>
- <!-- started from TvNotificationPanel -->
- <activity
- android:name=".statusbar.tv.notifications.TvNotificationPanelActivity"
- android:excludeFromRecents="true"
- android:launchMode="singleTask"
- android:noHistory="true"
- android:theme="@style/TvSidePanelTheme" />
-
<!-- started from SliceProvider -->
<activity android:name=".SlicePermissionActivity"
android:theme="@style/Theme.SystemUI.Dialog.Alert"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 764a855..8306620 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -302,10 +302,9 @@
interface Callback {
/** Whether we are currently on the keyguard or not. */
- @JvmDefault fun isOnKeyguard(): Boolean = false
+ fun isOnKeyguard(): Boolean = false
/** Hide the keyguard and animate using [runner]. */
- @JvmDefault
fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner) {
throw UnsupportedOperationException()
}
@@ -316,16 +315,16 @@
interface Listener {
/** Called when an activity launch animation started. */
- @JvmDefault fun onLaunchAnimationStart() {}
+ fun onLaunchAnimationStart() {}
/**
* Called when an activity launch animation is finished. This will be called if and only if
* [onLaunchAnimationStart] was called earlier.
*/
- @JvmDefault fun onLaunchAnimationEnd() {}
+ fun onLaunchAnimationEnd() {}
/** Called when an activity launch animation made progress. */
- @JvmDefault fun onLaunchAnimationProgress(linearProgress: Float) {}
+ fun onLaunchAnimationProgress(linearProgress: Float) {}
}
/**
diff --git a/packages/SystemUI/compose/core/tests/Android.bp b/packages/SystemUI/compose/core/tests/Android.bp
index 6119e96..06d94ac 100644
--- a/packages/SystemUI/compose/core/tests/Android.bp
+++ b/packages/SystemUI/compose/core/tests/Android.bp
@@ -44,5 +44,5 @@
"androidx.compose.ui_ui-test-manifest",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/packages/SystemUI/compose/features/tests/Android.bp b/packages/SystemUI/compose/features/tests/Android.bp
index ff534bd..c7c9140 100644
--- a/packages/SystemUI/compose/features/tests/Android.bp
+++ b/packages/SystemUI/compose/features/tests/Android.bp
@@ -44,5 +44,5 @@
"androidx.compose.ui_ui-test-manifest",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/packages/SystemUI/customization/Android.bp b/packages/SystemUI/customization/Android.bp
index dc450bb..fc37b355 100644
--- a/packages/SystemUI/customization/Android.bp
+++ b/packages/SystemUI/customization/Android.bp
@@ -48,5 +48,5 @@
],
min_sdk_version: "current",
plugins: ["dagger2-compiler"],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 18753fd..006fc09 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -422,9 +422,6 @@
-packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallFlags.kt
-packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt
-packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/ConnectivitySlots.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
-packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
-packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt
-packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
@@ -696,7 +693,6 @@
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ClockTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
diff --git a/packages/SystemUI/res/anim/tv_bottom_sheet_button_state_list_animator.xml b/packages/SystemUI/res/anim/tv_bottom_sheet_button_state_list_animator.xml
deleted file mode 100644
index fc3b4ed..0000000
--- a/packages/SystemUI/res/anim/tv_bottom_sheet_button_state_list_animator.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<selector
- xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_focused="true">
- <set>
- <objectAnimator
- android:duration="200"
- android:propertyName="scaleX"
- android:valueFrom="1.0"
- android:valueTo="@dimen/bottom_sheet_button_selection_scaled"
- android:valueType="floatType"/>
- <objectAnimator
- android:duration="200"
- android:propertyName="scaleY"
- android:valueFrom="1.0"
- android:valueTo="@dimen/bottom_sheet_button_selection_scaled"
- android:valueType="floatType"/>
- </set>
- </item>
- <item android:state_focused="false">
- <set>
- <objectAnimator
- android:duration="200"
- android:propertyName="scaleX"
- android:valueFrom="@dimen/bottom_sheet_button_selection_scaled"
- android:valueTo="1.0"
- android:valueType="floatType"/>
- <objectAnimator
- android:duration="200"
- android:propertyName="scaleY"
- android:valueFrom="@dimen/bottom_sheet_button_selection_scaled"
- android:valueTo="1.0"
- android:valueType="floatType"/>
- </set>
- </item>
-</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/tv_bottom_sheet_enter.xml b/packages/SystemUI/res/anim/tv_bottom_sheet_enter.xml
deleted file mode 100644
index cace36d..0000000
--- a/packages/SystemUI/res/anim/tv_bottom_sheet_enter.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@android:interpolator/decelerate_quint">
- <translate android:fromYDelta="100%"
- android:toYDelta="0"
- android:duration="900"/>
-</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/tv_bottom_sheet_exit.xml b/packages/SystemUI/res/anim/tv_bottom_sheet_exit.xml
deleted file mode 100644
index f7efe7cd..0000000
--- a/packages/SystemUI/res/anim/tv_bottom_sheet_exit.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@android:interpolator/decelerate_quint">
- <translate android:fromYDelta="0"
- android:toYDelta="100%"
- android:duration="500"/>
-</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/tv_privacy_chip_collapse.xml b/packages/SystemUI/res/anim/tv_privacy_chip_collapse.xml
deleted file mode 100644
index 94deced..0000000
--- a/packages/SystemUI/res/anim/tv_privacy_chip_collapse.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:propertyName="collapseProgress"
- android:interpolator="@interpolator/tv_privacy_chip_collapse_interpolator"
- android:valueTo="1"
- android:valueType="floatType"
- android:duration="@integer/privacy_chip_animation_millis" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/tv_privacy_chip_expand.xml b/packages/SystemUI/res/anim/tv_privacy_chip_expand.xml
deleted file mode 100644
index 81c83e2..0000000
--- a/packages/SystemUI/res/anim/tv_privacy_chip_expand.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:propertyName="collapseProgress"
- android:interpolator="@interpolator/tv_privacy_chip_expand_interpolator"
- android:valueTo="0"
- android:valueType="floatType"
- android:duration="@integer/privacy_chip_animation_millis" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/bottom_sheet_button_text_color.xml b/packages/SystemUI/res/color/bottom_sheet_button_text_color.xml
deleted file mode 100644
index 05248f1..0000000
--- a/packages/SystemUI/res/color/bottom_sheet_button_text_color.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_focused="true"
- android:color="@color/bottom_sheet_button_text_color_focused"/>
- <item android:color="@color/bottom_sheet_button_text_color_unfocused"/>
-</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml b/packages/SystemUI/res/color/qs_dialog_btn_filled_large_background.xml
similarity index 62%
copy from packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
copy to packages/SystemUI/res/color/qs_dialog_btn_filled_large_background.xml
index 9b0bae0..f8d4af5 100644
--- a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
+++ b/packages/SystemUI/res/color/qs_dialog_btn_filled_large_background.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,8 +13,10 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_focused="true"
- android:color="@color/bottom_sheet_button_background_color_focused"/>
- <item android:color="@color/bottom_sheet_button_background_color_unfocused"/>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:state_enabled="false"
+ android:color="?androidprv:attr/materialColorPrimaryFixed"
+ android:alpha="0.30"/>
+ <item android:color="?androidprv:attr/materialColorPrimaryFixed"/>
</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml b/packages/SystemUI/res/color/qs_dialog_btn_filled_large_text.xml
similarity index 62%
copy from packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
copy to packages/SystemUI/res/color/qs_dialog_btn_filled_large_text.xml
index 9b0bae0..faba8fc 100644
--- a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
+++ b/packages/SystemUI/res/color/qs_dialog_btn_filled_large_text.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,8 +13,10 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_focused="true"
- android:color="@color/bottom_sheet_button_background_color_focused"/>
- <item android:color="@color/bottom_sheet_button_background_color_unfocused"/>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:state_enabled="false"
+ android:color="?androidprv:attr/materialColorOnPrimaryFixed"
+ android:alpha="0.30"/>
+ <item android:color="?androidprv:attr/materialColorOnPrimaryFixed"/>
</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml b/packages/SystemUI/res/color/qs_dialog_btn_outline.xml
similarity index 63%
copy from packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
copy to packages/SystemUI/res/color/qs_dialog_btn_outline.xml
index 9b0bae0..1adfe5b 100644
--- a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
+++ b/packages/SystemUI/res/color/qs_dialog_btn_outline.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,8 +13,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_focused="true"
- android:color="@color/bottom_sheet_button_background_color_focused"/>
- <item android:color="@color/bottom_sheet_button_background_color_unfocused"/>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:state_enabled="false"
+ android:color="?androidprv:attr/materialColorPrimary"
+ android:alpha="0.30"/>
+ <item android:color="?androidprv:attr/materialColorPrimary"/>
</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml b/packages/SystemUI/res/color/qs_dialog_btn_outline_text.xml
similarity index 63%
rename from packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
rename to packages/SystemUI/res/color/qs_dialog_btn_outline_text.xml
index 9b0bae0..5dc994f23 100644
--- a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
+++ b/packages/SystemUI/res/color/qs_dialog_btn_outline_text.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,8 +13,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_focused="true"
- android:color="@color/bottom_sheet_button_background_color_focused"/>
- <item android:color="@color/bottom_sheet_button_background_color_unfocused"/>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:state_enabled="false"
+ android:color="?androidprv:attr/materialColorOnSurface"
+ android:alpha="0.30"/>
+ <item android:color="?androidprv:attr/materialColorOnSurface"/>
</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/bottom_sheet_background.xml b/packages/SystemUI/res/drawable/bottom_sheet_background.xml
deleted file mode 100644
index 87850a0..0000000
--- a/packages/SystemUI/res/drawable/bottom_sheet_background.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <solid android:color="@color/bottom_sheet_background_color"/>
- <corners android:radius="@dimen/bottom_sheet_corner_radius"/>
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml b/packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml
deleted file mode 100644
index cd2aa9c..0000000
--- a/packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <solid android:color="@color/bottom_sheet_background_color_with_blur"/>
- <corners android:radius="@dimen/bottom_sheet_corner_radius"/>
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/bottom_sheet_button_background.xml b/packages/SystemUI/res/drawable/bottom_sheet_button_background.xml
deleted file mode 100644
index 585a6bc..0000000
--- a/packages/SystemUI/res/drawable/bottom_sheet_button_background.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <solid android:color="@color/bottom_sheet_button_background_color"/>
- <corners android:radius="@dimen/bottom_sheet_button_corner_radius"/>
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml
index 1590daa..50405ca 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml
@@ -26,7 +26,7 @@
<item>
<shape android:shape="rectangle">
<corners android:radius="18dp"/>
- <solid android:color="?androidprv:attr/materialColorPrimaryFixed"/>
+ <solid android:color="@color/qs_dialog_btn_filled_large_background"/>
</shape>
</item>
</ripple>
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
index b0dc652..9e9533a 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
@@ -15,7 +15,6 @@
~ limitations under the License.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:insetTop="@dimen/dialog_button_vertical_inset"
android:insetBottom="@dimen/dialog_button_vertical_inset">
<ripple android:color="?android:attr/colorControlHighlight">
@@ -29,7 +28,7 @@
<shape android:shape="rectangle">
<corners android:radius="?android:attr/buttonCornerRadius"/>
<solid android:color="@android:color/transparent"/>
- <stroke android:color="?androidprv:attr/materialColorPrimary"
+ <stroke android:color="@color/qs_dialog_btn_outline"
android:width="1dp"
/>
<padding android:left="@dimen/dialog_button_horizontal_padding"
diff --git a/packages/SystemUI/res/drawable/tv_volume_dialog_background.xml b/packages/SystemUI/res/drawable/tv_volume_dialog_background.xml
index 3705d918..264e10a 100644
--- a/packages/SystemUI/res/drawable/tv_volume_dialog_background.xml
+++ b/packages/SystemUI/res/drawable/tv_volume_dialog_background.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
+<!-- TODO(b/289498394) move this to the TvSystemUI target -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
diff --git a/packages/SystemUI/res/drawable/tv_volume_dialog_circle.xml b/packages/SystemUI/res/drawable/tv_volume_dialog_circle.xml
index 3c4fc05..bc73aab 100644
--- a/packages/SystemUI/res/drawable/tv_volume_dialog_circle.xml
+++ b/packages/SystemUI/res/drawable/tv_volume_dialog_circle.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
+<!-- TODO(b/289498394) move this to the TvSystemUI target -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/tv_volume_dialog_circle" />
diff --git a/packages/SystemUI/res/drawable/tv_volume_row_seek_thumb.xml b/packages/SystemUI/res/drawable/tv_volume_row_seek_thumb.xml
index 588782d..3c31861 100644
--- a/packages/SystemUI/res/drawable/tv_volume_row_seek_thumb.xml
+++ b/packages/SystemUI/res/drawable/tv_volume_row_seek_thumb.xml
@@ -14,6 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<!-- TODO(b/289498394) move this to the TvSystemUI target -->
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<solid android:color="@color/tv_volume_dialog_accent" />
<size android:width="@dimen/tv_volume_seek_bar_thumb_diameter"
diff --git a/packages/SystemUI/res/interpolator/tv_privacy_chip_collapse_interpolator.xml b/packages/SystemUI/res/interpolator/tv_privacy_chip_collapse_interpolator.xml
deleted file mode 100644
index 1a40173..0000000
--- a/packages/SystemUI/res/interpolator/tv_privacy_chip_collapse_interpolator.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:controlX1="0.4"
- android:controlY1="1.00"
- android:controlX2="0.12"
- android:controlY2="1.00"/>
diff --git a/packages/SystemUI/res/interpolator/tv_privacy_chip_expand_interpolator.xml b/packages/SystemUI/res/interpolator/tv_privacy_chip_expand_interpolator.xml
deleted file mode 100644
index ed44715..0000000
--- a/packages/SystemUI/res/interpolator/tv_privacy_chip_expand_interpolator.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:controlX1="0.12"
- android:controlY1="1.00"
- android:controlX2="0.4"
- android:controlY2="1.00"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
index 991dc63e..bddcb6a 100644
--- a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
@@ -13,6 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<!-- TODO(b/289498394) move this to the TvSystemUI target -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:tag="row"
diff --git a/packages/SystemUI/res/layout-television/inattentive_sleep_warning.xml b/packages/SystemUI/res/layout-television/inattentive_sleep_warning.xml
deleted file mode 100644
index eb21c43..0000000
--- a/packages/SystemUI/res/layout-television/inattentive_sleep_warning.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/sleep_warning_dialog_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:theme="@android:style/Theme.DeviceDefault.Dialog"
- android:focusable="true">
- <View
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@android:color/black"
- android:alpha="?android:backgroundDimAmount" />
- <LinearLayout
- android:layout_width="380dp"
- android:layout_height="wrap_content"
- android:background="@drawable/rounded_bg_full"
- android:padding="16dp"
- android:layout_margin="32dp"
- android:layout_gravity="bottom|right"
- android:orientation="vertical">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/inattentive_sleep_warning_title"
- android:layout_marginBottom="8dp"
- android:textColor="?android:attr/textColorPrimary"
- android:textAppearance="@android:style/TextAppearance.DeviceDefault.Large"/>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/inattentive_sleep_warning_message"
- android:textColor="?android:attr/textColorSecondary"
- android:textAppearance="@android:style/TextAppearance.DeviceDefault"/>
- </LinearLayout>
-</FrameLayout>
diff --git a/packages/SystemUI/res/layout/qs_tile_side_icon.xml b/packages/SystemUI/res/layout/qs_tile_side_icon.xml
index f1b7259..fbcead1 100644
--- a/packages/SystemUI/res/layout/qs_tile_side_icon.xml
+++ b/packages/SystemUI/res/layout/qs_tile_side_icon.xml
@@ -30,12 +30,11 @@
android:visibility="gone"
/>
- <ImageView
+ <com.android.systemui.qs.tileimpl.ChevronImageView
android:id="@+id/chevron"
android:layout_width="@dimen/qs_icon_size"
android:layout_height="@dimen/qs_icon_size"
android:src="@*android:drawable/ic_chevron_end"
- android:autoMirrored="true"
android:visibility="gone"
android:importantForAccessibility="no"
/>
diff --git a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
deleted file mode 100644
index d6c63eb..0000000
--- a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2018, 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.
-*/
--->
-<com.android.systemui.statusbar.StatusBarMobileView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/mobile_combo"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical" >
-
- <include layout="@layout/status_bar_mobile_signal_group_inner" />
-
-</com.android.systemui.statusbar.StatusBarMobileView>
-
diff --git a/packages/SystemUI/res/layout/tv_bottom_sheet.xml b/packages/SystemUI/res/layout/tv_bottom_sheet.xml
deleted file mode 100644
index b69cdc7..0000000
--- a/packages/SystemUI/res/layout/tv_bottom_sheet.xml
+++ /dev/null
@@ -1,87 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/bottom_sheet"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:orientation="horizontal"
- android:minHeight="@dimen/bottom_sheet_min_height"
- android:paddingHorizontal="@dimen/bottom_sheet_padding_horizontal"
- android:paddingVertical="@dimen/bottom_sheet_padding_vertical">
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:minWidth="80dp"
- android:gravity="center"
- android:orientation="horizontal">
- <ImageView
- android:id="@+id/bottom_sheet_icon"
- android:layout_width="@dimen/bottom_sheet_icon_size"
- android:layout_height="@dimen/bottom_sheet_icon_size"
- android:layout_gravity="center_vertical"
- android:tint="@color/bottom_sheet_icon_color"/>
- <ImageView
- android:id="@+id/bottom_sheet_second_icon"
- android:layout_width="@dimen/bottom_sheet_icon_size"
- android:layout_height="@dimen/bottom_sheet_icon_size"
- android:layout_marginStart="@dimen/bottom_sheet_icon_margin"
- android:layout_gravity="center_vertical"
- android:tint="@color/bottom_sheet_icon_color"/>
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/bottom_sheet_padding_horizontal"
- android:layout_weight="1"
- android:orientation="vertical">
- <TextView
- android:id="@+id/bottom_sheet_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/bottom_sheet_title_margin_bottom"
- android:textAppearance="@style/BottomSheet.TitleText"/>
-
- <TextView
- android:id="@+id/bottom_sheet_body"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/bottom_sheet_details_margin_bottom"
- android:textAppearance="@style/BottomSheet.BodyText" />
- </LinearLayout>
-
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="@dimen/bottom_sheet_actions_width"
- android:layout_height="match_parent"
- android:gravity="center">
- <Button
- android:id="@+id/bottom_sheet_positive_button"
- style="@style/BottomSheet.ActionItem" />
- <Space
- android:layout_width="0dp"
- android:layout_height="@dimen/bottom_sheet_actions_spacing" />
- <Button
- android:id="@+id/bottom_sheet_negative_button"
- style="@style/BottomSheet.ActionItem" />
- </LinearLayout>
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_notification_item.xml b/packages/SystemUI/res/layout/tv_notification_item.xml
deleted file mode 100644
index 711cd4e..0000000
--- a/packages/SystemUI/res/layout/tv_notification_item.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2020 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.
- -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/tv_notification_panel_width"
- android:layout_height="wrap_content"
- android:background="?android:attr/selectableItemBackground"
- android:orientation="vertical"
- android:padding="12dp">
-
- <TextView
- android:id="@+id/tv_notification_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingBottom="12dp"
- android:textColor="@color/tv_notification_text_color"
- android:textSize="18sp" />
-
- <TextView
- android:id="@+id/tv_notification_details"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textColor="@color/tv_notification_text_color" />
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_notification_panel.xml b/packages/SystemUI/res/layout/tv_notification_panel.xml
deleted file mode 100644
index eae44c8..0000000
--- a/packages/SystemUI/res/layout/tv_notification_panel.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2020 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.
- -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/tv_notification_panel"
- android:layout_width="@dimen/tv_notification_panel_width"
- android:layout_height="match_parent"
- android:layout_gravity="end"
- android:background="@android:color/transparent"
- android:orientation="vertical">
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="12dp"
- android:paddingTop="24dp"
- android:text="@string/tv_notification_panel_title"
- android:textColor="@color/tv_notification_text_color"
- android:textSize="24sp"
- android:textStyle="bold" />
-
- <TextView
- android:id="@+id/no_tv_notifications"
- style="?android:attr/titleTextStyle"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:gravity="top|center"
- android:paddingTop="24dp"
- android:text="@string/tv_notification_panel_no_notifications"
- android:textColor="@color/tv_notification_text_color"
- android:visibility="gone" />
-
- <androidx.leanback.widget.VerticalGridView
- android:id="@+id/notifications_list"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1" />
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml
deleted file mode 100644
index 9069b8f..0000000
--- a/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/privacy_chip_height"
- android:minWidth="@dimen/privacy_chip_dot_bg_width"
- android:gravity="center">
- <LinearLayout
- android:id="@+id/icons_container"
- android:layout_width="match_parent"
- android:layout_height="@dimen/privacy_chip_height"
- android:minWidth="@dimen/privacy_chip_dot_bg_width"
- android:orientation="horizontal"
- android:gravity="center"
- android:paddingHorizontal="@dimen/privacy_chip_padding_horizontal"
- android:clipToPadding="false" />
-</FrameLayout>
diff --git a/packages/SystemUI/res/layout/tv_privacy_chip_container.xml b/packages/SystemUI/res/layout/tv_privacy_chip_container.xml
deleted file mode 100644
index 20ffe2b9..0000000
--- a/packages/SystemUI/res/layout/tv_privacy_chip_container.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:minWidth="@dimen/privacy_chips_max_width"
- android:orientation="horizontal"
- android:clipToPadding="false"
- android:gravity="end"
- android:padding="@dimen/privacy_chips_bar_padding">
- <Space
- android:layout_width="0dp"
- android:layout_height="@dimen/privacy_chip_height" />
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings_tv.xml b/packages/SystemUI/res/values-af/strings_tv.xml
deleted file mode 100644
index 4fec3b2..0000000
--- a/packages/SystemUI/res/values-af/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN is gekoppel"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN is ontkoppel"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Kennisgewings"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Geen kennisgewings nie"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofoon neem tans op"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera neem tans op"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera en mikrofoon neem tans op"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofoon het opname gestop"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera het opname gestop"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera en mikrofoon het opname gestop"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Skermopname het begin"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Skermopname het gestop"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-am/strings_tv.xml b/packages/SystemUI/res/values-am/strings_tv.xml
deleted file mode 100644
index d3d1433..0000000
--- a/packages/SystemUI/res/values-am/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN ተያይዟል"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ተቋርቷል"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"በ<xliff:g id="VPN_APP">%1$s</xliff:g> በኩል"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"ማሳወቂያዎች"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"ምንም ማሳወቂያዎች የሉም"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"ማይክሮፎን እየቀዳ ነው"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"ካሜራ እየቀረጸ ነው"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"ካሜራ እየቀረጸ እና ማይክሮፎን እየቀዳ ነው"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"ማይክሮፎን መቅዳት አቁሟል"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"ካሜራ መቅረጽ አቁሟል"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"ካሜራ መቅረጽ እና ማይክሮፎን መቅዳት አቁመዋል"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"የማያ ገፅ ቀረጻ ተጀምሯል"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"የማያ ገፅ ቀረጻ ቆሟል"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ar/strings_tv.xml b/packages/SystemUI/res/values-ar/strings_tv.xml
deleted file mode 100644
index 0a96069..0000000
--- a/packages/SystemUI/res/values-ar/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"شبكة VPN متصلة."</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"شبكة VPN غير متصلة."</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"عبر <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"الإشعارات"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"ما من إشعارات"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"جارٍ التسجيل بالميكرفون"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"جارٍ التسجيل بالكاميرا"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"جارٍ التسجيل بالكاميرا والميكروفون"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"توقف التسجيل بالميكرفون."</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"توقف التسجيل بالكاميرا."</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"توقف التسجيل بالكاميرا والميكروفون."</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"بدأ تسجيل الشاشة."</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"توقَّف تسجيل الشاشة."</string>
-</resources>
diff --git a/packages/SystemUI/res/values-as/strings_tv.xml b/packages/SystemUI/res/values-as/strings_tv.xml
deleted file mode 100644
index ec37d53..0000000
--- a/packages/SystemUI/res/values-as/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"ভিপিএন সংযোগ হৈ আছে"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"ভিপিএনৰ সংযোগ বিচ্ছিন্ন হৈছে"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g>ৰ জৰিয়তে"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"জাননী"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"কোনো জাননী নাই"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"মাইক্ৰ’ফ’নটোৱে ৰেক’ৰ্ড কৰি আছে"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"কেমেৰাটোৱে ৰেক’ৰ্ড কৰি আছে"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"কেমেৰা আৰু মাইক্ৰ’ফ’নটোৱে ৰেক’ৰ্ড কৰি আছে"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"মাইক্ৰ’ফ’নটোৱে ৰেক’ৰ্ড কৰাটো বন্ধ কৰিছে"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"কেমেৰাটোৱে ৰেক’ৰ্ড কৰাটো বন্ধ কৰিছে"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"কেমেৰা আৰু মাইক্ৰ’ফ’নটোৱে ৰেক’ৰ্ড কৰাটো বন্ধ কৰিছে"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"স্ক্ৰীন ৰেকৰ্ডিং আৰম্ভ কৰা হৈছে"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"স্ক্ৰীন ৰেকৰ্ডিং বন্ধ হৈছে"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-az/strings_tv.xml b/packages/SystemUI/res/values-az/strings_tv.xml
deleted file mode 100644
index 055b2fa..0000000
--- a/packages/SystemUI/res/values-az/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN qoşulub"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ayrılıb"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> vasitəsilə"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Bildirişlər"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Bildiriş yoxdur"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon yazır"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera yazır"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera və mikrofon yazır"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon yazmağı dayandırıb"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera yazmağı dayandırıb"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera və mikrofon yazmağı dayandırıb"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Ekranı videoya çəkməyə başlandı"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Ekranı videoya çəkməyi dayandırdınız"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml b/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml
deleted file mode 100644
index f72890bd..0000000
--- a/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN je povezan"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Veza sa VPN-om je prekinuta"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Preko: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Obaveštenja"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nema obaveštenja"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon snima"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera snima"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera i mikrofon snimaju"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Snimanje mikrofonom je zaustavljeno"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Snimanje kamerom je zaustavljeno"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Snimanje kamerom i mikrofonom je zaustavljeno"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Snimanje ekrana je započeto"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Snimanje ekrana je zaustavljeno"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-be/strings_tv.xml b/packages/SystemUI/res/values-be/strings_tv.xml
deleted file mode 100644
index aee04c2..0000000
--- a/packages/SystemUI/res/values-be/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN падключаны"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN адключаны"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Праз <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Апавяшчэнні"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Апавяшчэнняў няма"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Ідзе запіс з выкарыстаннем мікрафона"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Ідзе запіс з выкарыстаннем камеры"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Ідзе запіс з выкарыстаннем камеры і мікрафона"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Запіс з выкарыстаннем мікрафона спынены"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Запіс з выкарыстаннем камеры спынены"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Запіс з выкарыстаннем камеры і мікрафона спынены"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Пачаўся запіс экрана"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Запіс экрана спынены"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-bg/strings_tv.xml b/packages/SystemUI/res/values-bg/strings_tv.xml
deleted file mode 100644
index 6d578ad..0000000
--- a/packages/SystemUI/res/values-bg/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN е свързана"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Връзката с VPN е прекратена"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Чрез <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Известия"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Няма известия"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Микрофонът записва"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Камерата записва"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Камерата и микрофонът записват"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Микрофонът спря да записва"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Камерата спря да записва"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Камерата и микрофонът спряха да записват"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Записването на екрана започна"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Записването на екрана бе спряно"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-bn/strings_tv.xml b/packages/SystemUI/res/values-bn/strings_tv.xml
deleted file mode 100644
index dcaefd97..0000000
--- a/packages/SystemUI/res/values-bn/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN কানেক্ট করা হয়েছে"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ডিসকানেক্ট করা হয়েছে"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g>-এর মাধ্যমে"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"বিজ্ঞপ্তি"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"কোনও বিজ্ঞপ্তি নেই"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"মাইক্রোফোনে রেকর্ড করা হচ্ছে"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"ক্যামেরায় রেকর্ড করা হচ্ছে"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"ক্যামেরা ও মাইক্রোফোনে রেকর্ড করা হচ্ছে"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"মাইক্রোফোনে রেকর্ড করা বন্ধ হয়ে গেছে"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"ক্যামেরায় রেকর্ড করা বন্ধ হয়ে গেছে"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"ক্যামেরা ও মাইক্রোফোনে রেকর্ড করা বন্ধ হয়ে গেছে"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"স্ক্রিন রেকর্ড করা শুরু হয়েছে"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"স্ক্রিন রেকর্ড করা বন্ধ হয়েছে"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-bs/strings_tv.xml b/packages/SystemUI/res/values-bs/strings_tv.xml
deleted file mode 100644
index db8c5b3..0000000
--- a/packages/SystemUI/res/values-bs/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN je povezan"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Veza s VPN-om je prekinuta"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Putem: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Obavještenja"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nema obavještenja"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon snima"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera snima"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera i mikrofon snimaju"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon je prestao snimati"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera je prestala snimati"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera i mikrofon su prestali snimati"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Snimanje ekrana je pokrenuto"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Snimanje ekrana je zaustavljeno"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ca/strings_tv.xml b/packages/SystemUI/res/values-ca/strings_tv.xml
deleted file mode 100644
index 972d38b..0000000
--- a/packages/SystemUI/res/values-ca/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"La VPN està connectada"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"La VPN està desconnectada"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Mitjançant <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notificacions"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Cap notificació"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"El micròfon està gravant"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"La càmera està gravant"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"La càmera i el micròfon estan gravant"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"El micròfon ha deixat de gravar"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"La càmera ha deixat de gravar"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"La càmera i el micròfon han deixat de gravar"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"La gravació de la pantalla ha començat"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"La gravació de la pantalla s\'ha aturat"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-cs/strings_tv.xml b/packages/SystemUI/res/values-cs/strings_tv.xml
deleted file mode 100644
index ab391f4..0000000
--- a/packages/SystemUI/res/values-cs/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"Síť VPN je připojena"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Síť VPN je odpojena"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Přes <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Oznámení"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Žádná oznámení"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon nahrává"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera nahrává"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera a mikrofon nahrávají"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon přestal nahrávat"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera přestala nahrávat"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera a mikrofon přestaly nahrávat"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Zahájeno nahrávání obrazovky"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Záznam obrazovky ukončen"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-da/strings_tv.xml b/packages/SystemUI/res/values-da/strings_tv.xml
deleted file mode 100644
index 66b7964..0000000
--- a/packages/SystemUI/res/values-da/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN er tilsluttet"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN er afbrudt"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifikationer"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Ingen notifikationer"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofonen optager"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kameraet optager"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kameraet og mikrofonen optager"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofonen er stoppet med at optage"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kameraet er stoppet med at optage"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kameraet og mikrofonen er stoppet med at optage"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Skærmoptagelsen er startet"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Skærmoptagelsen er stoppet"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-de/strings_tv.xml b/packages/SystemUI/res/values-de/strings_tv.xml
deleted file mode 100644
index 5df9d32..0000000
--- a/packages/SystemUI/res/values-de/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN ist verbunden"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ist nicht verbunden"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Über <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Benachrichtigungen"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Keine Benachrichtigungen"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon nimmt auf"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera nimmt auf"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera und Mikrofon nehmen auf"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Aufnahme des Mikrofons gestoppt"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Aufnahme der Kamera gestoppt"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Aufnahme von Kamera und Mikrofon gestoppt"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Bildschirmaufzeichnung gestartet"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Bildschirmaufzeichnung beendet"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-el/strings_tv.xml b/packages/SystemUI/res/values-el/strings_tv.xml
deleted file mode 100644
index 9fb126d..0000000
--- a/packages/SystemUI/res/values-el/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"Το VPN συνδέθηκε"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Το VPN αποσυνδέθηκε"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Μέσω <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Ειδοποιήσεις"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Δεν υπάρχουν ειδοποιήσεις."</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Πραγματοποιείται εγγραφή από το μικρόφωνο"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Πραγματοποιείται εγγραφή από την κάμερα"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Πραγματοποιείται εγγραφή από την κάμερα και το μικρόφωνο"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Η εγγραφή από το μικρόφωνο διακόπηκε"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Η εγγραφή από την κάμερα διακόπηκε"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Η εγγραφή από την κάμερα και το μικρόφωνο διακόπηκε"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Η εγγραφή οθόνης ξεκίνησε"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Η εγγραφή οθόνης σταμάτησε"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings_tv.xml b/packages/SystemUI/res/values-en-rAU/strings_tv.xml
deleted file mode 100644
index e97dbe4..0000000
--- a/packages/SystemUI/res/values-en-rAU/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN is connected"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN is disconnected"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifications"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"No notifications"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Microphone is recording"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Camera is recording"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Camera and microphone are recording"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Microphone stopped recording"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Camera stopped recording"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Camera and microphone stopped recording"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Screen recording started"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Screen recording stopped"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings_tv.xml b/packages/SystemUI/res/values-en-rCA/strings_tv.xml
deleted file mode 100644
index a628846..0000000
--- a/packages/SystemUI/res/values-en-rCA/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN is connected"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN is disconnected"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifications"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"No Notifications"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Microphone is recording"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Camera is recording"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Camera and Microphone are recording"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Microphone stopped recording"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Camera stopped recording"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Camera and Microphone stopped recording"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Screen recording started"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Screen recording stopped"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings_tv.xml b/packages/SystemUI/res/values-en-rGB/strings_tv.xml
deleted file mode 100644
index e97dbe4..0000000
--- a/packages/SystemUI/res/values-en-rGB/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN is connected"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN is disconnected"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifications"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"No notifications"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Microphone is recording"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Camera is recording"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Camera and microphone are recording"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Microphone stopped recording"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Camera stopped recording"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Camera and microphone stopped recording"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Screen recording started"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Screen recording stopped"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings_tv.xml b/packages/SystemUI/res/values-en-rIN/strings_tv.xml
deleted file mode 100644
index e97dbe4..0000000
--- a/packages/SystemUI/res/values-en-rIN/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN is connected"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN is disconnected"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifications"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"No notifications"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Microphone is recording"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Camera is recording"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Camera and microphone are recording"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Microphone stopped recording"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Camera stopped recording"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Camera and microphone stopped recording"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Screen recording started"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Screen recording stopped"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings_tv.xml b/packages/SystemUI/res/values-en-rXC/strings_tv.xml
deleted file mode 100644
index 06116ed..0000000
--- a/packages/SystemUI/res/values-en-rXC/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN is connected"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN is disconnected"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifications"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"No Notifications"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Microphone is recording"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Camera is recording"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Camera and Microphone are recording"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Microphone stopped recording"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Camera stopped recording"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Camera and Microphone stopped recording"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Screen recording started"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Screen recording stopped"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings_tv.xml b/packages/SystemUI/res/values-es-rUS/strings_tv.xml
deleted file mode 100644
index 513e0c7..0000000
--- a/packages/SystemUI/res/values-es-rUS/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"La VPN está conectada."</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"La VPN está desconectada"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"A través de <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notificaciones"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"No hay notificaciones"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"El micrófono está grabando"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"La cámara está grabando"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"La cámara y el micrófono están grabando"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"El micrófono dejó de grabar"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"La cámara dejó de grabar"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"La cámara y el micrófono dejaron de grabar"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Se inició la grabación de pantalla"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Se detuvo la grabación de pantalla"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-es/strings_tv.xml b/packages/SystemUI/res/values-es/strings_tv.xml
deleted file mode 100644
index 1482fd0..0000000
--- a/packages/SystemUI/res/values-es/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"La VPN está conectada"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"La VPN está desconectada"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"A través de <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notificaciones"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Sin notificaciones"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"El micrófono está grabando"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"La cámara está grabando"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"La cámara y el micrófono están grabando"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"El micrófono ha dejado de grabar"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"La cámara ha dejado de grabar"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"La cámara y el micrófono han dejado de grabar"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Grabación de pantalla iniciada"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Grabación de pantalla detenida"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-et/strings_tv.xml b/packages/SystemUI/res/values-et/strings_tv.xml
deleted file mode 100644
index ee7bba8..0000000
--- a/packages/SystemUI/res/values-et/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN on ühendatud"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN-i ühendus on katkestatud"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Teenuse <xliff:g id="VPN_APP">%1$s</xliff:g> kaudu"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Märguanded"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Märguandeid pole"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon salvestab"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kaamera salvestab"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kaamera ja mikrofon salvestavad"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon peatas salvestamise"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kaamera peatas salvestamise"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kaamera ja mikrofon peatasid salvestamise"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Ekraanikuva salvestamine algas"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Ekraanikuva salvestamine on peatatud"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-eu/strings_tv.xml b/packages/SystemUI/res/values-eu/strings_tv.xml
deleted file mode 100644
index de5249b..0000000
--- a/packages/SystemUI/res/values-eu/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN sarera konektatuta dago"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ez dago sarera konektatuta"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> bidez"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Jakinarazpenak"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Ez dago jakinarazpenik"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofonoa grabatzen ari da"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera grabatzen ari da"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera eta mikrofonoa grabatzen ari dira"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofonoak grabatzeari utzi dio"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamerak grabatzeari utzi dio"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamerak eta mikrofonoak grabatzeari utzi diote"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Hasi da pantailaren grabaketa"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Gelditu da pantailaren grabaketa"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-fa/strings_tv.xml b/packages/SystemUI/res/values-fa/strings_tv.xml
deleted file mode 100644
index 89f57af..0000000
--- a/packages/SystemUI/res/values-fa/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN متصل است"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN قطع است"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"ازطریق <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"اعلانها"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"اعلانی ندارید"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"میکروفون درحال ضبط کردن است"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"دوربین درحال ضبط کردن است"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"دوربین و میکروفون درحال ضبط کردن هستند"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"ضبط میکروفون متوقف شد"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"ضبط دوربین متوقف شد"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"ضبط دوربین و میکروفون متوقف شد"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"ضبط صفحه شروع شد"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"ضبط صفحه متوقف شد"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-fi/strings_tv.xml b/packages/SystemUI/res/values-fi/strings_tv.xml
deleted file mode 100644
index 61198d4..0000000
--- a/packages/SystemUI/res/values-fi/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN on yhdistetty"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ei ole yhdistettynä"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Palvelun <xliff:g id="VPN_APP">%1$s</xliff:g> kautta"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Ilmoitukset"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Ei ilmoituksia"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofoni tallentaa"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera kuvaa"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera ja mikrofoni tallentavat"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofoni lopetti tallentamisen"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera lopetti kuvaamisen"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera ja mikrofoni lopettivat tallentamisen"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Näytön tallennus aloitettu"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Näytön tallennus lopetettu"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings_tv.xml b/packages/SystemUI/res/values-fr-rCA/strings_tv.xml
deleted file mode 100644
index 7ab63af..0000000
--- a/packages/SystemUI/res/values-fr-rCA/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"RPV connecté"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"RPV déconnecté"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Par <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifications"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Aucune notification"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Le microphone enregistre"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"L\'appareil photo enregistre"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"L\'appareil photo et le microphone enregistrent"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Le microphone a arrêté l\'enregistrement"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"L\'appareil photo a arrêté l\'enregistrement"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"L\'appareil photo et le microphone ont arrêté l\'enregistrement"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"L\'enregistrement d\'écran a commencé"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"L\'enregistrement d\'écran s\'est arrêté"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-fr/strings_tv.xml b/packages/SystemUI/res/values-fr/strings_tv.xml
deleted file mode 100644
index d1ac554..0000000
--- a/packages/SystemUI/res/values-fr/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN connecté"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN déconnecté"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifications"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Aucune notification"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Le micro enregistre…"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"La caméra enregistre…"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"La caméra et le micro enregistrent…"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Le micro a arrêté l\'enregistrement"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"La caméra a arrêté l\'enregistrement"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"La caméra et le micro ont arrêté l\'enregistrement"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Enregistrement de l\'écran démarré"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Enregistrement de l\'écran arrêté"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-gl/strings_tv.xml b/packages/SystemUI/res/values-gl/strings_tv.xml
deleted file mode 100644
index 58279ee..0000000
--- a/packages/SystemUI/res/values-gl/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"A VPN está conectada"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"A VPN está desconectada"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"A través de <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notificacións"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Non hai notificacións"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"O micrófono está gravando"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"A cámara está gravando"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"A cámara e o micrófono están gravando"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"O micrófono deixou de gravar"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"A cámara deixou de gravar"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"A cámara e o micrófono deixaron de gravar"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Iniciouse a gravación da pantalla"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Parouse a gravación da pantalla"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-gu/strings_tv.xml b/packages/SystemUI/res/values-gu/strings_tv.xml
deleted file mode 100644
index 096a93a..0000000
--- a/packages/SystemUI/res/values-gu/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN કનેક્ટ કરેલું છે"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ડિસ્કનેક્ટ કરેલું છે"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> મારફતે"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"નોટિફિકેશન"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"કોઈ નોટિફિકેશન નથી"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"માઇક્રોફોનનું રેકોર્ડિંગ ચાલુ છે"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"કૅમેરાનું રેકોર્ડિંગ ચાલુ છે"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"કૅમેરા અને માઇક્રોફોનનું રેકોર્ડિંગ ચાલુ છે"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"માઇક્રોફોનનું રેકોર્ડિંગ બંધ થઈ ગયું"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"કૅમેરાનું રેકોર્ડિંગ બંધ થઈ ગયું"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"કૅમેરા અને માઇક્રોફોનનું રેકોર્ડિંગ બંધ થઈ ગયું"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"સ્ક્રીન રેકોર્ડિંગ શરૂ થયું"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"સ્ક્રીન રેકોર્ડિંગ બંધ કર્યું"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-hi/strings_tv.xml b/packages/SystemUI/res/values-hi/strings_tv.xml
deleted file mode 100644
index 0e5e1225..0000000
--- a/packages/SystemUI/res/values-hi/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"वीपीएन कनेक्ट हो गया है"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"वीपीएन डिसकनेक्ट हो गया है"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> के ज़रिए"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"सूचनाएं"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"कोई सूचना नहीं है"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"माइक्रोफ़ोन रिकॉर्ड कर रहा है"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"कैमरा रिकॉर्ड कर रहा है"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"कैमरा और माइक्रोफ़ोन रिकॉर्ड कर रहे हैं"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"माइक्रोफ़ोन ने रिकॉर्ड करना बंद कर दिया है"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"कैमरे ने रिकॉर्ड करना बंद कर दिया है"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"कैमरे और माइक्रोफ़ोन ने रिकॉर्ड करना बंद कर दिया है"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"स्क्रीन रिकॉर्डिंग शुरू की गई"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"स्क्रीन रिकॉर्डिंग बंद की गई"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-hr/strings_tv.xml b/packages/SystemUI/res/values-hr/strings_tv.xml
deleted file mode 100644
index 61cf646..0000000
--- a/packages/SystemUI/res/values-hr/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN je spojen"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN je isključen"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Putem mreže <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Obavijesti"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nema obavijesti"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon snima"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera snima"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera i mikrofon snimaju"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon je prestao snimati"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera je prestala snimati"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera i mikrofon prestali su snimati"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Snimanje zaslona je počelo"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Snimanje zaslona je zaustavljeno"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-hu/strings_tv.xml b/packages/SystemUI/res/values-hu/strings_tv.xml
deleted file mode 100644
index f251c38..0000000
--- a/packages/SystemUI/res/values-hu/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"A VPN-kapcsolat létrejött"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"A VPN-kapcsolat megszakadt"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Ezzel: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Értesítések"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nincs értesítés"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"A mikrofon felvételt készít…"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"A kamera felvételt készít…"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"A kamera és a mikrofon felvételt készít…"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"A mikrofon befejezte a felvételkészítést"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"A kamera befejezte a felvételkészítést"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"A kamera és a mikrofon befejezte a felvételkészítést"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"A képernyő rögzítése elindult"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"A képernyő rögzítése leállt"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-hy/strings_tv.xml b/packages/SystemUI/res/values-hy/strings_tv.xml
deleted file mode 100644
index b6a2e13..0000000
--- a/packages/SystemUI/res/values-hy/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN-ը միացված է"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN-ն անջատված է"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g>-ի միջոցով"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Ծանուցումներ"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Ծանուցումներ չկան"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Խոսափողը ձայնագրում է"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Տեսախցիկը տեսագրում է"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Տեսախցիկն ու խոսափողը տեսաձայնագրում են"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Խոսափողն այլևս չի ձայնագրում"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Տեսախցիկն այլևս չի տեսագրում"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Տեսախցիկն ու խոսափողը այլևս չեն տեսաձայնագրում"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Էկրանի տեսագրումը սկսվեց"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Էկրանի տեսագրումը կանգնեցվեց"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-in/strings_tv.xml b/packages/SystemUI/res/values-in/strings_tv.xml
deleted file mode 100644
index 1b8261e7..0000000
--- a/packages/SystemUI/res/values-in/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN terhubung"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN terputus"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Melalui <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifikasi"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Tidak Ada Notifikasi"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon sedang merekam"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera sedang merekam"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera dan Mikrofon sedang merekam"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon berhenti merekam"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera berhenti merekam"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera dan Mikrofon berhenti merekam"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Perekaman layar dimulai"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Perekaman layar dihentikan"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-is/strings_tv.xml b/packages/SystemUI/res/values-is/strings_tv.xml
deleted file mode 100644
index 7150fc8..0000000
--- a/packages/SystemUI/res/values-is/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN er tengt"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN er ekki tengt"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Í gegnum <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Tilkynningar"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Engar tilkynningar"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Hljóðnemi er að taka upp"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Myndavél er að taka upp"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Myndavél og hljóðnemi eru að taka upp"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Hljóðnemi er hættur að taka upp"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Myndavél er hætt að taka upp"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Myndavél og hljóðnemi eru hætt að taka upp"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Skjáupptaka er hafin"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Skjáupptöku er lokið"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-it/strings_tv.xml b/packages/SystemUI/res/values-it/strings_tv.xml
deleted file mode 100644
index ab9ee78..0000000
--- a/packages/SystemUI/res/values-it/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN connessa"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN disconnessa"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Tramite <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifiche"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nessuna notifica"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Registrazione in corso con il microfono"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Registrazione in corso con la fotocamera"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Registrazione in corso con fotocamera e microfono"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Registrazione con il microfono interrotta"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Registrazione con la fotocamera interrotta"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Registrazione con fotocamera e microfono interrotta"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Registrazione dello schermo avviata"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Registrazione dello schermo interrotta"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-iw/strings_tv.xml b/packages/SystemUI/res/values-iw/strings_tv.xml
deleted file mode 100644
index 1085505..0000000
--- a/packages/SystemUI/res/values-iw/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"ה-VPN מחובר"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"ה-VPN מנותק"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"דרך <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"התראות"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"אין התראות"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"המיקרופון מקליט"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"המצלמה מקליטה"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"המצלמה והמיקרופון מקליטים"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"המיקרופון הפסיק להקליט"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"המצלמה הפסיקה להקליט"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"המצלמה והמיקרופון הפסיקו להקליט"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"הקלטת המסך החלה"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"הקלטת המסך הופסקה"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ja/strings_tv.xml b/packages/SystemUI/res/values-ja/strings_tv.xml
deleted file mode 100644
index 6b68c73..0000000
--- a/packages/SystemUI/res/values-ja/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN に接続しました"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN に接続していません"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> 経由"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"通知"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"通知はありません"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"マイクで録音しています"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"カメラで録画しています"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"カメラとマイクで録画、録音しています"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"マイクが録音を停止しました"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"カメラが録画を停止しました"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"カメラとマイクが録画、録音を停止しました"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"画面の録画を開始しました"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"画面の録画を停止しました"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ka/strings_tv.xml b/packages/SystemUI/res/values-ka/strings_tv.xml
deleted file mode 100644
index dcaa138..0000000
--- a/packages/SystemUI/res/values-ka/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN დაკავშირებულია"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN გათიშულია"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g>-ის მიერ"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"შეტყობინებები"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"შეტყობინებები არ არის"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"მიკროფონი იწერს"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"კამერა იწერს"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"კამერა და მიკროფონი იწერს"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"მიკროფონმა ჩაწერა შეწყვიტა"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"კამერამ ჩაწერა შეწყვიტა"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"კამერამ და მიკროფონმა ჩაწერა შეწყვიტა"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"ეკრანის ჩაწერა დაიწყო"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"ეკრანის ჩაწერა დასრულდა"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-kk/strings_tv.xml b/packages/SystemUI/res/values-kk/strings_tv.xml
deleted file mode 100644
index bb0e06b..0000000
--- a/packages/SystemUI/res/values-kk/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN қосылған"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ажыратылған"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> арқылы жалғанған"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Хабарландырулар"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Хабарландырулар жоқ"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Микрофон жазып жатыр."</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Камера жазып жатыр."</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Камера мен микрофон жазып жатыр."</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Микрофон жазуды тоқтатты."</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Камера жазуды тоқтатты."</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Камера мен микрофон жазуды тоқтатты."</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Экранды бейнеге жазу басталды."</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Экранды бейнеге жазу тоқтатылды."</string>
-</resources>
diff --git a/packages/SystemUI/res/values-km/strings_tv.xml b/packages/SystemUI/res/values-km/strings_tv.xml
deleted file mode 100644
index 1be9d48..0000000
--- a/packages/SystemUI/res/values-km/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN ត្រូវបានភ្ជាប់"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ត្រូវបានផ្ដាច់"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"តាមរយៈ <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"ការជូនដំណឹង"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"គ្មានការជូនដំណឹងទេ"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"មីក្រូហ្វូនកំពុងថត"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"កាមេរ៉ាកំពុងថត"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"កាមេរ៉ា និងមីក្រូហ្វូនកំពុងថត"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"មីក្រូហ្វូនបានឈប់ថត"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"កាមេរ៉ាបានឈប់ថត"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"កាមេរ៉ា និងមីក្រូហ្វូនបានឈប់ថត"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"មុខងារថតវីដេអូអេក្រង់បានចាប់ផ្ដើម"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"មុខងារថតវីដេអូអេក្រង់បានបញ្ឈប់"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-kn/strings_tv.xml b/packages/SystemUI/res/values-kn/strings_tv.xml
deleted file mode 100644
index cfcc896..0000000
--- a/packages/SystemUI/res/values-kn/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ಕನೆಕ್ಷನ್ ಕಡಿತಗೊಂಡಿದೆ"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> ಮೂಲಕ"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"ಅಧಿಸೂಚನೆಗಳು"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"ಯಾವುದೇ ಅಧಿಸೂಚನೆಗಳಿಲ್ಲ"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"ಮೈಕ್ರೊಫೋನ್ ರೆಕಾರ್ಡಿಂಗ್ ಆಗುತ್ತಿದೆ"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"ಕ್ಯಾಮರಾ ರೆಕಾರ್ಡಿಂಗ್ ಆಗುತ್ತಿದೆ"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"ಕ್ಯಾಮರಾ ಮತ್ತು ಮೈಕ್ರೊಫೋನ್ ರೆಕಾರ್ಡಿಂಗ್ ಆಗುತ್ತಿವೆ"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"ಮೈಕ್ರೊಫೋನ್ ರೆಕಾರ್ಡಿಂಗ್ ನಿಲ್ಲಿಸಿದೆ"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"ಕ್ಯಾಮರಾ ರೆಕಾರ್ಡಿಂಗ್ ನಿಲ್ಲಿಸಲಾಗಿದೆ"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"ಕ್ಯಾಮರಾ ಮತ್ತು ಮೈಕ್ರೊಫೋನ್ ರೆಕಾರ್ಡಿಂಗ್ ನಿಲ್ಲಿಸಿವೆ"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಪ್ರಾರಂಭವಾಗಿದೆ"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ನಿಲ್ಲಿಸಲಾಗಿದೆ"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ko/strings_tv.xml b/packages/SystemUI/res/values-ko/strings_tv.xml
deleted file mode 100644
index 2412ae8..0000000
--- a/packages/SystemUI/res/values-ko/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN에 연결됨"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN 연결이 해제됨"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g>에 연결됨"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"알림"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"알림 없음"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"마이크 녹음 중"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"카메라 녹화 중"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"카메라 녹화 및 마이크 녹음 중"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"마이크 녹음 중단됨"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"카메라 녹화 중단됨"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"카메라 녹화 및 마이크 녹음 중단됨"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"화면 녹화가 시작되었습니다."</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"화면 녹화가 중지되었습니다."</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ky/strings_tv.xml b/packages/SystemUI/res/values-ky/strings_tv.xml
deleted file mode 100644
index 7ad134a..0000000
--- a/packages/SystemUI/res/values-ky/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN туташтырылды"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ажыратылды"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> аркылуу"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Билдирмелер"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Билдирме жок"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Микрофон жаздырууда"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Камера жаздырууда"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Камера менен микрофон жаздырууда"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Микрофон жаздырууну токтотту"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Камера жаздырууну токтотту"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Камера менен микрофон жаздырууну токтотту"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Экрандан видео жаздырылып башталды"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Экрандан видео жаздыруу токтотулду"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-land-television/dimens.xml b/packages/SystemUI/res/values-land-television/dimens.xml
index 8fc4612..52f591f 100644
--- a/packages/SystemUI/res/values-land-television/dimens.xml
+++ b/packages/SystemUI/res/values-land-television/dimens.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
+<!-- TODO(b/289498394) move this to the TvSystemUI target -->
<resources>
<!-- Height of volume bar -->
<dimen name="volume_dialog_panel_height">190dp</dimen>
diff --git a/packages/SystemUI/res/values-lo/strings_tv.xml b/packages/SystemUI/res/values-lo/strings_tv.xml
deleted file mode 100644
index 759931d..0000000
--- a/packages/SystemUI/res/values-lo/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"ເຊື່ອມຕໍ່ VPN ແລ້ວ"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"ຕັດການເຊື່ອມຕໍ່ VPN ແລ້ວ"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"ຜ່ານ <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"ການແຈ້ງເຕືອນ"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"ບໍ່ມີການແຈ້ງເຕືອນ"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"ໄມໂຄຣໂຟນກຳລັງບັນທຶກ"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"ກ້ອງຖ່າຍຮູບກຳລັງບັນທຶກ"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"ກ້ອງຖ່າຍຮູບ ແລະ ໄມໂຄຣໂຟນກຳລັງບັນທຶກ"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"ໄມໂຄຣໂຟນຢຸດການບັນທຶກແລ້ວ"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"ກ້ອງຖ່າຍຮູບຢຸດການບັນທຶກແລ້ວ"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"ກ້ອງຖ່າຍຮູບ ແລະ ໄມໂຄຣໂຟນຢຸດການບັນທຶກແລ້ວ"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"ເລີ່ມການບັນທຶກໜ້າຈໍແລ້ວ"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"ຢຸດການບັນທຶກໜ້າຈໍແລ້ວ"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-lt/strings_tv.xml b/packages/SystemUI/res/values-lt/strings_tv.xml
deleted file mode 100644
index 93c77f5..0000000
--- a/packages/SystemUI/res/values-lt/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN prijungtas"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN atjungtas"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Per „<xliff:g id="VPN_APP">%1$s</xliff:g>“"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Pranešimai"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nėra jokių pranešimų"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofonas įrašo"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera įrašo"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera ir mikrofonas įrašo"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofonas nebeįrašo"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera nebeįrašo"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera ir mikrofonas nebeįrašo"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Ekrano vaizdo įrašymas pradėtas"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Ekrano vaizdo įrašymas sustabdytas"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-lv/strings_tv.xml b/packages/SystemUI/res/values-lv/strings_tv.xml
deleted file mode 100644
index da64f32..0000000
--- a/packages/SystemUI/res/values-lv/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"Savienojums ar VPN ir izveidots."</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Savienojums ar VPN ir pārtraukts."</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Izmantojot: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Paziņojumi"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nav paziņojumu"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Notiek ierakstīšana ar mikrofonu"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Notiek ierakstīšana ar kameru"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Notiek ierakstīšana ar kameru un mikrofonu"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Ierakstīšana ar mikrofonu apturēta"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Ierakstīšana ar kameru apturēta"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Ierakstīšana ar kameru un mikrofonu apturēta"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Ekrāna ierakstīšana ir sākta."</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Ekrāna ierakstīšana ir apturēta."</string>
-</resources>
diff --git a/packages/SystemUI/res/values-mk/strings_tv.xml b/packages/SystemUI/res/values-mk/strings_tv.xml
deleted file mode 100644
index 529d19d..0000000
--- a/packages/SystemUI/res/values-mk/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN е поврзана"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN е исклучена"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Преку <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Известувања"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Нема известувања"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Микрофонот снима"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Камерата снима"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Камерата и микрофонот снимаат"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Микрофонот прекина со снимање"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Камерата прекина со снимање"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Камерата и микрофонот прекинаа со снимање"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Снимањето екран започна"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Снимањето екран сопре"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ml/strings_tv.xml b/packages/SystemUI/res/values-ml/strings_tv.xml
deleted file mode 100644
index 2faccb8..0000000
--- a/packages/SystemUI/res/values-ml/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN കണക്റ്റ് ചെയ്തു"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN വിച്ഛേദിച്ചു"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> വഴി"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"അറിയിപ്പുകൾ"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"അറിയിപ്പുകൾ ഒന്നുമില്ല"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"മൈക്രോഫോൺ റെക്കോർഡ് ചെയ്യുകയാണ്"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"ക്യാമറ റെക്കോർഡ് ചെയ്യുകയാണ്"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"ക്യാമറയും മൈക്രോഫോണും റെക്കോർഡ് ചെയ്യുകയാണ്"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"മൈക്രോഫോൺ റെക്കോർഡ് ചെയ്യുന്നത് നിർത്തി"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"ക്യാമറ റെക്കോർഡ് ചെയ്യുന്നത് നിർത്തി"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"ക്യാമറയും മൈക്രോഫോണും റെക്കോർഡ് ചെയ്യുന്നത് നിർത്തി"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"സ്ക്രീൻ റെക്കോർഡിംഗ് ആരംഭിച്ചു"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"സ്ക്രീൻ റെക്കോർഡിംഗ് നിർത്തി"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-mn/strings_tv.xml b/packages/SystemUI/res/values-mn/strings_tv.xml
deleted file mode 100644
index c9b667c..0000000
--- a/packages/SystemUI/res/values-mn/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN холбогдсон"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN салсан"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g>-р"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Мэдэгдэл"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Мэдэгдэл байхгүй байна"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Микрофон бичиж байна"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Камер бичиж байна"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Камер болон микрофон бичиж байна"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Микрофон бичихээ зогсоосон"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Камер бичихээ зогсоосон"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Камер болон микрофон бичихээ зогсоосон"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Дэлгэцийн бичлэгийг эхлүүлсэн"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Дэлгэцийн бичлэгийг зогсоосон"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-mr/strings_tv.xml b/packages/SystemUI/res/values-mr/strings_tv.xml
deleted file mode 100644
index 74d60cd..0000000
--- a/packages/SystemUI/res/values-mr/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN कनेक्ट केले"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN डिस्कनेक्ट केले"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> द्वारे"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"सूचना"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"सूचना नाहीत"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"मायक्रोफोन रेकॉर्ड करत आहे"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"कॅमेरा रेकॉर्ड करत आहे"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"कॅमेरा आणि मायक्रोफोन रेकॉर्ड करत आहेत"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"मायक्रोफोनने रेकॉर्ड करणे थांबवले"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"कॅमेराने रेकॉर्ड करणे थांबवले"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"कॅमेरा आणि मायक्रोफोनने रेकॉर्ड करणे थांबवले"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"स्क्रीन रेकॉर्डिंग सुरू झाले आहे"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"स्क्रीन रेकॉर्डिंग थांबले आहे"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ms/strings_tv.xml b/packages/SystemUI/res/values-ms/strings_tv.xml
deleted file mode 100644
index 08b9de0..0000000
--- a/packages/SystemUI/res/values-ms/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN telah disambungkan"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN diputuskan sambungan"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Melalui <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Pemberitahuan"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Tiada Pemberitahuan"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon sedang merakam"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera sedang merakam"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera dan Mikrofon sedang merakam"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon berhenti merakam"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera berhenti merakam"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera dan Mikrofon berhenti merakam"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Rakaman skrin dimulakan"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Rakaman skrin dihentikan"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-my/strings_tv.xml b/packages/SystemUI/res/values-my/strings_tv.xml
deleted file mode 100644
index 0ac3950..0000000
--- a/packages/SystemUI/res/values-my/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN ချိတ်ဆက်ထားသည်"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ချိတ်ဆက်မှုမရှိပါ"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> မှတစ်ဆင့်"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"အကြောင်းကြားချက်များ"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"အကြောင်းကြားချက်များ မရှိပါ"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"မိုက်ခရိုဖုန်း မှတ်တမ်းတင်နေသည်"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"ကင်မရာ မှတ်တမ်းတင်နေသည်"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"ကင်မရာနှင့် မိုက်ခရိုဖုန်းက မှတ်တမ်းတင်နေသည်"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"မိုက်ခရိုဖုန်း မှတ်တမ်းတင်ခြင်းကို ရပ်ထားသည်"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"ကင်မရာ မှတ်တမ်းတင်ခြင်းကို ရပ်ထားသည်"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"ကင်မရာနှင့် မိုက်ခရိုဖုန်းက မှတ်တမ်းတင်ခြင်းကို ရပ်ထားသည်"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"မျက်နှာပြင် ရိုက်ကူးမှု စတင်လိုက်ပါပြီ"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"မျက်နှာပြင် ရိုက်ကူးမှုကို ရပ်လိုက်ပါပြီ"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-nb/strings_tv.xml b/packages/SystemUI/res/values-nb/strings_tv.xml
deleted file mode 100644
index 5848f58..0000000
--- a/packages/SystemUI/res/values-nb/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN er tilkoblet"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN er frakoblet"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Varsler"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Ingen varsler"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofonen tar opp"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kameraet tar opp"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kameraet og mikrofonen tar opp"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofonen stoppet opptaket"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kameraet stoppet opptaket"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kameraet og mikrofonen stoppet opptaket"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Skjermopptaket er startet"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Skjermopptaket er stoppet"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ne/strings_tv.xml b/packages/SystemUI/res/values-ne/strings_tv.xml
deleted file mode 100644
index ffc315b..0000000
--- a/packages/SystemUI/res/values-ne/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN कनेक्ट गरिएको छ"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN डिस्कनेक्ट गरिएको छ"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> मार्फत"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"सूचनाहरू"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"कुनै पनि सूचना छैन"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"माइक्रोफोनले रेकर्ड गर्दै छ"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"क्यामेराले रेकर्ड गर्दै छ"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"क्यामेरा र माइक्रोफोनले रेकर्ड गर्दै छन्"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"माइक्रोफोनले रेकर्ड गर्न छाड्यो"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"क्यामेराले रेकर्ड गर्न छाड्यो"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"क्यामेरा र माइक्रोफोनले रेकर्ड गर्न छाडे"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"स्क्रिन रेकर्ड गर्न थालियो"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"स्क्रिन रेकर्ड गर्न छाडियो"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-nl/strings_tv.xml b/packages/SystemUI/res/values-nl/strings_tv.xml
deleted file mode 100644
index e018d97..0000000
--- a/packages/SystemUI/res/values-nl/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"Verbinding met VPN"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Geen verbinding met VPN"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Meldingen"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Geen meldingen"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Microfoon neemt op"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Camera neemt op"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Camera en microfoon nemen op"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Microfoon neemt niet meer op"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Camera neemt niet meer op"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Camera en microfoon nemen niet meer op"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Schermopname gestart"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Schermopname gestopt"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-or/strings_tv.xml b/packages/SystemUI/res/values-or/strings_tv.xml
deleted file mode 100644
index 178bd4b..0000000
--- a/packages/SystemUI/res/values-or/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN ସଂଯୋଗ କରାଯାଇଛି"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ବିଚ୍ଛିନ୍ନ କରାଯାଇଛି"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> ମାଧ୍ୟମରେ"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"କୌଣସି ବିଜ୍ଞପ୍ତି ନାହିଁ"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"ମାଇକ୍ରୋଫୋନ୍ ରେକର୍ଡିଂ କରୁଛି"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"କ୍ୟାମେରା ରେକର୍ଡିଂ କରୁଛି"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"କ୍ୟାମେରା ଏବଂ ମାଇକ୍ରୋଫୋନ୍ ରେକର୍ଡିଂ କରୁଛି"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"ମାଇକ୍ରୋଫୋନ୍ ରେକର୍ଡିଂ ବନ୍ଦ କରିଛି"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"କ୍ୟାମେରା ରେକର୍ଡିଂ ବନ୍ଦ କରିଛି"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"କ୍ୟାମେରା ଏବଂ ମାଇକ୍ରୋଫୋନ୍ ରେକର୍ଡିଂ ବନ୍ଦ କରିଛି"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"ସ୍କ୍ରିନ ରେକର୍ଡିଂ ଆରମ୍ଭ କରାଯାଇଛି"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"ସ୍କ୍ରିନ ରେକର୍ଡିଂ ବନ୍ଦ କରାଯାଇଛି"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-pa/strings_tv.xml b/packages/SystemUI/res/values-pa/strings_tv.xml
deleted file mode 100644
index 4939a73..0000000
--- a/packages/SystemUI/res/values-pa/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN ਕਨੈਕਟ ਹੈ"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ਡਿਸਕਨੈਕਟ ਹੈ"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> ਰਾਹੀਂ"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"ਸੂਚਨਾਵਾਂ"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"ਕੋਈ ਸੂਚਨਾ ਨਹੀਂ"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਰਿਕਾਰਡ ਕਰ ਰਿਹਾ ਹੈ"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"ਕੈਮਰਾ ਰਿਕਾਰਡ ਕਰ ਰਿਹਾ ਹੈ"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"ਕੈਮਰਾ ਅਤੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਰਿਕਾਰਡ ਕਰ ਰਹੇ ਹਨ"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੇ ਰਿਕਾਰਡ ਕਰਨਾ ਬੰਦ ਕਰ ਦਿੱਤਾ ਹੈ"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"ਕੈਮਰੇ ਨੇ ਰਿਕਾਰਡ ਕਰਨਾ ਬੰਦ ਕਰ ਦਿੱਤਾ ਹੈ"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"ਕੈਮਰੇ ਅਤੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੇ ਰਿਕਾਰਡ ਕਰਨਾ ਬੰਦ ਕਰ ਦਿੱਤਾ ਹੈ"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਸ਼ੁਰੂ ਹੋਈ"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਬੰਦ ਹੋਈ"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-pl/strings_tv.xml b/packages/SystemUI/res/values-pl/strings_tv.xml
deleted file mode 100644
index 98db830..0000000
--- a/packages/SystemUI/res/values-pl/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"Połączono z VPN"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Rozłączono z VPN"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Przez: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Powiadomienia"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Brak powiadomień"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon rejestruje"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Aparat rejestruje"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Aparat i mikrofon rejestrują"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon przestał rejestrować"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Aparat przestał rejestrować"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Aparat i mikrofon przestały rejestrować"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Rozpoczęto nagrywanie ekranu"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Zatrzymano nagrywanie ekranu"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings_tv.xml b/packages/SystemUI/res/values-pt-rBR/strings_tv.xml
deleted file mode 100644
index 6c5ff0c..0000000
--- a/packages/SystemUI/res/values-pt-rBR/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"A VPN está conectada"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"A VPN está desconectada"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notificações"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nenhuma notificação"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"O microfone está gravando"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"A câmera está gravando"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"A câmera e o microfone estão gravando"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"O microfone parou de gravar"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"A câmera parou de gravar"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"A câmera e o microfone pararam de gravar"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Gravação de tela iniciada"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Gravação de tela interrompida"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings_tv.xml b/packages/SystemUI/res/values-pt-rPT/strings_tv.xml
deleted file mode 100644
index c3e6c43..0000000
--- a/packages/SystemUI/res/values-pt-rPT/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"A VPN está ligada"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"A VPN está desligada"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Através de <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notificações"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Sem notificações"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"O microfone está a gravar"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"A câmara está a gravar"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"A câmara e o microfone estão a gravar"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"O microfone parou a gravação"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"A câmara parou a gravação"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"A câmara e o microfone pararam a gravação"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Gravação de ecrã iniciada"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Gravação de ecrã parada"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-pt/strings_tv.xml b/packages/SystemUI/res/values-pt/strings_tv.xml
deleted file mode 100644
index 6c5ff0c..0000000
--- a/packages/SystemUI/res/values-pt/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"A VPN está conectada"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"A VPN está desconectada"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notificações"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nenhuma notificação"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"O microfone está gravando"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"A câmera está gravando"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"A câmera e o microfone estão gravando"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"O microfone parou de gravar"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"A câmera parou de gravar"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"A câmera e o microfone pararam de gravar"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Gravação de tela iniciada"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Gravação de tela interrompida"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ro/strings_tv.xml b/packages/SystemUI/res/values-ro/strings_tv.xml
deleted file mode 100644
index 991eef6..0000000
--- a/packages/SystemUI/res/values-ro/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN este conectat"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN este deconectat"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Prin <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notificări"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nicio notificare"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Microfonul înregistrează"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Camera foto înregistrează"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Camera foto și microfonul înregistrează"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Microfonul nu mai înregistrează"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Camera foto a oprit înregistrarea"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Camera foto și microfonul nu mai înregistrează"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Înregistrarea ecranului a început"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Înregistrarea ecranului s-a oprit"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ru/strings_tv.xml b/packages/SystemUI/res/values-ru/strings_tv.xml
deleted file mode 100644
index e23558c..0000000
--- a/packages/SystemUI/res/values-ru/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN-подключение установлено"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN-подключение отключено"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Через приложение <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Уведомления"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Уведомлений нет."</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Выполняется запись с микрофона"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Выполняется запись с камеры"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Выполняется запись с камеры и микрофона"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Запись с микрофона остановлена"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Запись с камеры остановлена"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Запись с камеры и микрофона остановлена"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Запись видео с экрана началась."</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Запись видео с экрана остановлена."</string>
-</resources>
diff --git a/packages/SystemUI/res/values-si/strings_tv.xml b/packages/SystemUI/res/values-si/strings_tv.xml
deleted file mode 100644
index 1eaa24a..0000000
--- a/packages/SystemUI/res/values-si/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN සම්බන්ධිතයි"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN විසන්ධි කර ඇත"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> හරහා"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"දැනුම්දීම්"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"දැනුම්දීම් නැත"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"මයික්රෆෝනය පටිගත කරයි"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"කැමරාව පටිගත කරයි"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"කැමරාව සහ මයික්රෆෝනය පටිගත කරයි"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"මයික්රෆෝනය පටිගත කිරීම නැවැත්වීය"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"කැමරාව පටිගත කිරීම නැවැත්වීය"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"කැමරාව සහ මයික්රෆෝනය පටිගත කිරීම නැවැත්වීය"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"තිර පටිගත කිරීම ආරම්භ විය"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"තිර පටිගත කිරීම නතර විය"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-sk/strings_tv.xml b/packages/SystemUI/res/values-sk/strings_tv.xml
deleted file mode 100644
index a7479aa..0000000
--- a/packages/SystemUI/res/values-sk/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"Sieť VPN je pripojená"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Sieť VPN je odpojená"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Cez: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Upozornenia"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Žiadne upozornenia"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofón nahráva"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera nahráva"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera a mikrofón nahrávajú"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofón prestal nahrávať"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera prestala nahrávať"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera a mikrofón prestali nahrávať"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Nahrávanie obrazovky bolo spustené"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Nahrávanie obrazovky bolo zastavené"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-sl/strings_tv.xml b/packages/SystemUI/res/values-sl/strings_tv.xml
deleted file mode 100644
index 765b590..0000000
--- a/packages/SystemUI/res/values-sl/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"Povezava z omrežjem VPN je vzpostavljena"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Povezava z omrežjem VPN je prekinjena"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Prek storitve <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Obvestila"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Ni obvestil"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Snemanje z mikrofonom"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Snemanje s fotoaparatom"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Snemanje s fotoaparatom in mikrofonom"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Ustavljeno snemanje z mikrofonom"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Ustavljeno snemanje s fotoaparatom"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Ustavljeno snemanje s fotoaparatom in mikrofonom"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Snemanje zaslona se je začelo."</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Snemanje zaslona je ustavljeno."</string>
-</resources>
diff --git a/packages/SystemUI/res/values-sq/strings_tv.xml b/packages/SystemUI/res/values-sq/strings_tv.xml
deleted file mode 100644
index 12b42b8..0000000
--- a/packages/SystemUI/res/values-sq/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN-ja është e lidhur"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN-ja është shkëputur"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Nëpërmjet <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Njoftimet"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Asnjë njoftim"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"\"Mikrofoni\" po regjistron"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"\"Kamera\" po regjistron"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"\"Kamera\" dhe \"Mikrofoni\" po regjistrojnë"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"\"Mikrofoni\" ndaloi së regjistruari"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"\"Kamera\" ndaloi së regjistruari"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"\"Kamera\" dhe \"Mikrofoni\" ndaluan së regjistruari"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Regjistrimi i ekranit filloi"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Regjistrimi i ekranit ndaloi"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-sr/strings_tv.xml b/packages/SystemUI/res/values-sr/strings_tv.xml
deleted file mode 100644
index 85c38ca..0000000
--- a/packages/SystemUI/res/values-sr/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN је повезан"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Веза са VPN-ом је прекинута"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Преко: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Обавештења"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Нема обавештења"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Микрофон снима"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Камера снима"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Камера и микрофон снимају"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Снимање микрофоном је заустављено"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Снимање камером је заустављено"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Снимање камером и микрофоном је заустављено"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Снимање екрана је започето"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Снимање екрана је заустављено"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-sv/strings_tv.xml b/packages/SystemUI/res/values-sv/strings_tv.xml
deleted file mode 100644
index 6830436..0000000
--- a/packages/SystemUI/res/values-sv/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN är anslutet"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN är frånkopplat"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Aviseringar"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Inga aviseringar"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofonen spelar in"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kameran spelar in"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kameran och mikrofonen spelar in"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofonen slutade spela in"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kameran slutade spela in"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kameran och mikrofonen slutade spelade in"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Skärminspelningen har startats"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Skärminspelningen har stoppats"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-sw/strings_tv.xml b/packages/SystemUI/res/values-sw/strings_tv.xml
deleted file mode 100644
index f3e2ca2..0000000
--- a/packages/SystemUI/res/values-sw/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN imeunganishwa"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN imeondolewa"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Kupitia <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Arifa"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Hakuna Arifa"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Maikrofoni inarekodi"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera inarekodi"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera na Maikrofoni zinarekodi"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Maikrofoni imeacha kurekodi"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera imeacha kurekodi"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera na Maikrofoni zimeacha kurekodi"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Imeanza kurekodi skrini"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Imeacha kurekodi skrini"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ta/strings_tv.xml b/packages/SystemUI/res/values-ta/strings_tv.xml
deleted file mode 100644
index 3e0baf6..0000000
--- a/packages/SystemUI/res/values-ta/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN இணைக்கப்பட்டது"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN துண்டிக்கப்பட்டது"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> வழியாக"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"அறிவிப்புகள்"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"அறிவிப்புகள் எதுவுமில்லை"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"மைக்ரோஃபோன் ரெக்கார்டு செய்கிறது"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"கேமரா ரெக்கார்டு செய்கிறது"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"கேமராவும் மைக்ரோஃபோனும் ரெக்கார்டு செய்கின்றன"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"மைக்ரோஃபோன் ரெக்கார்டு செய்வதை நிறுத்திவிட்டது"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"கேமரா ரெக்கார்டு செய்வதை நிறுத்திவிட்டது"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"கேமராவும் மைக்ரோஃபோனும் ரெக்கார்டு செய்வதை நிறுத்திவிட்டன"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"ஸ்கிரீன் ரெக்கார்டிங் தொடங்கியது"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"ஸ்கிரீன் ரெக்கார்டிங் நிறுத்தப்பட்டது"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-te/strings_tv.xml b/packages/SystemUI/res/values-te/strings_tv.xml
deleted file mode 100644
index 2131064..0000000
--- a/packages/SystemUI/res/values-te/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN కనెక్ట్ అయింది"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN డిస్కనెక్ట్ అయింది"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> ద్వారా"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"నోటిఫికేషన్లు"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"నోటిఫికేషన్లు లేవు"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"మైక్రోఫోన్ రికార్డింగ్ చేస్తోంది"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"కెమెరా రికార్డింగ్ చేస్తోంది"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"కెమెరా, మైక్రోఫోన్లు రికార్డింగ్ చేస్తున్నాయి"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"మైక్రోఫోన్ రికార్డింగ్ చేయడం ఆపివేసింది"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"రికార్డింగ్ చేయడాన్ని కెమెరా ఆపివేసింది"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"కెమెరా, మైక్రోఫోన్లు రికార్డింగ్ చేయడం ఆపివేశాయి"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"స్క్రీన్ రికార్డింగ్ ప్రారంభమైంది"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"స్క్రీన్ రికార్డింగ్ ఆపివేయబడింది"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-television/colors.xml b/packages/SystemUI/res/values-television/colors.xml
deleted file mode 100644
index 3e9e182..0000000
--- a/packages/SystemUI/res/values-television/colors.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<resources>
- <color name="volume_dialog_background_color">#E61F232B</color>
- <color name="volume_dialog_background_color_above_blur">#C71F232B</color>
-
- <color name="bottom_sheet_icon_color">#D2E3FC</color>
-
- <color name="bottom_sheet_title_color">#E8F0FE</color>
- <color name="bottom_sheet_body_color">#D2E3FC</color>
-
- <color name="bottom_sheet_background_color">#1F232C</color>
- <color name="bottom_sheet_background_color_with_blur">#AA1A2734</color>
-
- <color name="bottom_sheet_button_background_color_focused">#E8F0FE</color>
- <color name="bottom_sheet_button_background_color_unfocused">#0FE8EAED</color>
-
- <color name="bottom_sheet_button_text_color_focused">#DB202124</color>
- <color name="bottom_sheet_button_text_color_unfocused">#B5E8EAED</color>
-
- <color name="privacy_mic_cam_chip">#5BB974</color> <!-- g400 -->
- <color name="privacy_media_projection_chip">#C9CCD0</color>
- <color name="privacy_icon_tint">#30302A</color>
- <color name="privacy_chip_dot_bg_tint">#66000000</color>
- <color name="cast_connected_fill">#FF0000</color>
-</resources>
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
deleted file mode 100644
index 2ace86f..0000000
--- a/packages/SystemUI/res/values-television/config.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2019, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
- for different hardware and product builds. -->
-<resources>
- <!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. -->
- <integer name="recents_svelte_level">3</integer>
-
- <!-- Show a separate icon for low and high volume on the volume dialog -->
- <bool name="config_showLowMediaVolumeIcon">true</bool>
-
- <!-- Change the volume row tint when it is inactive, i.e. when it is being dismissed -->
- <bool name="config_changeVolumeRowTintWhenInactive">false</bool>
-
- <!-- The duraction of the show animation for the volume dialog in milliseconds -->
- <integer name="config_dialogShowAnimationDurationMs">600</integer>
-
- <!-- The duraction of the hide animation for the volume dialog in milliseconds -->
- <integer name="config_dialogHideAnimationDurationMs">400</integer>
-
- <!-- Whether to use window background blur for the volume dialog. -->
- <bool name="config_volumeDialogUseBackgroundBlur">true</bool>
-
- <!-- Whether to tint the icon of the sensor hardware privacy toggle unblock dialog.
- Set to false if using a custom icon. -->
- <bool name="config_unblockHwSensorIconEnableTint">true</bool>
-
- <!-- Configuration to set Learn more in device logs as URL link -->
- <bool name="log_access_confirmation_learn_more_as_link">false</bool>
-</resources>
diff --git a/packages/SystemUI/res/values-television/dimens.xml b/packages/SystemUI/res/values-television/dimens.xml
deleted file mode 100644
index ee615d9..0000000
--- a/packages/SystemUI/res/values-television/dimens.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2020 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
- -->
-<resources>
- <!-- Opacity at which the background for the shutdown UI will be drawn. -->
- <item name="shutdown_scrim_behind_alpha" format="float" type="dimen">1.0</item>
-
- <dimen name="bottom_sheet_padding_horizontal">32dp</dimen>
- <dimen name="bottom_sheet_padding_vertical">24dp</dimen>
-
- <dimen name="bottom_sheet_icon_size">42dp</dimen>
- <dimen name="bottom_sheet_icon_margin">8dp</dimen>
- <dimen name="bottom_sheet_title_margin_bottom">18dp</dimen>
- <dimen name="bottom_sheet_details_margin_bottom">8dp</dimen>
-
- <dimen name="bottom_sheet_actions_width">296dp</dimen>
- <dimen name="bottom_sheet_actions_spacing">12dp</dimen>
- <item name="bottom_sheet_button_selection_scaled" format="float" type="dimen">1.1</item>
- <dimen name="bottom_sheet_button_width">232dp</dimen>
- <dimen name="bottom_sheet_button_padding_horizontal">20dp</dimen>
- <dimen name="bottom_sheet_button_padding_vertical">16dp</dimen>
-
- <dimen name="bottom_sheet_corner_radius">24dp</dimen>
- <dimen name="bottom_sheet_button_corner_radius">10dp</dimen>
-
- <dimen name="bottom_sheet_min_height">208dp</dimen>
- <dimen name="bottom_sheet_margin">24dp</dimen>
- <dimen name="bottom_sheet_background_blur_radius">37dp</dimen>
-
- <dimen name="privacy_chips_max_width">110dp</dimen>
- <dimen name="privacy_chips_bar_padding">9dp</dimen>
- <dimen name="privacy_chip_margin">3dp</dimen>
- <dimen name="privacy_chip_icon_margin_in_between">4dp</dimen>
- <dimen name="privacy_chip_padding_horizontal">5dp</dimen>
- <dimen name="privacy_chip_icon_size">12dp</dimen>
- <dimen name="privacy_chip_collapsed_icon_size">10dp</dimen>
- <dimen name="privacy_chip_height">24dp</dimen>
- <dimen name="privacy_chip_radius">12dp</dimen>
-
- <dimen name="privacy_chip_dot_size">8dp</dimen>
- <dimen name="privacy_chip_dot_radius">4dp</dimen>
-
- <dimen name="privacy_chip_dot_bg_width">24dp</dimen>
- <dimen name="privacy_chip_dot_bg_height">18dp</dimen>
- <dimen name="privacy_chip_dot_bg_radius">9dp</dimen>
-
- <dimen name="unblock_hw_sensor_icon_width">@dimen/bottom_sheet_icon_size</dimen>
- <dimen name="unblock_hw_sensor_icon_height">@dimen/bottom_sheet_icon_size</dimen>
-
-</resources>
diff --git a/packages/SystemUI/res/values-television/integers.xml b/packages/SystemUI/res/values-television/integers.xml
deleted file mode 100644
index 02f5d0d..0000000
--- a/packages/SystemUI/res/values-television/integers.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2020 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
- -->
-<resources>
- <!-- The position of the volume dialog on the screen.
- See com.android.systemui.volume.VolumeDialogImpl.
- Value 81 corresponds to BOTTOM|CENTER_HORIZONTAL.
- Value 21 corresponds to RIGHT|CENTER_VERTICAL.
- Value 8388629 corresponds to END|CENTER_VERTICAL -->
- <integer name="volume_dialog_gravity">8388629</integer>
-
- <integer name="privacy_chip_animation_millis">300</integer>
-</resources>
diff --git a/packages/SystemUI/res/values-television/styles.xml b/packages/SystemUI/res/values-television/styles.xml
index c517845..4a4fac2 100644
--- a/packages/SystemUI/res/values-television/styles.xml
+++ b/packages/SystemUI/res/values-television/styles.xml
@@ -14,15 +14,9 @@
limitations under the License.
-->
+<!-- TODO(b/289498394) move this to the TvSystemUI target -->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Dialog" />
- <style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Dialog.Alert" />
-
- <style name="Animation.ShutdownUi">
- <item name="android:windowEnterAnimation">@null</item>
- <item name="android:windowExitAnimation">@null</item>
- </style>
<style name="volume_dialog_theme" parent="Theme.SystemUI">
<item name="android:colorAccent">@color/tv_volume_dialog_accent</item>
@@ -30,43 +24,4 @@
<item name="android:dialogCornerRadius">@dimen/volume_dialog_panel_width_half</item>
</style>
- <style name="PrivacyChip">
- <item name="android:colorError">@color/cast_connected_fill</item>
- </style>
-
- <style name="BottomSheet" parent="Theme.Leanback">
- <item name="android:windowIsFloating">true</item>
- <item name="android:windowActivityTransitions">true</item>
- <item name="android:windowNoTitle">true</item>
- <item name="android:windowIsTranslucent">true</item>
- <item name="android:backgroundDimAmount">0.2</item>
- </style>
-
- <style name="BottomSheet.TitleText">
- <item name="android:textSize">28sp</item>
- <item name="android:textColor">@color/bottom_sheet_title_color</item>
- </style>
-
- <style name="BottomSheet.BodyText">
- <item name="android:textSize">16sp</item>
- <item name="android:textColor">@color/bottom_sheet_body_color</item>
- </style>
-
- <style name="BottomSheet.ActionItem">
- <item name="android:layout_width">@dimen/bottom_sheet_button_width</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:gravity">left|center_vertical</item>
- <item name="android:textSize">16sp</item>
- <item name="android:textColor">@color/bottom_sheet_button_text_color</item>
- <item name="android:background">@drawable/bottom_sheet_button_background</item>
- <item name="android:paddingHorizontal">@dimen/bottom_sheet_button_padding_horizontal</item>
- <item name="android:paddingVertical">@dimen/bottom_sheet_button_padding_vertical</item>
- <item name="android:stateListAnimator">@anim/tv_bottom_sheet_button_state_list_animator</item>
- </style>
-
- <!-- The style for log access consent button -->
- <style name="LogAccessDialogTheme" parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
- <item name="permissionGrantButtonTopStyle">?android:buttonBarButtonStyle</item>
- <item name="permissionGrantButtonBottomStyle">?android:buttonBarButtonStyle</item>
- </style>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings_tv.xml b/packages/SystemUI/res/values-th/strings_tv.xml
deleted file mode 100644
index 1df2612..0000000
--- a/packages/SystemUI/res/values-th/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"เชื่อมต่อ VPN แล้ว"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"ยกเลิกการเชื่อมต่อ VPN แล้ว"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"ผ่าน <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"การแจ้งเตือน"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"ไม่มีการแจ้งเตือน"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"ไมโครโฟนกำลังบันทึก"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"กล้องกำลังบันทึก"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"กล้องและไมโครโฟนกำลังบันทึก"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"ไมโครโฟนหยุดบันทึกแล้ว"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"กล้องหยุดบันทึกแล้ว"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"กล้องและไมโครโฟนหยุดบันทึกแล้ว"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"เริ่มบันทึกหน้าจอแล้ว"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"หยุดการบันทึกหน้าจอแล้ว"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-tl/strings_tv.xml b/packages/SystemUI/res/values-tl/strings_tv.xml
deleted file mode 100644
index 890ccd4..0000000
--- a/packages/SystemUI/res/values-tl/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"Nakakonekta ang VPN"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Nakadiskonekta ang VPN"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Sa pamamagitan ng <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Mga Notification"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Walang Notification"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Nagre-record ang Mikropono"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Nagre-record ang Camera"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Nagre-record ang Camera at Mikropono"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Huminto sa pag-record ang Mikropono"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Huminto sa pag-record ang Camera"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Huminto sa pag-record ang Camera at Mikropono"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Sinimulan ang pag-record ng screen"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Itinigil ang pag-record ng screen"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-tr/strings_tv.xml b/packages/SystemUI/res/values-tr/strings_tv.xml
deleted file mode 100644
index f981aa2..0000000
--- a/packages/SystemUI/res/values-tr/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN bağlandı"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN bağlantısı kesildi"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> üzerinden"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Bildirimler"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Bildirim Yok"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon kaydediyor"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera kaydediyor"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera ve Mikrofon kaydediyor"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon kaydı durdu"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera kaydı durdu"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera ve Mikrofon kaydı durdu"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Ekran kaydı başladı"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Ekran kaydı durduruldu"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-uk/strings_tv.xml b/packages/SystemUI/res/values-uk/strings_tv.xml
deleted file mode 100644
index 8b96aac..0000000
--- a/packages/SystemUI/res/values-uk/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"Мережу VPN під\'єднано"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Мережу VPN від\'єднано"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Через <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Сповіщення"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Немає сповіщень"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Мікрофон записує"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Камера записує"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Камера й мікрофон записують"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Мікрофон припинив запис"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Камера припинила запис"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Камера й мікрофон припинили запис"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Запис відео з екрана розпочато"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Запис відео з екрана зупинено"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ur/strings_tv.xml b/packages/SystemUI/res/values-ur/strings_tv.xml
deleted file mode 100644
index ed186ac..0000000
--- a/packages/SystemUI/res/values-ur/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN منسلک ہے"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN غیر منسلک ہے"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"بذریعہ <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"اطلاعات"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"کوئی اطلاع نہیں ہے"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"مائیکروفون ریکارڈ کر رہا ہے"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"کیمرا ریکارڈ کر رہا ہے"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"کیمرا اور مائیکروفون ریکارڈ کر رہا ہے"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"مائیکروفون نے ریکارڈ کرنا بند کر دیا"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"کیمرے نے ریکارڈ کرنا بند کر دیا"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"کیمرے اور مائیکروفون نے ریکارڈ کرنا بند کر دیا"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"اسکرین ریکارڈنگ شروع ہو گئی"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"اسکرین ریکارڈنگ بند کر دی گئی"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-uz/strings_tv.xml b/packages/SystemUI/res/values-uz/strings_tv.xml
deleted file mode 100644
index c34ee4f..0000000
--- a/packages/SystemUI/res/values-uz/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN ulandi"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN uzildi"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> orqali"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Bildirishnomalar"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Bildirishnomalar yoʻq"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon yozib olmoqda"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera yozib olmoqda"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera va mikrofon yozib olmoqda"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon yozib olishni toʻxtatdi"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera yozib olishni toʻxtatdi"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera va mikrofon yozib olishni toʻxtatdi"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Ekrandan yozib olish boshlandi"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Ekrandan yozib olish toʻxtatildi"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-vi/strings_tv.xml b/packages/SystemUI/res/values-vi/strings_tv.xml
deleted file mode 100644
index b140fc0..0000000
--- a/packages/SystemUI/res/values-vi/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN đã được kết nối"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN đã bị ngắt kết nối"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Thông qua <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Thông báo"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Không có thông báo"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Micrô đang ghi"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Máy ảnh đang ghi"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Máy ảnh và micrô đang ghi"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Micrô đã dừng ghi"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Máy ảnh đã dừng ghi"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Máy ảnh và micrô đã dừng ghi"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Đã bắt đầu ghi màn hình"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Đã dừng ghi màn hình"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings_tv.xml b/packages/SystemUI/res/values-zh-rCN/strings_tv.xml
deleted file mode 100644
index ad4d94b0..0000000
--- a/packages/SystemUI/res/values-zh-rCN/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN 已连接"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN 已断开连接"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"通过“<xliff:g id="VPN_APP">%1$s</xliff:g>”"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"通知"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"没有通知"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"麦克风正在录制"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"相机正在录制"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"相机和麦克风正在录制"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"麦克风已停止录制"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"相机已停止录制"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"相机和麦克风已停止录制"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"已开始录制屏幕"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"已停止录制屏幕"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings_tv.xml b/packages/SystemUI/res/values-zh-rHK/strings_tv.xml
deleted file mode 100644
index 88ec5e0..0000000
--- a/packages/SystemUI/res/values-zh-rHK/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN 已連線"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN 已中斷連線"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"透過 <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"通知"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"沒有通知"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"麥克風正在錄音"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"相機正在錄影"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"相機和麥克風正在錄影及錄音"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"麥克風已停止錄音"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"相機已停止錄影"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"相機和麥克風已停止錄影及錄音"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"已開始錄製螢幕畫面"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"已停止錄製螢幕畫面"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings_tv.xml b/packages/SystemUI/res/values-zh-rTW/strings_tv.xml
deleted file mode 100644
index b6b1b1e..0000000
--- a/packages/SystemUI/res/values-zh-rTW/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN 已連線"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN 連線已中斷"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"透過「<xliff:g id="VPN_APP">%1$s</xliff:g>」"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"通知"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"沒有通知"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"麥克風正在錄音"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"相機正在錄影"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"相機和麥克風正在錄影及錄音"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"麥克風已停止錄音"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"相機已停止錄影"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"相機和麥克風已停止錄影及錄音"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"已開始錄製螢幕畫面"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"已停止錄製螢幕畫面"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-zu/strings_tv.xml b/packages/SystemUI/res/values-zu/strings_tv.xml
deleted file mode 100644
index 59fcb8d..0000000
--- a/packages/SystemUI/res/values-zu/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="notification_vpn_connected" msgid="3891023882833274730">"I-VPN ixhunyiwe"</string>
- <string name="notification_vpn_disconnected" msgid="7150747626448044843">"I-VPN inqanyuliwe"</string>
- <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Nge-<xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
- <string name="tv_notification_panel_title" msgid="5311050946506276154">"Izaziso"</string>
- <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Azikho Izaziso"</string>
- <string name="mic_recording_announcement" msgid="7587123608060316575">"Imakrofoni iyarekhoda"</string>
- <string name="camera_recording_announcement" msgid="7240177719403759112">"Ikhamera iyarekhoda"</string>
- <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Ikhamera nemakrofoni kuyarekhoda"</string>
- <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Imakrofoni iyekile ukurekhoda"</string>
- <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Ikhamera iyeke ukurekhoda"</string>
- <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Ikhemera nemakrofoni kuyekile ukurekhoda"</string>
- <string name="screen_recording_announcement" msgid="2996750593472241520">"Ukurekhoda isikrini kuqalile"</string>
- <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Ukurekhoda isikrini kumisiwe"</string>
-</resources>
diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml
index 51b57bd..2bab3cb 100644
--- a/packages/SystemUI/res/values/colors_tv.xml
+++ b/packages/SystemUI/res/values/colors_tv.xml
@@ -16,6 +16,7 @@
* limitations under the License.
*/
-->
+<!-- TODO(b/289498394) move this to the TvSystemUI target -->
<resources>
<color name="recents_tv_card_background_color">#FF263238</color>
<color name="recents_tv_card_title_text_color">#CCEEEEEE</color>
@@ -32,8 +33,4 @@
<color name="tv_volume_dialog_seek_bar_background">#A03C4043</color>
<color name="tv_volume_dialog_seek_bar_fill">#FFF8F9FA</color>
<color name="tv_volume_dialog_accent">#FFDADCE0</color>
-
- <color name="tv_notification_default_background_color">#383838</color>
- <color name="tv_notification_blur_background_color">#a0383838</color>
- <color name="tv_notification_text_color">#FFFFFF</color>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 9c864ab..a38c629 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -366,10 +366,16 @@
<!-- Whether or not notifications that can be expanded will always be in their expanded state.
This value only affects notifications that are not a group of notifications from the same
- applications. If this value is false, then only the first notification will be expanded;
- the other notifications need to be manually expanded by the user. -->
+ applications. If this value is false, then only the first notification will be expanded
+ when config_autoExpandFirstNotification is true; the other notifications need to be
+ manually expanded by the user. -->
<bool name="config_alwaysExpandNonGroupedNotifications">false</bool>
+ <!-- Whether or not the first expandable notification will be expanded automatically by the
+ system. This value only affects notifications that are not a group of notifications from
+ the same applications and when config_alwaysExpandNonGroupedNotifications is false. -->
+ <bool name="config_autoExpandFirstNotification">true</bool>
+
<!-- Whether or not an expandable notification can be manually expanded or collapsed by the
user. Grouped notifications are still expandable even if this value is false. -->
<bool name="config_enableNonGroupedNotificationExpand">true</bool>
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
deleted file mode 100644
index 8e372e4..0000000
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Title and subtitle for AudioRecordingIndicator -->
-
- <string name="notification_vpn_connected">VPN is connected</string>
- <string name="notification_vpn_disconnected">VPN is disconnected</string>
- <!-- Disclosure text in the connected notification that indicates that the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=40] -->
- <string name="notification_disclosure_vpn_text">Via <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
-
- <string name="tv_notification_panel_title">Notifications</string>
- <string name="tv_notification_panel_no_notifications">No Notifications</string>
-
- <string name="mic_recording_announcement">Microphone is recording</string>
- <string name="camera_recording_announcement">Camera is recording</string>
- <string name="mic_and_camera_recording_announcement">Camera and Microphone are recording</string>
- <string name="mic_stopped_recording_announcement">Microphone stopped recording</string>
- <string name="camera_stopped_recording_announcement">Camera stopped recording</string>
- <string name="mic_camera_stopped_recording_announcement">Camera and Microphone stopped recording</string>
- <string name="screen_recording_announcement">Screen recording started</string>
- <string name="screen_stopped_recording_announcement">Screen recording stopped</string>
-
-</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 6b85621..31f40e9 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -1126,13 +1126,13 @@
<style name="Widget.Dialog.Button.BorderButton">
<item name="android:background">@drawable/qs_dialog_btn_outline</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textColor">@color/qs_dialog_btn_outline_text</item>
</style>
<style name="Widget.Dialog.Button.Large">
<item name="android:background">@drawable/qs_dialog_btn_filled_large</item>
<item name="android:minHeight">56dp</item>
- <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryFixed</item>
+ <item name="android:textColor">@color/qs_dialog_btn_filled_large_text</item>
</style>
<style name="Widget.Dialog.Button.QuickSettings">
diff --git a/packages/SystemUI/res/values/styles_tv.xml b/packages/SystemUI/res/values/styles_tv.xml
deleted file mode 100644
index 3e09026..0000000
--- a/packages/SystemUI/res/values/styles_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/**
- * Copyright (c) 2016, 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.
- */
--->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <style name="PipTheme" parent="@android:style/Theme.Translucent.NoTitleBar.Fullscreen">
- <item name="android:windowBackground">@android:color/transparent</item>
- <item name="android:backgroundDimEnabled">false</item>
- <item name="android:windowDisablePreview">true</item>
- </style>
-
- <style name="TvSidePanelTheme">
- <item name="android:windowIsTranslucent">true</item>
- <item name="android:windowBackground">@android:color/transparent</item>
- <item name="android:backgroundDimEnabled">false</item>
- <item name="android:windowNoTitle">true</item>
- <item name="android:windowContentOverlay">@null</item>
- <item name="android:windowIsFloating">true</item>
- </style>
-</resources>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 28e786b..ca30e15 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -65,7 +65,7 @@
],
min_sdk_version: "current",
plugins: ["dagger2-compiler"],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
}
java_library {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt
index 50e5466..1cb8e43 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt
@@ -12,12 +12,9 @@
) : FrameLayout(context, attrs) {
private var drawAlpha: Int = 255
- init {
- setLayerType(View.LAYER_TYPE_SOFTWARE, null)
- }
-
protected override fun onSetAlpha(alpha: Int): Boolean {
- drawAlpha = alpha
+ // Ignore alpha passed from View, prefer to compute it from set values
+ drawAlpha = (255 * this.alpha * transitionAlpha).toInt()
return true
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 2377057..d9b7bde 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -69,7 +69,7 @@
(long) (125 * KeyguardPatternView.DISAPPEAR_MULTIPLIER_LOCKED),
0.6f /* translationScale */,
0.45f /* delayScale */, AnimationUtils.loadInterpolator(
- mContext, android.R.interpolator.fast_out_linear_in));
+ mContext, android.R.interpolator.fast_out_linear_in));
mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation);
mYTrans = getResources().getDimensionPixelSize(R.dimen.pin_view_trans_y_entry);
@@ -82,8 +82,10 @@
}
void onDevicePostureChanged(@DevicePostureInt int posture) {
- mLastDevicePosture = posture;
- updateMargins();
+ if (mLastDevicePosture != posture) {
+ mLastDevicePosture = posture;
+ updateMargins();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 38c07dc..2bdf46e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -104,8 +104,10 @@
}
void onDevicePostureChanged(@DevicePostureInt int posture) {
- mLastDevicePosture = posture;
- updateMargins();
+ if (mLastDevicePosture != posture) {
+ mLastDevicePosture = posture;
+ updateMargins();
+ }
}
private void updateMargins() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index a6252a3..7585279 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -113,6 +113,7 @@
public void dump(PrintWriter pw, String[] args) {
pw.println("KeyguardStatusView:");
pw.println(" mDarkAmount: " + mDarkAmount);
+ pw.println(" visibility: " + getVisibility());
if (mClockView != null) {
mClockView.dump(pw, args);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 6854c97..a04d13b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -37,15 +37,19 @@
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
+import androidx.viewpager.widget.ViewPager;
import com.android.app.animation.Interpolators;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.keyguard.KeyguardClockSwitch.ClockSize;
import com.android.keyguard.logging.KeyguardLogger;
+import com.android.systemui.Dumpable;
import com.android.systemui.R;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
@@ -58,14 +62,17 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
+import java.io.PrintWriter;
+
import javax.inject.Inject;
/**
* Injectable controller for {@link KeyguardStatusView}.
*/
-public class KeyguardStatusViewController extends ViewController<KeyguardStatusView> {
+public class KeyguardStatusViewController extends ViewController<KeyguardStatusView> implements
+ Dumpable {
private static final boolean DEBUG = KeyguardConstants.DEBUG;
- private static final String TAG = "KeyguardStatusViewController";
+ @VisibleForTesting static final String TAG = "KeyguardStatusViewController";
/**
* Duration to use for the animator when the keyguard status view alignment changes, and a
@@ -87,6 +94,8 @@
private Boolean mStatusViewCentered = true;
+ private DumpManager mDumpManager;
+
private final TransitionListenerAdapter mKeyguardStatusAlignmentTransitionListener =
new TransitionListenerAdapter() {
@Override
@@ -112,7 +121,8 @@
ScreenOffAnimationController screenOffAnimationController,
KeyguardLogger logger,
FeatureFlags featureFlags,
- InteractionJankMonitor interactionJankMonitor) {
+ InteractionJankMonitor interactionJankMonitor,
+ DumpManager dumpManager) {
super(keyguardStatusView);
mKeyguardSliceViewController = keyguardSliceViewController;
mKeyguardClockSwitchController = keyguardClockSwitchController;
@@ -123,11 +133,13 @@
logger.getBuffer());
mInteractionJankMonitor = interactionJankMonitor;
mFeatureFlags = featureFlags;
+ mDumpManager = dumpManager;
}
@Override
public void onInit() {
mKeyguardClockSwitchController.init();
+ mDumpManager.registerDumpable(this);
}
@Override
@@ -143,6 +155,13 @@
}
/**
+ * Called in notificationPanelViewController to avoid leak
+ */
+ public void onDestroy() {
+ mDumpManager.unregisterDumpable(TAG);
+ }
+
+ /**
* Updates views on doze time tick.
*/
public void dozeTimeTick() {
@@ -365,6 +384,19 @@
// Excluding media from the transition on split-shade, as it doesn't transition
// horizontally properly.
transition.excludeTarget(R.id.status_view_media_container, true);
+
+ // Exclude smartspace viewpager and its children from the transition.
+ // - Each step of the transition causes the ViewPager to invoke resize,
+ // which invokes scrolling to the recalculated position. The scrolling
+ // actions are congested, resulting in kinky translation, and
+ // delay in settling to the final position. (http://b/281620564#comment1)
+ // - Also, the scrolling is unnecessary in the transition. We just want
+ // the viewpager to stay on the same page.
+ // - Exclude by Class type instead of resource id, since the resource id
+ // isn't available for all devices, and probably better to exclude all
+ // ViewPagers any way.
+ transition.excludeTarget(ViewPager.class, true);
+ transition.excludeChildren(ViewPager.class, true);
}
transition.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
@@ -397,6 +429,24 @@
adapter.setDuration(KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION);
adapter.addTarget(clockView);
set.addTransition(adapter);
+
+ if (splitShadeEnabled) {
+ // Exclude smartspace viewpager and its children from the transition set.
+ // - This is necessary in addition to excluding them from the
+ // ChangeBounds child transition.
+ // - Without this, the viewpager is scrolled to the new position
+ // (corresponding to its end size) before the size change is realized.
+ // Note that the size change is realized at the end of the ChangeBounds
+ // transition. With the "prescrolling", the viewpager ends up in a weird
+ // position, then recovers smoothly during the transition, and ends at
+ // the position for the current page.
+ // - Exclude by Class type instead of resource id, since the resource id
+ // isn't available for all devices, and probably better to exclude all
+ // ViewPagers any way.
+ set.excludeTarget(ViewPager.class, true);
+ set.excludeChildren(ViewPager.class, true);
+ }
+
set.addListener(mKeyguardStatusAlignmentTransitionListener);
TransitionManager.beginDelayedTransition(notifContainerParent, set);
}
@@ -408,6 +458,11 @@
constraintSet.applyTo(notifContainerParent);
}
+ @Override
+ public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+ mView.dump(pw, args);
+ }
+
@VisibleForTesting
static class SplitShadeTransitionAdapter extends Transition {
private static final String PROP_BOUNDS_LEFT = "splitShadeTransitionAdapter:boundsLeft";
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 5e84a57..6bd9df0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -164,13 +164,13 @@
import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent;
-import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.DetectionStatus;
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.FailedAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.AcquiredFaceAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus;
+import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus;
import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.WeatherData;
@@ -1471,28 +1471,32 @@
private FaceAuthenticationListener mFaceAuthenticationListener =
new FaceAuthenticationListener() {
@Override
- public void onAuthenticationStatusChanged(@NonNull AuthenticationStatus status) {
- if (status instanceof AcquiredAuthenticationStatus) {
+ public void onAuthenticationStatusChanged(
+ @NonNull FaceAuthenticationStatus status
+ ) {
+ if (status instanceof AcquiredFaceAuthenticationStatus) {
handleFaceAcquired(
- ((AcquiredAuthenticationStatus) status).getAcquiredInfo());
- } else if (status instanceof ErrorAuthenticationStatus) {
- ErrorAuthenticationStatus error = (ErrorAuthenticationStatus) status;
+ ((AcquiredFaceAuthenticationStatus) status).getAcquiredInfo());
+ } else if (status instanceof ErrorFaceAuthenticationStatus) {
+ ErrorFaceAuthenticationStatus error =
+ (ErrorFaceAuthenticationStatus) status;
handleFaceError(error.getMsgId(), error.getMsg());
- } else if (status instanceof FailedAuthenticationStatus) {
+ } else if (status instanceof FailedFaceAuthenticationStatus) {
handleFaceAuthFailed();
- } else if (status instanceof HelpAuthenticationStatus) {
- HelpAuthenticationStatus helpMsg = (HelpAuthenticationStatus) status;
+ } else if (status instanceof HelpFaceAuthenticationStatus) {
+ HelpFaceAuthenticationStatus helpMsg =
+ (HelpFaceAuthenticationStatus) status;
handleFaceHelp(helpMsg.getMsgId(), helpMsg.getMsg());
- } else if (status instanceof SuccessAuthenticationStatus) {
+ } else if (status instanceof SuccessFaceAuthenticationStatus) {
FaceManager.AuthenticationResult result =
- ((SuccessAuthenticationStatus) status).getSuccessResult();
+ ((SuccessFaceAuthenticationStatus) status).getSuccessResult();
handleFaceAuthenticated(result.getUserId(), result.isStrongBiometric());
}
}
@Override
- public void onDetectionStatusChanged(@NonNull DetectionStatus status) {
- handleFaceAuthenticated(status.getUserId(), status.isStrongBiometric());
+ public void onDetectionStatusChanged(@NonNull FaceDetectionStatus status) {
+ handleBiometricDetected(status.getUserId(), FACE, status.isStrongBiometric());
}
};
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index 71f78c3..3990b10 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -31,6 +31,7 @@
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.Assert;
import com.google.errorprone.annotations.CompileTimeConstant;
@@ -85,6 +86,7 @@
boolean keyguardFadingAway,
boolean goingToFullShade,
int oldStatusBarState) {
+ Assert.isMainThread();
PropertyAnimator.cancelAnimation(mView, AnimatableProperty.ALPHA);
boolean isOccluded = mKeyguardStateController.isOccluded();
mKeyguardViewVisibilityAnimating = false;
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 04acd0b..b01e136 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -49,7 +49,6 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.hdmi.HdmiCecSetMenuLanguageHelper;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
@@ -242,7 +241,6 @@
@Inject Lazy<LocationController> mLocationController;
@Inject Lazy<RotationLockController> mRotationLockController;
@Inject Lazy<ZenModeController> mZenModeController;
- @Inject Lazy<HdmiCecSetMenuLanguageHelper> mHdmiCecSetMenuLanguageHelper;
@Inject Lazy<HotspotController> mHotspotController;
@Inject Lazy<CastController> mCastController;
@Inject Lazy<FlashlightController> mFlashlightController;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index e5a4d1a..7ae1443 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -479,10 +479,10 @@
failureReason,
messageAfterError = modalities.asDefaultHelpMessage(applicationContext),
authenticateAfterError = modalities.hasFingerprint,
- suppressIf = { currentMessage ->
+ suppressIf = { currentMessage, history ->
modalities.hasFaceAndFingerprint &&
failedModality == BiometricModality.Face &&
- currentMessage.isError
+ (currentMessage.isError || history.faceFailed)
},
failedModality = failedModality,
)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistory.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistory.kt
new file mode 100644
index 0000000..d002bf0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistory.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.ui.viewmodel
+
+import com.android.systemui.biometrics.shared.model.BiometricModality
+
+/** Contains metadata about key events that have occurred while biometric prompt is showing. */
+interface PromptHistory {
+
+ /** If face authentication has failed at least once. */
+ val faceFailed: Boolean
+
+ /** If fingerprint authentication has failed at least once. */
+ val fingerprintFailed: Boolean
+}
+
+class PromptHistoryImpl : PromptHistory {
+ private var failures = mutableSetOf<BiometricModality>()
+
+ override val faceFailed
+ get() = failures.contains(BiometricModality.Face)
+
+ override val fingerprintFailed
+ get() = failures.contains(BiometricModality.Fingerprint)
+
+ /** Record a failure event. */
+ fun failure(modality: BiometricModality) {
+ if (modality != BiometricModality.None) {
+ failures.add(modality)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 8a2e405..dca19c5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -204,6 +204,7 @@
}
.distinctUntilChanged()
+ private val history = PromptHistoryImpl()
private var messageJob: Job? = null
/**
@@ -214,13 +215,13 @@
* is set (or via [showHelp] when not set) after the error is dismissed.
*
* The error is ignored if the user has already authenticated or if [suppressIf] is true given
- * the currently showing [PromptMessage].
+ * the currently showing [PromptMessage] and [PromptHistory].
*/
suspend fun showTemporaryError(
message: String,
messageAfterError: String,
authenticateAfterError: Boolean,
- suppressIf: (PromptMessage) -> Boolean = { false },
+ suppressIf: (PromptMessage, PromptHistory) -> Boolean = { _, _ -> false },
hapticFeedback: Boolean = true,
failedModality: BiometricModality = BiometricModality.None,
) = coroutineScope {
@@ -230,7 +231,9 @@
_canTryAgainNow.value = supportsRetry(failedModality)
- if (suppressIf(_message.value)) {
+ val suppress = suppressIf(_message.value, history)
+ history.failure(failedModality)
+ if (suppress) {
return@coroutineScope
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
index 2abdb84..e3e9b3a 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
@@ -16,10 +16,10 @@
package com.android.systemui.bouncer.domain.interactor
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.time.SystemClock
@@ -35,8 +35,8 @@
private val keyguardStateController: KeyguardStateController,
private val bouncerRepository: KeyguardBouncerRepository,
private val biometricSettingsRepository: BiometricSettingsRepository,
- private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
private val systemClock: SystemClock,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
) {
var receivedDownTouch = false
val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
@@ -78,7 +78,7 @@
biometricSettingsRepository.isFingerprintEnrolled.value &&
biometricSettingsRepository.isStrongBiometricAllowed.value &&
biometricSettingsRepository.isFingerprintEnabledByDevicePolicy.value &&
- !deviceEntryFingerprintAuthRepository.isLockedOut.value &&
+ !keyguardUpdateMonitor.isFingerprintLockedOut &&
!keyguardStateController.isUnlocked &&
!statusBarStateController.isDozing
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 8d5a2dd..32e40c9 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -20,7 +20,6 @@
import com.android.systemui.ForegroundServicesDialog;
import com.android.systemui.contrast.ContrastDialogActivity;
-import com.android.systemui.hdmi.HdmiCecSetMenuLanguageActivity;
import com.android.systemui.keyguard.WorkLockActivity;
import com.android.systemui.people.PeopleSpaceActivity;
import com.android.systemui.people.widget.LaunchConversationActivity;
@@ -28,10 +27,7 @@
import com.android.systemui.screenshot.appclips.AppClipsActivity;
import com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity;
import com.android.systemui.sensorprivacy.SensorUseStartedActivity;
-import com.android.systemui.sensorprivacy.television.TvSensorPrivacyChangedActivity;
-import com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity;
import com.android.systemui.settings.brightness.BrightnessDialog;
-import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity;
import com.android.systemui.telephony.ui.activity.SwitchToManagedProfileForCallActivity;
import com.android.systemui.tuner.TunerActivity;
import com.android.systemui.usb.UsbAccessoryUriActivity;
@@ -118,12 +114,6 @@
@ClassKey(CreateUserActivity.class)
public abstract Activity bindCreateUserActivity(CreateUserActivity activity);
- /** Inject into TvNotificationPanelActivity. */
- @Binds
- @IntoMap
- @ClassKey(TvNotificationPanelActivity.class)
- public abstract Activity bindTvNotificationPanelActivity(TvNotificationPanelActivity activity);
-
/** Inject into PeopleSpaceActivity. */
@Binds
@IntoMap
@@ -160,25 +150,7 @@
@ClassKey(SensorUseStartedActivity.class)
public abstract Activity bindSensorUseStartedActivity(SensorUseStartedActivity activity);
- /** Inject into TvUnblockSensorActivity. */
- @Binds
- @IntoMap
- @ClassKey(TvUnblockSensorActivity.class)
- public abstract Activity bindTvUnblockSensorActivity(TvUnblockSensorActivity activity);
- /** Inject into HdmiCecSetMenuLanguageActivity. */
- @Binds
- @IntoMap
- @ClassKey(HdmiCecSetMenuLanguageActivity.class)
- public abstract Activity bindHdmiCecSetMenuLanguageActivity(
- HdmiCecSetMenuLanguageActivity activity);
-
- /** Inject into TvSensorPrivacyChangedActivity. */
- @Binds
- @IntoMap
- @ClassKey(TvSensorPrivacyChangedActivity.class)
- public abstract Activity bindTvSensorPrivacyChangedActivity(
- TvSensorPrivacyChangedActivity activity);
/** Inject into SwitchToManagedProfileForCallActivity. */
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 35cf4a1..3562477 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -43,8 +43,6 @@
import com.android.systemui.screenshot.ReferenceScreenshotModule;
import com.android.systemui.settings.dagger.MultiUserUtilsModule;
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shade.ShadeControllerImpl;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyboardShortcutsModule;
@@ -150,9 +148,6 @@
@Binds
abstract DockManager bindDockManager(DockManagerImpl dockManager);
- @Binds
- abstract ShadeController provideShadeController(ShadeControllerImpl shadeController);
-
@SysUISingleton
@Provides
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index d82bf58..6fdb4ca 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -223,10 +223,10 @@
Optional<NaturalRotationUnfoldProgressProvider> getNaturalRotationUnfoldProgressProvider();
/** */
- Optional<MediaMuteAwaitConnectionCli> getMediaMuteAwaitConnectionCli();
+ MediaMuteAwaitConnectionCli getMediaMuteAwaitConnectionCli();
/** */
- Optional<NearbyMediaDevicesManager> getNearbyMediaDevicesManager();
+ NearbyMediaDevicesManager getNearbyMediaDevicesManager();
/**
* Returns {@link CoreStartable}s that should be started with the application.
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index d70c57f..dfec771 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -21,11 +21,9 @@
import androidx.annotation.Nullable;
import com.android.systemui.SystemUIInitializer;
-import com.android.systemui.tv.TvWMComponent;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.dagger.TvWMShellModule;
import com.android.wm.shell.dagger.WMShellModule;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.desktopmode.DesktopMode;
@@ -52,7 +50,7 @@
* provided by its particular device/form-factor SystemUI implementation.
*
* ie. {@link WMComponent} includes {@link WMShellModule}
- * and {@link TvWMComponent} includes {@link TvWMShellModule}
+ * and {@code TvWMComponent} includes {@link com.android.wm.shell.dagger.TvWMShellModule}
*/
@WMSingleton
@Subcomponent(modules = {WMShellModule.class})
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt
index 0e22406..f3a07fc 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt
@@ -16,15 +16,52 @@
package com.android.systemui.dreams
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.core.LogLevel
-import com.android.systemui.log.dagger.DreamLog
-import javax.inject.Inject
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.core.MessageBuffer
/** Logs dream-related stuff to a {@link LogBuffer}. */
-class DreamLogger @Inject constructor(@DreamLog private val buffer: LogBuffer) {
- /** Logs a debug message to the buffer. */
- fun d(tag: String, message: String) {
- buffer.log(tag, LogLevel.DEBUG, { str1 = message }, { message })
- }
+class DreamLogger(buffer: MessageBuffer, tag: String) : Logger(buffer, tag) {
+ fun logDreamOverlayEnabled(enabled: Boolean) =
+ d({ "Dream overlay enabled: $bool1" }) { bool1 = enabled }
+
+ fun logIgnoreAddComplication(reason: String, complication: String) =
+ d({ "Ignore adding complication, reason: $str1, complication: $str2" }) {
+ str1 = reason
+ str2 = complication
+ }
+
+ fun logIgnoreRemoveComplication(reason: String, complication: String) =
+ d({ "Ignore removing complication, reason: $str1, complication: $str2" }) {
+ str1 = reason
+ str2 = complication
+ }
+
+ fun logAddComplication(complication: String) =
+ d({ "Add dream complication: $str1" }) { str1 = complication }
+
+ fun logRemoveComplication(complication: String) =
+ d({ "Remove dream complication: $str1" }) { str1 = complication }
+
+ fun logOverlayActive(active: Boolean) = d({ "Dream overlay active: $bool1" }) { bool1 = active }
+
+ fun logLowLightActive(active: Boolean) =
+ d({ "Low light mode active: $bool1" }) { bool1 = active }
+
+ fun logHasAssistantAttention(hasAttention: Boolean) =
+ d({ "Dream overlay has Assistant attention: $bool1" }) { bool1 = hasAttention }
+
+ fun logStatusBarVisible(visible: Boolean) =
+ d({ "Dream overlay status bar visible: $bool1" }) { bool1 = visible }
+
+ fun logAvailableComplicationTypes(types: Int) =
+ d({ "Available complication types: $int1" }) { int1 = types }
+
+ fun logShouldShowComplications(showComplications: Boolean) =
+ d({ "Dream overlay should show complications: $bool1" }) { bool1 = showComplications }
+
+ fun logShowOrHideStatusBarItem(show: Boolean, type: String) =
+ d({ "${if (bool1) "Showing" else "Hiding"} dream status bar item: $int1" }) {
+ bool1 = show
+ str1 = type
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
index 484bf3d..01fb522 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -36,6 +36,9 @@
import com.android.systemui.dreams.dagger.DreamOverlayModule
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.DreamLog
import com.android.systemui.statusbar.BlurUtils
import com.android.systemui.statusbar.CrossFadeHelper
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -65,12 +68,14 @@
private val mDreamInTranslationYDistance: Int,
@Named(DreamOverlayModule.DREAM_IN_TRANSLATION_Y_DURATION)
private val mDreamInTranslationYDurationMs: Long,
- private val mLogger: DreamLogger,
+ @DreamLog logBuffer: LogBuffer,
) {
companion object {
private const val TAG = "DreamOverlayAnimationsController"
}
+ private val logger = Logger(logBuffer, TAG)
+
private var mAnimator: Animator? = null
private lateinit var view: View
@@ -179,11 +184,11 @@
doOnEnd {
mAnimator = null
mOverlayStateController.setEntryAnimationsFinished(true)
- mLogger.d(TAG, "Dream overlay entry animations finished.")
+ logger.d("Dream overlay entry animations finished.")
}
- doOnCancel { mLogger.d(TAG, "Dream overlay entry animations canceled.") }
+ doOnCancel { logger.d("Dream overlay entry animations canceled.") }
start()
- mLogger.d(TAG, "Dream overlay entry animations started.")
+ logger.d("Dream overlay entry animations started.")
}
}
@@ -242,11 +247,11 @@
doOnEnd {
mAnimator = null
mOverlayStateController.setExitAnimationsRunning(false)
- mLogger.d(TAG, "Dream overlay exit animations finished.")
+ logger.d("Dream overlay exit animations finished.")
}
- doOnCancel { mLogger.d(TAG, "Dream overlay exit animations canceled.") }
+ doOnCancel { logger.d("Dream overlay exit animations canceled.") }
start()
- mLogger.d(TAG, "Dream overlay exit animations started.")
+ logger.d("Dream overlay exit animations started.")
}
mOverlayStateController.setExitAnimationsRunning(true)
return mAnimator as AnimatorSet
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index c2421dc..c9748f9 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -28,6 +28,8 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.dagger.DreamLog;
import com.android.systemui.statusbar.policy.CallbackController;
import java.util.ArrayList;
@@ -115,10 +117,10 @@
public DreamOverlayStateController(@Main Executor executor,
@Named(DREAM_OVERLAY_ENABLED) boolean overlayEnabled,
FeatureFlags featureFlags,
- DreamLogger dreamLogger) {
+ @DreamLog LogBuffer logBuffer) {
mExecutor = executor;
mOverlayEnabled = overlayEnabled;
- mLogger = dreamLogger;
+ mLogger = new DreamLogger(logBuffer, TAG);
mFeatureFlags = featureFlags;
if (mFeatureFlags.isEnabled(Flags.ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS)) {
mSupportedTypes = Complication.COMPLICATION_TYPE_NONE
@@ -126,7 +128,7 @@
} else {
mSupportedTypes = Complication.COMPLICATION_TYPE_NONE;
}
- mLogger.d(TAG, "Dream overlay enabled: " + mOverlayEnabled);
+ mLogger.logDreamOverlayEnabled(mOverlayEnabled);
}
/**
@@ -134,14 +136,13 @@
*/
public void addComplication(Complication complication) {
if (!mOverlayEnabled) {
- mLogger.d(TAG,
- "Ignoring adding complication due to overlay disabled: " + complication);
+ mLogger.logIgnoreAddComplication("overlay disabled", complication.toString());
return;
}
mExecutor.execute(() -> {
if (mComplications.add(complication)) {
- mLogger.d(TAG, "Added dream complication: " + complication);
+ mLogger.logAddComplication(complication.toString());
mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged());
}
});
@@ -152,14 +153,13 @@
*/
public void removeComplication(Complication complication) {
if (!mOverlayEnabled) {
- mLogger.d(TAG,
- "Ignoring removing complication due to overlay disabled: " + complication);
+ mLogger.logIgnoreRemoveComplication("overlay disabled", complication.toString());
return;
}
mExecutor.execute(() -> {
if (mComplications.remove(complication)) {
- mLogger.d(TAG, "Removed dream complication: " + complication);
+ mLogger.logRemoveComplication(complication.toString());
mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged());
}
});
@@ -305,7 +305,7 @@
* @param active {@code true} if overlay is active, {@code false} otherwise.
*/
public void setOverlayActive(boolean active) {
- mLogger.d(TAG, "Dream overlay active: " + active);
+ mLogger.logOverlayActive(active);
modifyState(active ? OP_SET_STATE : OP_CLEAR_STATE, STATE_DREAM_OVERLAY_ACTIVE);
}
@@ -314,7 +314,7 @@
* @param active {@code true} if low light mode is active, {@code false} otherwise.
*/
public void setLowLightActive(boolean active) {
- mLogger.d(TAG, "Low light mode active: " + active);
+ mLogger.logLowLightActive(active);
if (isLowLightActive() && !active) {
// Notify that we're exiting low light only on the transition from active to not active.
@@ -346,7 +346,7 @@
* @param hasAttention {@code true} if has the user's attention, {@code false} otherwise.
*/
public void setHasAssistantAttention(boolean hasAttention) {
- mLogger.d(TAG, "Dream overlay has Assistant attention: " + hasAttention);
+ mLogger.logHasAssistantAttention(hasAttention);
modifyState(hasAttention ? OP_SET_STATE : OP_CLEAR_STATE, STATE_HAS_ASSISTANT_ATTENTION);
}
@@ -355,7 +355,7 @@
* @param visible {@code true} if the status bar is visible, {@code false} otherwise.
*/
public void setDreamOverlayStatusBarVisible(boolean visible) {
- mLogger.d(TAG, "Dream overlay status bar visible: " + visible);
+ mLogger.logStatusBarVisible(visible);
modifyState(
visible ? OP_SET_STATE : OP_CLEAR_STATE, STATE_DREAM_OVERLAY_STATUS_BAR_VISIBLE);
}
@@ -373,7 +373,7 @@
*/
public void setAvailableComplicationTypes(@Complication.ComplicationType int types) {
mExecutor.execute(() -> {
- mLogger.d(TAG, "Available complication types: " + types);
+ mLogger.logAvailableComplicationTypes(types);
mAvailableComplicationTypes = types;
mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged);
});
@@ -391,7 +391,7 @@
*/
public void setShouldShowComplications(boolean shouldShowComplications) {
mExecutor.execute(() -> {
- mLogger.d(TAG, "Should show complications: " + shouldShowComplications);
+ mLogger.logShouldShowComplications(shouldShowComplications);
mShouldShowComplications = shouldShowComplications;
mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged);
});
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 3a28408..a6401b6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -36,6 +36,8 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.dagger.DreamLog;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
@@ -161,7 +163,7 @@
DreamOverlayStatusBarItemsProvider statusBarItemsProvider,
DreamOverlayStateController dreamOverlayStateController,
UserTracker userTracker,
- DreamLogger dreamLogger) {
+ @DreamLog LogBuffer logBuffer) {
super(view);
mResources = resources;
mMainExecutor = mainExecutor;
@@ -177,7 +179,7 @@
mZenModeController = zenModeController;
mDreamOverlayStateController = dreamOverlayStateController;
mUserTracker = userTracker;
- mLogger = dreamLogger;
+ mLogger = new DreamLogger(logBuffer, TAG);
// Register to receive show/hide updates for the system status bar. Our custom status bar
// needs to hide when the system status bar is showing to ovoid overlapping status bars.
@@ -346,8 +348,8 @@
@Nullable String contentDescription) {
mMainExecutor.execute(() -> {
if (mIsAttached) {
- mLogger.d(TAG, (show ? "Showing" : "Hiding") + " dream status bar item: "
- + DreamOverlayStatusBarView.getLoggableStatusIconType(iconType));
+ mLogger.logShowOrHideStatusBarItem(
+ show, DreamOverlayStatusBarView.getLoggableStatusIconType(iconType));
mView.showIcon(iconType, show, contentDescription);
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 0670ec3..efa5981 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -254,7 +254,7 @@
/** Migrate the indication area to the new keyguard root view. */
// TODO(b/280067944): Tracking bug.
@JvmField
- val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area", teamfood = true)
+ val MIGRATE_INDICATION_AREA = releasedFlag(236, "migrate_indication_area")
/**
* Migrate the bottom area to the new keyguard root view.
@@ -294,6 +294,11 @@
@JvmField
val MIGRATE_NSSL = unreleasedFlag(242, "migrate_nssl")
+ /** Migrate the status view from the notification panel to keyguard root view. */
+ // TODO(b/291767565): Tracking bug.
+ @JvmField
+ val MIGRATE_KEYGUARD_STATUS_VIEW = unreleasedFlag(243, "migrate_keyguard_status_view")
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -362,16 +367,6 @@
// 600- status bar
- // TODO(b/256614753): Tracking Bug
- val NEW_STATUS_BAR_MOBILE_ICONS = releasedFlag(606, "new_status_bar_mobile_icons")
-
- // TODO(b/256614751): Tracking Bug
- val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND =
- unreleasedFlag(608, "new_status_bar_mobile_icons_backend", teamfood = true)
-
- // TODO(b/260881289): Tracking Bug
- val NEW_STATUS_BAR_ICONS_DEBUG_COLORING =
- unreleasedFlag(611, "new_status_bar_icons_debug_coloring")
// TODO(b/265892345): Tracking Bug
val PLUG_IN_STATUS_BAR_CHIP = releasedFlag(265892345, "plug_in_status_bar_chip")
@@ -411,12 +406,6 @@
// TODO(b/254512502): Tracking Bug
val MEDIA_SESSION_ACTIONS = unreleasedFlag(901, "media_session_actions")
- // TODO(b/254512726): Tracking Bug
- val MEDIA_NEARBY_DEVICES = releasedFlag(903, "media_nearby_devices")
-
- // TODO(b/254512695): Tracking Bug
- val MEDIA_MUTE_AWAIT = releasedFlag(904, "media_mute_await")
-
// TODO(b/254512654): Tracking Bug
@JvmField val DREAM_MEDIA_COMPLICATION = unreleasedFlag(905, "dream_media_complication")
@@ -432,9 +421,6 @@
// TODO(b/263272731): Tracking Bug
val MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE = releasedFlag(910, "media_ttt_receiver_success_ripple")
- // TODO(b/265813373): Tracking Bug
- val MEDIA_TAP_TO_TRANSFER_DISMISS_GESTURE = releasedFlag(912, "media_ttt_dismiss_gesture")
-
// TODO(b/266157412): Tracking Bug
val MEDIA_RETAIN_SESSIONS = unreleasedFlag(913, "media_retain_sessions")
@@ -780,7 +766,8 @@
// TODO(b/285174336): Tracking Bug
@JvmField
- val USE_REPOS_FOR_BOUNCER_SHOWING = unreleasedFlag(2900, "use_repos_for_bouncer_showing")
+ val USE_REPOS_FOR_BOUNCER_SHOWING =
+ unreleasedFlag(2900, "use_repos_for_bouncer_showing", teamfood = true)
// 3100 - Haptic interactions
diff --git a/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt b/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt
new file mode 100644
index 0000000..eaecda5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.flags
+
+import android.util.Log
+import com.android.systemui.Dependency
+
+/**
+ * This class promotes best practices for flag guarding System UI view refactors.
+ * * [isEnabled] allows changing an implementation.
+ * * [assertDisabled] allows authors to flag code as being "dead" when the flag gets enabled and
+ * ensure that it is not being invoked accidentally in the post-flag refactor.
+ * * [expectEnabled] allows authors to guard new code with a "safe" alternative when invoked on
+ * flag-disabled builds, but with a check that should crash eng builds or tests when the
+ * expectation is violated.
+ *
+ * The constructors prefer that you provide a [FeatureFlags] instance, but does not require it,
+ * falling back to [Dependency.get]. This fallback should ONLY be used to flag-guard code changes
+ * inside views where injecting flag values after initialization can be error-prone.
+ */
+class ViewRefactorFlag
+private constructor(
+ private val injectedFlags: FeatureFlags?,
+ private val flag: BooleanFlag,
+ private val readFlagValue: (FeatureFlags) -> Boolean
+) {
+ @JvmOverloads
+ constructor(
+ flags: FeatureFlags? = null,
+ flag: UnreleasedFlag
+ ) : this(flags, flag, { it.isEnabled(flag) })
+
+ @JvmOverloads
+ constructor(
+ flags: FeatureFlags? = null,
+ flag: ReleasedFlag
+ ) : this(flags, flag, { it.isEnabled(flag) })
+
+ /** Whether the flag is enabled. Called to switch between an old behavior and a new behavior. */
+ val isEnabled by lazy {
+ @Suppress("DEPRECATION")
+ val featureFlags = injectedFlags ?: Dependency.get(FeatureFlags::class.java)
+ readFlagValue(featureFlags)
+ }
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ *
+ * Example usage:
+ * ```
+ * public void setController(NotificationShelfController notificationShelfController) {
+ * mShelfRefactor.assertDisabled();
+ * mController = notificationShelfController;
+ * }
+ * ````
+ */
+ fun assertDisabled() = check(!isEnabled) { "Code path not supported when $flag is enabled." }
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ *
+ * Example usage:
+ * ```
+ * public void setShelfIcons(NotificationIconContainer icons) {
+ * if (mShelfRefactor.expectEnabled()) {
+ * mShelfIcons = icons;
+ * }
+ * }
+ * ```
+ */
+ fun expectEnabled(): Boolean {
+ if (!isEnabled) {
+ val message = "Code path not supported when $flag is disabled."
+ Log.wtf(TAG, message, Exception(message))
+ }
+ return isEnabled
+ }
+
+ private companion object {
+ private const val TAG = "ViewRefactorFlag"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageActivity.java b/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageActivity.java
deleted file mode 100644
index b304c3c..0000000
--- a/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageActivity.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.hdmi;
-
-import android.hardware.hdmi.HdmiControlManager;
-import android.os.Bundle;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-import com.android.systemui.tv.TvBottomSheetActivity;
-
-import javax.inject.Inject;
-
-/**
- * Confirmation dialog shown when Set Menu Language CEC message was received.
- */
-public class HdmiCecSetMenuLanguageActivity extends TvBottomSheetActivity
- implements View.OnClickListener {
- private static final String TAG = HdmiCecSetMenuLanguageActivity.class.getSimpleName();
-
- private final HdmiCecSetMenuLanguageHelper mHdmiCecSetMenuLanguageHelper;
-
- @Inject
- public HdmiCecSetMenuLanguageActivity(
- HdmiCecSetMenuLanguageHelper hdmiCecSetMenuLanguageHelper) {
- mHdmiCecSetMenuLanguageHelper = hdmiCecSetMenuLanguageHelper;
- }
-
- @Override
- public final void onCreate(Bundle b) {
- super.onCreate(b);
- getWindow().addPrivateFlags(
- WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
- String languageTag = getIntent().getStringExtra(HdmiControlManager.EXTRA_LOCALE);
- mHdmiCecSetMenuLanguageHelper.setLocale(languageTag);
- if (mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()) {
- finish();
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
- CharSequence title = getString(R.string.hdmi_cec_set_menu_language_title,
- mHdmiCecSetMenuLanguageHelper.getLocale().getDisplayLanguage());
- CharSequence text = getString(R.string.hdmi_cec_set_menu_language_description);
- initUI(title, text);
- }
-
- @Override
- public void onClick(View v) {
- if (v.getId() == R.id.bottom_sheet_positive_button) {
- mHdmiCecSetMenuLanguageHelper.acceptLocale();
- } else {
- mHdmiCecSetMenuLanguageHelper.declineLocale();
- }
- finish();
- }
-
- void initUI(CharSequence title, CharSequence text) {
- TextView titleTextView = findViewById(R.id.bottom_sheet_title);
- TextView contentTextView = findViewById(R.id.bottom_sheet_body);
- ImageView icon = findViewById(R.id.bottom_sheet_icon);
- ImageView secondIcon = findViewById(R.id.bottom_sheet_second_icon);
- Button okButton = findViewById(R.id.bottom_sheet_positive_button);
- Button cancelButton = findViewById(R.id.bottom_sheet_negative_button);
-
- titleTextView.setText(title);
- contentTextView.setText(text);
- icon.setImageResource(com.android.internal.R.drawable.ic_settings_language);
- secondIcon.setVisibility(View.GONE);
-
- okButton.setText(R.string.hdmi_cec_set_menu_language_accept);
- okButton.setOnClickListener(this);
-
- cancelButton.setText(R.string.hdmi_cec_set_menu_language_decline);
- cancelButton.setOnClickListener(this);
- cancelButton.requestFocus();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelper.java b/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelper.java
deleted file mode 100644
index 1f61647..0000000
--- a/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelper.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.hdmi;
-
-import android.os.UserHandle;
-import android.provider.Settings;
-
-import com.android.internal.app.LocalePicker;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.util.settings.SecureSettings;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Locale;
-import java.util.concurrent.Executor;
-
-import javax.inject.Inject;
-
-/**
- * Helper class to separate model and view for system language change initiated by HDMI CEC.
- */
-@SysUISingleton
-public class HdmiCecSetMenuLanguageHelper {
- private static final String TAG = HdmiCecSetMenuLanguageHelper.class.getSimpleName();
- private static final String SEPARATOR = ",";
-
- private final Executor mBackgroundExecutor;
- private final SecureSettings mSecureSettings;
-
- private Locale mLocale;
- private HashSet<String> mDenylist;
-
- @Inject
- public HdmiCecSetMenuLanguageHelper(@Background Executor executor,
- SecureSettings secureSettings) {
- mBackgroundExecutor = executor;
- mSecureSettings = secureSettings;
- String denylist = mSecureSettings.getStringForUser(
- Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, UserHandle.USER_CURRENT);
- mDenylist = new HashSet<>(denylist == null
- ? Collections.EMPTY_SET
- : Arrays.asList(denylist.split(SEPARATOR)));
- }
-
- /**
- * Set internal locale based on given language tag.
- */
- public void setLocale(String languageTag) {
- mLocale = Locale.forLanguageTag(languageTag);
- }
-
- /**
- * Returns the locale from {@code <Set Menu Language>} CEC message.
- */
- public Locale getLocale() {
- return mLocale;
- }
-
- /**
- * Returns whether the locale from {@code <Set Menu Language>} CEC message was already
- * denylisted.
- */
- public boolean isLocaleDenylisted() {
- return mDenylist.contains(mLocale.toLanguageTag());
- }
-
- /**
- * Accepts the new locale and updates system language.
- */
- public void acceptLocale() {
- mBackgroundExecutor.execute(() -> LocalePicker.updateLocale(mLocale));
- }
-
- /**
- * Declines the locale and puts it on the denylist.
- */
- public void declineLocale() {
- mDenylist.add(mLocale.toLanguageTag());
- mSecureSettings.putStringForUser(Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST,
- String.join(SEPARATOR, mDenylist), UserHandle.USER_CURRENT);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 3646144..489d2ab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -171,7 +171,6 @@
* for the canned animation (if applicable) so interested parties can sync with it. If no
* canned animation is playing, these are both 0.
*/
- @JvmDefault
fun onUnlockAnimationStarted(
playingCannedAnimation: Boolean,
isWakeAndUnlockNotFromDream: Boolean,
@@ -184,7 +183,6 @@
* The keyguard is no longer visible in this state and the app/launcher behind the keyguard
* is now completely visible.
*/
- @JvmDefault
fun onUnlockAnimationFinished() {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 23f6fa6..03a270e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -25,19 +25,24 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
+import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManager
import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManagerCommandListener
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
+import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
+import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.ExperimentalCoroutinesApi
/** Binds keyguard views on startup, and also exposes methods to allow rebinding if views change */
+@ExperimentalCoroutinesApi
@SysUISingleton
class KeyguardViewConfigurator
@Inject
@@ -51,11 +56,14 @@
private val indicationController: KeyguardIndicationController,
private val keyguardLayoutManager: KeyguardLayoutManager,
private val keyguardLayoutManagerCommandListener: KeyguardLayoutManagerCommandListener,
+ private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
+ private val chipbarCoordinator: ChipbarCoordinator,
) : CoreStartable {
private var indicationAreaHandle: DisposableHandle? = null
override fun start() {
+ bindKeyguardRootView()
val notificationPanel =
notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
bindIndicationArea(notificationPanel)
@@ -116,4 +124,13 @@
}
}
}
+
+ private fun bindKeyguardRootView() {
+ KeyguardRootViewBinder.bind(
+ keyguardRootView,
+ featureFlags,
+ occludingAppDeviceEntryMessageViewModel,
+ chipbarCoordinator,
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 9a32e94..e0834bb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -163,9 +163,11 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.settings.SystemSettings;
import com.android.systemui.util.time.SystemClock;
+import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
import com.android.wm.shell.keyguard.KeyguardTransitions;
import dagger.Lazy;
@@ -290,6 +292,8 @@
public static final String SYS_BOOT_REASON_PROP = "sys.boot.reason.last";
public static final String REBOOT_MAINLINE_UPDATE = "reboot,mainline_update";
private final DreamOverlayStateController mDreamOverlayStateController;
+ private final JavaAdapter mJavaAdapter;
+ private final WallpaperRepository mWallpaperRepository;
/** The stream type that the lock sounds are tied to. */
private int mUiSoundsStreamType;
@@ -1323,6 +1327,8 @@
KeyguardTransitions keyguardTransitions,
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
+ JavaAdapter javaAdapter,
+ WallpaperRepository wallpaperRepository,
Lazy<ShadeController> shadeControllerLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy,
Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
@@ -1384,6 +1390,8 @@
mScreenOffAnimationController = screenOffAnimationController;
mInteractionJankMonitor = interactionJankMonitor;
mDreamOverlayStateController = dreamOverlayStateController;
+ mJavaAdapter = javaAdapter;
+ mWallpaperRepository = wallpaperRepository;
mActivityLaunchAnimator = activityLaunchAnimator;
mScrimControllerLazy = scrimControllerLazy;
@@ -1487,6 +1495,10 @@
com.android.internal.R.anim.lock_screen_behind_enter);
mWorkLockController = new WorkLockActivityController(mContext, mUserTracker);
+
+ mJavaAdapter.alwaysCollectFlow(
+ mWallpaperRepository.getWallpaperSupportsAmbientMode(),
+ this::setWallpaperSupportsAmbientMode);
}
// TODO(b/273443374) remove, temporary util to get a feature flag
@@ -2970,6 +2982,7 @@
}
private void onKeyguardExitFinished() {
+ if (DEBUG) Log.d(TAG, "onKeyguardExitFinished()");
// only play "unlock" noises if not on a call (since the incall UI
// disables the keyguard)
if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
@@ -3191,13 +3204,14 @@
flags |= StatusBarManager.DISABLE_RECENT;
}
- if (mPowerGestureIntercepted) {
+ if (mPowerGestureIntercepted && mOccluded && isSecure()) {
flags |= StatusBarManager.DISABLE_RECENT;
}
if (DEBUG) {
Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mOccluded=" + mOccluded
+ " isSecure=" + isSecure() + " force=" + forceHideHomeRecentsButtons
+ + " mPowerGestureIntercepted=" + mPowerGestureIntercepted
+ " --> flags=0x" + Integer.toHexString(flags));
}
@@ -3425,6 +3439,7 @@
pw.print(" mPendingLock: "); pw.println(mPendingLock);
pw.print(" wakeAndUnlocking: "); pw.println(mWakeAndUnlocking);
pw.print(" mPendingPinLock: "); pw.println(mPendingPinLock);
+ pw.print(" mPowerGestureIntercepted: "); pw.println(mPowerGestureIntercepted);
}
/**
@@ -3464,7 +3479,7 @@
* In case it does support it, we have to fade in the incoming app, otherwise we'll reveal it
* with the light reveal scrim.
*/
- public void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {
+ private void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {
mWallpaperSupportsAmbientMode = supportsAmbientMode;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 1487408..a5ac7c7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -67,9 +67,11 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.settings.SystemSettings;
import com.android.systemui.util.time.SystemClock;
+import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
import com.android.wm.shell.keyguard.KeyguardTransitions;
import dagger.Lazy;
@@ -131,6 +133,8 @@
KeyguardTransitions keyguardTransitions,
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
+ JavaAdapter javaAdapter,
+ WallpaperRepository wallpaperRepository,
Lazy<ShadeController> shadeController,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
@@ -172,6 +176,8 @@
keyguardTransitions,
interactionJankMonitor,
dreamOverlayStateController,
+ javaAdapter,
+ wallpaperRepository,
shadeController,
notificationShadeWindowController,
activityLaunchAnimator,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index 0b6c7c4..ff3e77c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -325,6 +325,9 @@
private class StrongAuthTracker(private val userRepository: UserRepository, context: Context?) :
LockPatternUtils.StrongAuthTracker(context) {
+ private val selectedUserId =
+ userRepository.selectedUserInfo.map { it.id }.distinctUntilChanged()
+
// Backing field for onStrongAuthRequiredChanged
private val _authFlags =
MutableStateFlow(AuthenticationFlags(currentUserId, getStrongAuthForUser(currentUserId)))
@@ -336,15 +339,12 @@
)
val currentUserAuthFlags: Flow<AuthenticationFlags> =
- userRepository.selectedUserInfo
- .map { it.id }
- .distinctUntilChanged()
- .flatMapLatest { userId ->
- _authFlags
- .map { AuthenticationFlags(userId, getStrongAuthForUser(userId)) }
- .onEach { Log.d(TAG, "currentUser authFlags changed, new value: $it") }
- .onStart { emit(AuthenticationFlags(userId, getStrongAuthForUser(userId))) }
- }
+ selectedUserId.flatMapLatest { userId ->
+ _authFlags
+ .map { AuthenticationFlags(userId, getStrongAuthForUser(userId)) }
+ .onEach { Log.d(TAG, "currentUser authFlags changed, new value: $it") }
+ .onStart { emit(AuthenticationFlags(userId, getStrongAuthForUser(userId))) }
+ }
/** isStrongBiometricAllowed for the current user. */
val isStrongBiometricAllowed: Flow<Boolean> =
@@ -352,16 +352,17 @@
/** isNonStrongBiometricAllowed for the current user. */
val isNonStrongBiometricAllowed: Flow<Boolean> =
- userRepository.selectedUserInfo
- .map { it.id }
- .distinctUntilChanged()
+ selectedUserId
.flatMapLatest { userId ->
_nonStrongBiometricAllowed
.filter { it.first == userId }
.map { it.second }
- .onEach { Log.d(TAG, "isNonStrongBiometricAllowed changed for current user") }
+ .onEach {
+ Log.d(TAG, "isNonStrongBiometricAllowed changed for current user: $it")
+ }
.onStart { emit(isNonStrongBiometricAllowedAfterIdleTimeout(userId)) }
}
+ .and(isStrongBiometricAllowed)
private val currentUserId
get() = userRepository.getSelectedUserInfo().id
@@ -387,3 +388,6 @@
private fun DevicePolicyManager.isNotActive(userId: Int, policy: Int): Boolean =
(getKeyguardDisabledFeatures(null, userId) and policy) == 0
+
+private fun Flow<Boolean>.and(anotherFlow: Flow<Boolean>): Flow<Boolean> =
+ this.combine(anotherFlow) { a, b -> a && b }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index a3d1abe..d1f011e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -38,13 +38,13 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FailedAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.AcquiredFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
+import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.log.SessionTracker
@@ -88,10 +88,10 @@
val canRunFaceAuth: StateFlow<Boolean>
/** Provide the current status of face authentication. */
- val authenticationStatus: Flow<AuthenticationStatus>
+ val authenticationStatus: Flow<FaceAuthenticationStatus>
/** Provide the current status of face detection. */
- val detectionStatus: Flow<DetectionStatus>
+ val detectionStatus: Flow<FaceDetectionStatus>
/** Current state of whether face authentication is locked out or not. */
val isLockedOut: StateFlow<Boolean>
@@ -151,13 +151,13 @@
private var cancelNotReceivedHandlerJob: Job? = null
private var halErrorRetryJob: Job? = null
- private val _authenticationStatus: MutableStateFlow<AuthenticationStatus?> =
+ private val _authenticationStatus: MutableStateFlow<FaceAuthenticationStatus?> =
MutableStateFlow(null)
- override val authenticationStatus: Flow<AuthenticationStatus>
+ override val authenticationStatus: Flow<FaceAuthenticationStatus>
get() = _authenticationStatus.filterNotNull()
- private val _detectionStatus = MutableStateFlow<DetectionStatus?>(null)
- override val detectionStatus: Flow<DetectionStatus>
+ private val _detectionStatus = MutableStateFlow<FaceDetectionStatus?>(null)
+ override val detectionStatus: Flow<FaceDetectionStatus>
get() = _detectionStatus.filterNotNull()
private val _isLockedOut = MutableStateFlow(false)
@@ -396,18 +396,18 @@
private val faceAuthCallback =
object : FaceManager.AuthenticationCallback() {
override fun onAuthenticationFailed() {
- _authenticationStatus.value = FailedAuthenticationStatus
+ _authenticationStatus.value = FailedFaceAuthenticationStatus
_isAuthenticated.value = false
faceAuthLogger.authenticationFailed()
onFaceAuthRequestCompleted()
}
override fun onAuthenticationAcquired(acquireInfo: Int) {
- _authenticationStatus.value = AcquiredAuthenticationStatus(acquireInfo)
+ _authenticationStatus.value = AcquiredFaceAuthenticationStatus(acquireInfo)
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
- val errorStatus = ErrorAuthenticationStatus(errorCode, errString.toString())
+ val errorStatus = ErrorFaceAuthenticationStatus(errorCode, errString.toString())
if (errorStatus.isLockoutError()) {
_isLockedOut.value = true
}
@@ -433,11 +433,11 @@
if (faceAcquiredInfoIgnoreList.contains(code)) {
return
}
- _authenticationStatus.value = HelpAuthenticationStatus(code, helpStr.toString())
+ _authenticationStatus.value = HelpFaceAuthenticationStatus(code, helpStr.toString())
}
override fun onAuthenticationSucceeded(result: FaceManager.AuthenticationResult) {
- _authenticationStatus.value = SuccessAuthenticationStatus(result)
+ _authenticationStatus.value = SuccessFaceAuthenticationStatus(result)
_isAuthenticated.value = true
faceAuthLogger.faceAuthSuccess(result)
onFaceAuthRequestCompleted()
@@ -482,7 +482,7 @@
private val detectionCallback =
FaceManager.FaceDetectionCallback { sensorId, userId, isStrong ->
faceAuthLogger.faceDetected()
- _detectionStatus.value = DetectionStatus(sensorId, userId, isStrong)
+ _detectionStatus.value = FaceDetectionStatus(sensorId, userId, isStrong)
}
private var cancellationInProgress = false
@@ -545,11 +545,11 @@
faceAuthLogger.detectionNotSupported(faceManager, faceManager?.sensorPropertiesInternal)
return
}
- if (_isAuthRunning.value || detectCancellationSignal != null) {
+ if (_isAuthRunning.value) {
faceAuthLogger.skippingDetection(_isAuthRunning.value, detectCancellationSignal != null)
return
}
-
+ detectCancellationSignal?.cancel()
detectCancellationSignal = CancellationSignal()
withContext(mainDispatcher) {
// We always want to invoke face detect in the main thread.
@@ -574,6 +574,7 @@
if (authCancellationSignal == null) return
authCancellationSignal?.cancel()
+ cancelNotReceivedHandlerJob?.cancel()
cancelNotReceivedHandlerJob =
applicationScope.launch {
delay(DEFAULT_CANCEL_SIGNAL_TIMEOUT)
@@ -583,6 +584,7 @@
cancellationInProgress,
faceAuthRequestedWhileCancellation
)
+ _authenticationStatus.value = ErrorFaceAuthenticationStatus.cancelNotReceivedError()
onFaceAuthRequestCompleted()
}
cancellationInProgress = true
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index 52234b3..9bec300 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -21,27 +21,29 @@
import android.hardware.biometrics.BiometricSourceType
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Dumpable
import com.android.systemui.biometrics.AuthController
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dump.DumpManager
-import java.io.PrintWriter
+import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
/** Encapsulates state about device entry fingerprint auth mechanism. */
interface DeviceEntryFingerprintAuthRepository {
/** Whether the device entry fingerprint auth is locked out. */
- val isLockedOut: StateFlow<Boolean>
+ val isLockedOut: Flow<Boolean>
/**
* Whether the fingerprint sensor is currently listening, this doesn't mean that the user is
@@ -53,6 +55,9 @@
* Fingerprint sensor type present on the device, null if fingerprint sensor is not available.
*/
val availableFpSensorType: Flow<BiometricType?>
+
+ /** Provide the current status of fingerprint authentication. */
+ val authenticationStatus: Flow<FingerprintAuthenticationStatus>
}
/**
@@ -69,16 +74,7 @@
val authController: AuthController,
val keyguardUpdateMonitor: KeyguardUpdateMonitor,
@Application scope: CoroutineScope,
- dumpManager: DumpManager,
-) : DeviceEntryFingerprintAuthRepository, Dumpable {
-
- init {
- dumpManager.registerDumpable(this)
- }
-
- override fun dump(pw: PrintWriter, args: Array<String?>) {
- pw.println("isLockedOut=${isLockedOut.value}")
- }
+) : DeviceEntryFingerprintAuthRepository {
override val availableFpSensorType: Flow<BiometricType?>
get() {
@@ -114,7 +110,7 @@
else if (authController.isRearFpsSupported) BiometricType.REAR_FINGERPRINT else null
}
- override val isLockedOut: StateFlow<Boolean> =
+ override val isLockedOut: Flow<Boolean> =
conflatedCallbackFlow {
val sendLockoutUpdate =
fun() {
@@ -138,7 +134,7 @@
sendLockoutUpdate()
awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
}
- .stateIn(scope, started = SharingStarted.Eagerly, initialValue = false)
+ .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
override val isRunning: Flow<Boolean>
get() = conflatedCallbackFlow {
@@ -166,6 +162,93 @@
awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
}
+ override val authenticationStatus: Flow<FingerprintAuthenticationStatus>
+ get() = conflatedCallbackFlow {
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onBiometricAuthenticated(
+ userId: Int,
+ biometricSourceType: BiometricSourceType,
+ isStrongBiometric: Boolean,
+ ) {
+
+ sendUpdateIfFingerprint(
+ biometricSourceType,
+ SuccessFingerprintAuthenticationStatus(
+ userId,
+ isStrongBiometric,
+ ),
+ )
+ }
+
+ override fun onBiometricError(
+ msgId: Int,
+ errString: String?,
+ biometricSourceType: BiometricSourceType,
+ ) {
+ sendUpdateIfFingerprint(
+ biometricSourceType,
+ ErrorFingerprintAuthenticationStatus(
+ msgId,
+ errString,
+ ),
+ )
+ }
+
+ override fun onBiometricHelp(
+ msgId: Int,
+ helpString: String?,
+ biometricSourceType: BiometricSourceType,
+ ) {
+ sendUpdateIfFingerprint(
+ biometricSourceType,
+ HelpFingerprintAuthenticationStatus(
+ msgId,
+ helpString,
+ ),
+ )
+ }
+
+ override fun onBiometricAuthFailed(
+ biometricSourceType: BiometricSourceType,
+ ) {
+ sendUpdateIfFingerprint(
+ biometricSourceType,
+ FailFingerprintAuthenticationStatus,
+ )
+ }
+
+ override fun onBiometricAcquired(
+ biometricSourceType: BiometricSourceType,
+ acquireInfo: Int,
+ ) {
+ sendUpdateIfFingerprint(
+ biometricSourceType,
+ AcquiredFingerprintAuthenticationStatus(
+ acquireInfo,
+ ),
+ )
+ }
+
+ private fun sendUpdateIfFingerprint(
+ biometricSourceType: BiometricSourceType,
+ authenticationStatus: FingerprintAuthenticationStatus
+ ) {
+ if (biometricSourceType != BiometricSourceType.FINGERPRINT) {
+ return
+ }
+
+ trySendWithFailureLogging(
+ authenticationStatus,
+ TAG,
+ "new fingerprint authentication status"
+ )
+ }
+ }
+ keyguardUpdateMonitor.registerCallback(callback)
+ awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+ }
+
companion object {
const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 42bee4a..7475c42 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -31,11 +31,13 @@
import com.android.systemui.doze.DozeTransitionCallback
import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.dreams.DreamOverlayCallbackController
+import com.android.systemui.keyguard.ScreenLifecycle
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.ScreenModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -49,9 +51,11 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
@@ -148,6 +152,9 @@
/** Observable for device wake/sleep state */
val wakefulness: StateFlow<WakefulnessModel>
+ /** Observable for device screen state */
+ val screenModel: StateFlow<ScreenModel>
+
/** Observable for biometric unlock modes */
val biometricUnlockState: Flow<BiometricUnlockModel>
@@ -163,6 +170,9 @@
/** Whether quick settings or quick-quick settings is visible. */
val isQuickSettingsVisible: Flow<Boolean>
+ /** Receive an event for doze time tick */
+ val dozeTimeTick: Flow<Unit>
+
/**
* Returns `true` if the keyguard is showing; `false` otherwise.
*
@@ -204,6 +214,8 @@
fun setIsDozing(isDozing: Boolean)
fun setIsActiveDreamLockscreenHosted(isLockscreenHosted: Boolean)
+
+ fun dozeTimeTick()
}
/** Encapsulates application state for the keyguard. */
@@ -213,6 +225,7 @@
constructor(
statusBarStateController: StatusBarStateController,
wakefulnessLifecycle: WakefulnessLifecycle,
+ screenLifecycle: ScreenLifecycle,
biometricUnlockController: BiometricUnlockController,
private val keyguardStateController: KeyguardStateController,
private val keyguardBypassController: KeyguardBypassController,
@@ -370,6 +383,13 @@
_isDozing.value = isDozing
}
+ private val _dozeTimeTick = MutableSharedFlow<Unit>()
+ override val dozeTimeTick = _dozeTimeTick.asSharedFlow()
+
+ override fun dozeTimeTick() {
+ _dozeTimeTick.tryEmit(Unit)
+ }
+
private val _lastDozeTapToWakePosition = MutableStateFlow<Point?>(null)
override val lastDozeTapToWakePosition = _lastDozeTapToWakePosition.asStateFlow()
@@ -559,6 +579,42 @@
initialValue = WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle),
)
+ override val screenModel: StateFlow<ScreenModel> =
+ conflatedCallbackFlow {
+ val observer =
+ object : ScreenLifecycle.Observer {
+ override fun onScreenTurningOn() {
+ dispatchNewState()
+ }
+ override fun onScreenTurnedOn() {
+ dispatchNewState()
+ }
+ override fun onScreenTurningOff() {
+ dispatchNewState()
+ }
+ override fun onScreenTurnedOff() {
+ dispatchNewState()
+ }
+
+ private fun dispatchNewState() {
+ trySendWithFailureLogging(
+ ScreenModel.fromScreenLifecycle(screenLifecycle),
+ TAG,
+ "updated screen state",
+ )
+ }
+ }
+
+ screenLifecycle.addObserver(observer)
+ awaitClose { screenLifecycle.removeObserver(observer) }
+ }
+ .stateIn(
+ scope,
+ // Use Eagerly so that we're always listening and never miss an event.
+ SharingStarted.Eagerly,
+ initialValue = ScreenModel.fromScreenLifecycle(screenLifecycle),
+ )
+
override val fingerprintSensorLocation: Flow<Point?> = conflatedCallbackFlow {
fun sendFpLocation() {
trySendWithFailureLogging(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
index abe59b7..27e3a74 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
@@ -18,8 +18,8 @@
import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -40,10 +40,10 @@
private val _canRunFaceAuth = MutableStateFlow(false)
override val canRunFaceAuth: StateFlow<Boolean> = _canRunFaceAuth
- override val authenticationStatus: Flow<AuthenticationStatus>
+ override val authenticationStatus: Flow<FaceAuthenticationStatus>
get() = emptyFlow()
- override val detectionStatus: Flow<DetectionStatus>
+ override val detectionStatus: Flow<FaceDetectionStatus>
get() = emptyFlow()
private val _isLockedOut = MutableStateFlow(false)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractor.kt
new file mode 100644
index 0000000..c849b84
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractor.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.content.res.Resources
+import android.hardware.biometrics.BiometricSourceType
+import android.hardware.biometrics.BiometricSourceType.FINGERPRINT
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED
+import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.util.IndicationHelper
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNot
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+
+/**
+ * BiometricMessage business logic. Filters biometric error/acquired/fail/success events for
+ * authentication events that should never surface a message to the user at the current device
+ * state.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class BiometricMessageInteractor
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
+ private val fingerprintPropertyRepository: FingerprintPropertyRepository,
+ private val indicationHelper: IndicationHelper,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+) {
+ val fingerprintErrorMessage: Flow<BiometricMessage> =
+ fingerprintAuthRepository.authenticationStatus
+ .filter {
+ it is ErrorFingerprintAuthenticationStatus &&
+ !indicationHelper.shouldSuppressErrorMsg(FINGERPRINT, it.msgId)
+ }
+ .map {
+ val errorStatus = it as ErrorFingerprintAuthenticationStatus
+ BiometricMessage(
+ FINGERPRINT,
+ BiometricMessageType.ERROR,
+ errorStatus.msgId,
+ errorStatus.msg,
+ )
+ }
+
+ val fingerprintHelpMessage: Flow<BiometricMessage> =
+ fingerprintAuthRepository.authenticationStatus
+ .filter { it is HelpFingerprintAuthenticationStatus }
+ .filterNot { isPrimaryAuthRequired() }
+ .map {
+ val helpStatus = it as HelpFingerprintAuthenticationStatus
+ BiometricMessage(
+ FINGERPRINT,
+ BiometricMessageType.HELP,
+ helpStatus.msgId,
+ helpStatus.msg,
+ )
+ }
+
+ val fingerprintFailMessage: Flow<BiometricMessage> =
+ isUdfps().flatMapLatest { isUdfps ->
+ fingerprintAuthRepository.authenticationStatus
+ .filter { it is FailFingerprintAuthenticationStatus }
+ .filterNot { isPrimaryAuthRequired() }
+ .map {
+ BiometricMessage(
+ FINGERPRINT,
+ BiometricMessageType.FAIL,
+ BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+ if (isUdfps) {
+ resources.getString(
+ com.android.internal.R.string.fingerprint_udfps_error_not_match
+ )
+ } else {
+ resources.getString(
+ com.android.internal.R.string.fingerprint_error_not_match
+ )
+ },
+ )
+ }
+ }
+
+ private fun isUdfps() =
+ fingerprintPropertyRepository.sensorType.map {
+ it == FingerprintSensorType.UDFPS_OPTICAL ||
+ it == FingerprintSensorType.UDFPS_ULTRASONIC
+ }
+
+ private fun isPrimaryAuthRequired(): Boolean {
+ // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
+ // as long as primary auth, i.e. PIN/pattern/password, is required), so it's ok to
+ // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
+ // check of whether non-strong biometric is allowed since strong biometrics can still be
+ // used.
+ return !keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
+ }
+}
+
+data class BiometricMessage(
+ val source: BiometricSourceType,
+ val type: BiometricMessageType,
+ val id: Int,
+ val message: String?,
+)
+
+enum class BiometricMessageType {
+ HELP,
+ ERROR,
+ FAIL,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
index 2efcd0c..0c898be 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
@@ -35,4 +35,8 @@
fun setLastTapToWakePosition(position: Point) {
keyguardRepository.setLastDozeTapToWakePosition(position)
}
+
+ fun dozeTimeTick() {
+ keyguardRepository.dozeTimeTick()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
index 74ef7a5..141b130 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
@@ -16,8 +16,8 @@
package com.android.systemui.keyguard.domain.interactor
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
import kotlinx.coroutines.flow.Flow
/**
@@ -27,10 +27,10 @@
interface KeyguardFaceAuthInteractor {
/** Current authentication status */
- val authenticationStatus: Flow<AuthenticationStatus>
+ val authenticationStatus: Flow<FaceAuthenticationStatus>
/** Current detection status */
- val detectionStatus: Flow<DetectionStatus>
+ val detectionStatus: Flow<FaceDetectionStatus>
/** Can face auth be run right now */
fun canFaceAuthRun(): Boolean
@@ -72,8 +72,8 @@
*/
interface FaceAuthenticationListener {
/** Receive face authentication status updates */
- fun onAuthenticationStatusChanged(status: AuthenticationStatus)
+ fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus)
/** Receive status updates whenever face detection runs */
- fun onDetectionStatusChanged(status: DetectionStatus)
+ fun onDetectionStatusChanged(status: FaceDetectionStatus)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 7fae752..1553525 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -70,6 +70,8 @@
val dozeAmount: Flow<Float> = repository.linearDozeAmount
/** Whether the system is in doze mode. */
val isDozing: Flow<Boolean> = repository.isDozing
+ /** Receive an event for doze time tick */
+ val dozeTimeTick: Flow<Unit> = repository.dozeTimeTick
/** Whether Always-on Display mode is available. */
val isAodAvailable: Flow<Boolean> = repository.isAodAvailable
/** Doze transition information. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
index 5005b6c..10dd900 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
@@ -17,8 +17,8 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
@@ -31,9 +31,9 @@
*/
@SysUISingleton
class NoopKeyguardFaceAuthInteractor @Inject constructor() : KeyguardFaceAuthInteractor {
- override val authenticationStatus: Flow<AuthenticationStatus>
+ override val authenticationStatus: Flow<FaceAuthenticationStatus>
get() = emptyFlow()
- override val detectionStatus: Flow<DetectionStatus>
+ override val detectionStatus: Flow<FaceDetectionStatus>
get() = emptyFlow()
override fun canFaceAuthRun(): Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractor.kt
new file mode 100644
index 0000000..a2287c7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractor.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.content.Context
+import android.content.Intent
+import android.hardware.fingerprint.FingerprintManager
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
+import com.android.systemui.plugins.ActivityStarter
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.launch
+
+/** Business logic for handling authentication events when an app is occluding the lockscreen. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class OccludingAppDeviceEntryInteractor
+@Inject
+constructor(
+ biometricMessageInteractor: BiometricMessageInteractor,
+ fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
+ keyguardInteractor: KeyguardInteractor,
+ primaryBouncerInteractor: PrimaryBouncerInteractor,
+ alternateBouncerInteractor: AlternateBouncerInteractor,
+ @Application scope: CoroutineScope,
+ private val context: Context,
+ activityStarter: ActivityStarter,
+) {
+ private val keyguardOccludedByApp: Flow<Boolean> =
+ combine(
+ keyguardInteractor.isKeyguardOccluded,
+ keyguardInteractor.isKeyguardShowing,
+ primaryBouncerInteractor.isShowing,
+ alternateBouncerInteractor.isVisible,
+ ) { occluded, showing, primaryBouncerShowing, alternateBouncerVisible ->
+ occluded && showing && !primaryBouncerShowing && !alternateBouncerVisible
+ }
+ .distinctUntilChanged()
+ private val fingerprintUnlockSuccessEvents: Flow<Unit> =
+ fingerprintAuthRepository.authenticationStatus
+ .ifKeyguardOccludedByApp()
+ .filter { it is SuccessFingerprintAuthenticationStatus }
+ .map {} // maps FingerprintAuthenticationStatus => Unit
+ private val fingerprintLockoutEvents: Flow<Unit> =
+ fingerprintAuthRepository.authenticationStatus
+ .ifKeyguardOccludedByApp()
+ .filter {
+ it is ErrorFingerprintAuthenticationStatus &&
+ (it.msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT ||
+ it.msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
+ }
+ .map {} // maps FingerprintAuthenticationStatus => Unit
+ val message: Flow<BiometricMessage?> =
+ merge(
+ biometricMessageInteractor.fingerprintErrorMessage,
+ biometricMessageInteractor.fingerprintFailMessage,
+ biometricMessageInteractor.fingerprintHelpMessage,
+ )
+ .ifKeyguardOccludedByApp(/* elseFlow */ flowOf(null))
+
+ init {
+ scope.launch {
+ // On fingerprint success, go to the home screen
+ fingerprintUnlockSuccessEvents.collect { goToHomeScreen() }
+ }
+
+ scope.launch {
+ // On device fingerprint lockout, request the bouncer with a runnable to
+ // go to the home screen. Without this, the bouncer won't proceed to the home screen.
+ fingerprintLockoutEvents.collect {
+ activityStarter.dismissKeyguardThenExecute(
+ object : ActivityStarter.OnDismissAction {
+ override fun onDismiss(): Boolean {
+ goToHomeScreen()
+ return false
+ }
+
+ override fun willRunAnimationOnKeyguard(): Boolean {
+ return false
+ }
+ },
+ /* cancel= */ null,
+ /* afterKeyguardGone */ false
+ )
+ }
+ }
+ }
+
+ /** Launches an Activity which forces the current app to background by going home. */
+ private fun goToHomeScreen() {
+ context.startActivity(
+ Intent(Intent.ACTION_MAIN).apply {
+ addCategory(Intent.CATEGORY_HOME)
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ }
+ )
+ }
+
+ private fun <T> Flow<T>.ifKeyguardOccludedByApp(elseFlow: Flow<T> = emptyFlow()): Flow<T> {
+ return keyguardOccludedByApp.flatMapLatest { keyguardOccludedByApp ->
+ if (keyguardOccludedByApp) {
+ this
+ } else {
+ elseFlow
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
index d467225..6e7a20b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
@@ -30,8 +30,8 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.util.kotlin.pairwise
@@ -165,10 +165,10 @@
repository.cancel()
}
- private val _authenticationStatusOverride = MutableStateFlow<AuthenticationStatus?>(null)
+ private val faceAuthenticationStatusOverride = MutableStateFlow<FaceAuthenticationStatus?>(null)
/** Provide the status of face authentication */
override val authenticationStatus =
- merge(_authenticationStatusOverride.filterNotNull(), repository.authenticationStatus)
+ merge(faceAuthenticationStatusOverride.filterNotNull(), repository.authenticationStatus)
/** Provide the status of face detection */
override val detectionStatus = repository.detectionStatus
@@ -176,13 +176,13 @@
private fun runFaceAuth(uiEvent: FaceAuthUiEvent, fallbackToDetect: Boolean) {
if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
if (repository.isLockedOut.value) {
- _authenticationStatusOverride.value =
- ErrorAuthenticationStatus(
+ faceAuthenticationStatusOverride.value =
+ ErrorFaceAuthenticationStatus(
BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT,
context.resources.getString(R.string.keyguard_face_unlock_unavailable)
)
} else {
- _authenticationStatusOverride.value = null
+ faceAuthenticationStatusOverride.value = null
applicationScope.launch {
faceAuthenticationLogger.authRequested(uiEvent)
repository.authenticate(uiEvent, fallbackToDetection = fallbackToDetect)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
index bba0e37..c0308e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
@@ -21,10 +21,16 @@
import android.animation.IntEvaluator
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
+import com.android.systemui.statusbar.phone.hideAffordancesRequest
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
/** Encapsulates business logic for transitions between UDFPS states on the keyguard. */
@ExperimentalCoroutinesApi
@@ -35,6 +41,8 @@
configRepo: ConfigurationRepository,
burnInInteractor: BurnInInteractor,
keyguardInteractor: KeyguardInteractor,
+ shadeRepository: ShadeRepository,
+ dialogManager: SystemUIDialogManager,
) {
private val intEvaluator = IntEvaluator()
private val floatEvaluator = FloatEvaluator()
@@ -56,6 +64,26 @@
floatEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInProgress),
)
}
+
+ val dialogHideAffordancesRequest: Flow<Boolean> = dialogManager.hideAffordancesRequest
+
+ val qsProgress: Flow<Float> =
+ shadeRepository.qsExpansion // swipe from top of LS
+ .map { (it * 2).coerceIn(0f, 1f) }
+ .onStart { emit(0f) }
+
+ val shadeExpansion: Flow<Float> =
+ combine(
+ shadeRepository.udfpsTransitionToFullShadeProgress, // swipe from middle of LS
+ keyguardInteractor.statusBarState, // quick swipe from middle of LS
+ ) { shadeProgress, statusBarState ->
+ if (statusBarState == StatusBarState.SHADE_LOCKED) {
+ 1f
+ } else {
+ shadeProgress
+ }
+ }
+ .onStart { emit(0f) }
}
data class BurnInOffsets(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
index b354cfd..d9792cf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
@@ -23,33 +23,36 @@
* Authentication status provided by
* [com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository]
*/
-sealed class AuthenticationStatus
-
-/** Success authentication status. */
-data class SuccessAuthenticationStatus(val successResult: FaceManager.AuthenticationResult) :
- AuthenticationStatus()
-
-/** Face authentication help message. */
-data class HelpAuthenticationStatus(val msgId: Int, val msg: String?) : AuthenticationStatus()
-
-/** Face acquired message. */
-data class AcquiredAuthenticationStatus(val acquiredInfo: Int) : AuthenticationStatus()
-
-/** Face authentication failed message. */
-object FailedAuthenticationStatus : AuthenticationStatus()
-
-/** Face authentication error message */
-data class ErrorAuthenticationStatus(
- val msgId: Int,
- val msg: String? = null,
+sealed class FaceAuthenticationStatus(
// present to break equality check if the same error occurs repeatedly.
val createdAt: Long = elapsedRealtime()
-) : AuthenticationStatus() {
+)
+
+/** Success authentication status. */
+data class SuccessFaceAuthenticationStatus(val successResult: FaceManager.AuthenticationResult) :
+ FaceAuthenticationStatus()
+
+/** Face authentication help message. */
+data class HelpFaceAuthenticationStatus(val msgId: Int, val msg: String?) :
+ FaceAuthenticationStatus()
+
+/** Face acquired message. */
+data class AcquiredFaceAuthenticationStatus(val acquiredInfo: Int) : FaceAuthenticationStatus()
+
+/** Face authentication failed message. */
+object FailedFaceAuthenticationStatus : FaceAuthenticationStatus()
+
+/** Face authentication error message */
+data class ErrorFaceAuthenticationStatus(
+ val msgId: Int,
+ val msg: String? = null,
+) : FaceAuthenticationStatus() {
/**
* Method that checks if [msgId] is a lockout error. A lockout error means that face
* authentication is locked out.
*/
- fun isLockoutError() = msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT
+ fun isLockoutError() =
+ msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT || msgId == FaceManager.FACE_ERROR_LOCKOUT
/**
* Method that checks if [msgId] is a cancellation error. This means that face authentication
@@ -61,7 +64,21 @@
fun isHardwareError() =
msgId == FaceManager.FACE_ERROR_HW_UNAVAILABLE ||
msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS
+
+ companion object {
+ /**
+ * Error message that is created when cancel confirmation is not received from FaceManager
+ * after we request for a cancellation of face auth.
+ */
+ fun cancelNotReceivedError() = ErrorFaceAuthenticationStatus(-1, "")
+ }
}
/** Face detection success message. */
-data class DetectionStatus(val sensorId: Int, val userId: Int, val isStrongBiometric: Boolean)
+data class FaceDetectionStatus(
+ val sensorId: Int,
+ val userId: Int,
+ val isStrongBiometric: Boolean,
+ // present to break equality check if the same error occurs repeatedly.
+ val createdAt: Long = elapsedRealtime()
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt
new file mode 100644
index 0000000..7fc6016
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import android.os.SystemClock.elapsedRealtime
+
+/**
+ * Fingerprint authentication status provided by
+ * [com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository]
+ */
+sealed class FingerprintAuthenticationStatus
+
+/** Fingerprint authentication success status. */
+data class SuccessFingerprintAuthenticationStatus(
+ val userId: Int,
+ val isStrongBiometric: Boolean,
+) : FingerprintAuthenticationStatus()
+
+/** Fingerprint authentication help message. */
+data class HelpFingerprintAuthenticationStatus(
+ val msgId: Int,
+ val msg: String?,
+) : FingerprintAuthenticationStatus()
+
+/** Fingerprint acquired message. */
+data class AcquiredFingerprintAuthenticationStatus(val acquiredInfo: Int) :
+ FingerprintAuthenticationStatus()
+
+/** Fingerprint authentication failed message. */
+object FailFingerprintAuthenticationStatus : FingerprintAuthenticationStatus()
+
+/** Fingerprint authentication error message */
+data class ErrorFingerprintAuthenticationStatus(
+ val msgId: Int,
+ val msg: String? = null,
+ // present to break equality check if the same error occurs repeatedly.
+ val createdAt: Long = elapsedRealtime(),
+) : FingerprintAuthenticationStatus()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenModel.kt
new file mode 100644
index 0000000..80a1b75
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenModel.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.keyguard.shared.model
+
+import com.android.systemui.keyguard.ScreenLifecycle
+
+/** Model device screen lifecycle states. */
+data class ScreenModel(
+ val state: ScreenState,
+) {
+ companion object {
+ fun fromScreenLifecycle(screenLifecycle: ScreenLifecycle): ScreenModel {
+ return ScreenModel(ScreenState.fromScreenLifecycleInt(screenLifecycle.getScreenState()))
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenState.kt
new file mode 100644
index 0000000..fe5d935
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenState.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.keyguard.shared.model
+
+import com.android.systemui.keyguard.ScreenLifecycle
+
+enum class ScreenState {
+ /** Screen is fully off. */
+ SCREEN_OFF,
+ /** Signal that the screen is turning on. */
+ SCREEN_TURNING_ON,
+ /** Screen is fully on. */
+ SCREEN_ON,
+ /** Signal that the screen is turning off. */
+ SCREEN_TURNING_OFF;
+
+ companion object {
+ fun fromScreenLifecycleInt(value: Int): ScreenState {
+ return when (value) {
+ ScreenLifecycle.SCREEN_OFF -> SCREEN_OFF
+ ScreenLifecycle.SCREEN_TURNING_ON -> SCREEN_TURNING_ON
+ ScreenLifecycle.SCREEN_ON -> SCREEN_ON
+ ScreenLifecycle.SCREEN_TURNING_OFF -> SCREEN_TURNING_OFF
+ else -> throw IllegalArgumentException("Invalid screen value: $value")
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
new file mode 100644
index 0000000..1db596b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.annotation.DrawableRes
+import android.view.ViewGroup
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.common.shared.model.TintedIcon
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.temporarydisplay.ViewPriority
+import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
+import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+
+/** Bind occludingAppDeviceEntryMessageViewModel to run whenever the keyguard view is attached. */
+@ExperimentalCoroutinesApi
+object KeyguardRootViewBinder {
+ @JvmStatic
+ fun bind(
+ view: ViewGroup,
+ featureFlags: FeatureFlags,
+ occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
+ chipbarCoordinator: ChipbarCoordinator,
+ ) {
+ if (featureFlags.isEnabled(Flags.FP_LISTEN_OCCLUDING_APPS)) {
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
+ occludingAppDeviceEntryMessageViewModel.message.collect { biometricMessage
+ ->
+ if (biometricMessage?.message != null) {
+ chipbarCoordinator.displayView(
+ createChipbarInfo(
+ biometricMessage.message,
+ R.drawable.ic_lock,
+ )
+ )
+ } else {
+ chipbarCoordinator.removeView(ID, "occludingAppMsgNull")
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates an instance of [ChipbarInfo] that can be sent to [ChipbarCoordinator] for display.
+ */
+ private fun createChipbarInfo(message: String, @DrawableRes icon: Int): ChipbarInfo {
+ return ChipbarInfo(
+ startIcon =
+ TintedIcon(
+ Icon.Resource(icon, null),
+ ChipbarInfo.DEFAULT_ICON_TINT,
+ ),
+ text = Text.Loaded(message),
+ endItem = null,
+ vibrationEffect = null,
+ windowTitle = "OccludingAppUnlockMsgChip",
+ wakeReason = "OCCLUDING_APP_UNLOCK_MSG_CHIP",
+ timeoutMs = 3500,
+ id = ID,
+ priority = ViewPriority.CRITICAL,
+ instanceId = null,
+ )
+ }
+
+ private const val ID = "occluding_app_device_entry_unlock_msg"
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
index 728dd39..9872d97 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
@@ -37,6 +37,7 @@
view: LottieAnimationView,
viewModel: UdfpsAodViewModel,
) {
+ view.alpha = 0f
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt
index 26ef468..0113628 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt
@@ -38,6 +38,7 @@
view: ImageView,
viewModel: BackgroundViewModel,
) {
+ view.alpha = 0f
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
index 0ab8e52..bab04f2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
@@ -42,6 +42,7 @@
view: LottieAnimationView,
viewModel: FingerprintViewModel,
) {
+ view.alpha = 0f
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludingAppDeviceEntryMessageViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludingAppDeviceEntryMessageViewModel.kt
new file mode 100644
index 0000000..3a162d7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludingAppDeviceEntryMessageViewModel.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.BiometricMessage
+import com.android.systemui.keyguard.domain.interactor.OccludingAppDeviceEntryInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/** Shows authentication messages over occcluding apps over the lockscreen. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class OccludingAppDeviceEntryMessageViewModel
+@Inject
+constructor(
+ interactor: OccludingAppDeviceEntryInteractor,
+) {
+ val message: Flow<BiometricMessage?> = interactor.message
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
index fd4b666..b307f1b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
@@ -21,13 +21,18 @@
import com.android.settingslib.Utils.getColorAttrDefaultColor
import com.android.systemui.R
import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.wm.shell.animation.Interpolators
import javax.inject.Inject
import kotlin.math.roundToInt
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
@@ -38,6 +43,8 @@
lockscreenColorResId: Int,
alternateBouncerColorResId: Int,
transitionInteractor: KeyguardTransitionInteractor,
+ udfpsKeyguardInteractor: UdfpsKeyguardInteractor,
+ keyguardInteractor: KeyguardInteractor,
) {
private val toLockscreen: Flow<TransitionViewModel> =
transitionInteractor.anyStateToLockscreenTransition.map {
@@ -54,46 +61,53 @@
}
private val toAlternateBouncer: Flow<TransitionViewModel> =
- transitionInteractor.anyStateToAlternateBouncerTransition.map {
- TransitionViewModel(
- alpha = 1f,
- scale =
- if (visibleInKeyguardState(it.from)) {
- 1f
- } else {
- it.value
- },
- color = getColorAttrDefaultColor(context, alternateBouncerColorResId),
- )
+ keyguardInteractor.statusBarState.flatMapLatest { statusBarState ->
+ transitionInteractor.anyStateToAlternateBouncerTransition.map {
+ TransitionViewModel(
+ alpha = 1f,
+ scale =
+ if (visibleInKeyguardState(it.from, statusBarState)) {
+ 1f
+ } else {
+ Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it.value)
+ },
+ color = getColorAttrDefaultColor(context, alternateBouncerColorResId),
+ )
+ }
}
private val fadeOut: Flow<TransitionViewModel> =
- merge(
- transitionInteractor.anyStateToGoneTransition,
- transitionInteractor.anyStateToAodTransition,
- transitionInteractor.anyStateToOccludedTransition,
- transitionInteractor.anyStateToPrimaryBouncerTransition,
- transitionInteractor.anyStateToDreamingTransition,
- )
- .map {
- TransitionViewModel(
- alpha =
- if (visibleInKeyguardState(it.from)) {
- 1f - it.value
- } else {
- 0f
- },
- scale = 1f,
- color =
- if (it.from == KeyguardState.ALTERNATE_BOUNCER) {
- getColorAttrDefaultColor(context, alternateBouncerColorResId)
- } else {
- getColorAttrDefaultColor(context, lockscreenColorResId)
- },
+ keyguardInteractor.statusBarState.flatMapLatest { statusBarState ->
+ merge(
+ transitionInteractor.anyStateToGoneTransition,
+ transitionInteractor.anyStateToAodTransition,
+ transitionInteractor.anyStateToOccludedTransition,
+ transitionInteractor.anyStateToPrimaryBouncerTransition,
+ transitionInteractor.anyStateToDreamingTransition,
)
- }
+ .map {
+ TransitionViewModel(
+ alpha =
+ if (visibleInKeyguardState(it.from, statusBarState)) {
+ 1f - it.value
+ } else {
+ 0f
+ },
+ scale = 1f,
+ color =
+ if (it.from == KeyguardState.ALTERNATE_BOUNCER) {
+ getColorAttrDefaultColor(context, alternateBouncerColorResId)
+ } else {
+ getColorAttrDefaultColor(context, lockscreenColorResId)
+ },
+ )
+ }
+ }
- private fun visibleInKeyguardState(state: KeyguardState): Boolean {
+ private fun visibleInKeyguardState(
+ state: KeyguardState,
+ statusBarState: StatusBarState
+ ): Boolean {
return when (state) {
KeyguardState.OFF,
KeyguardState.DOZING,
@@ -102,17 +116,53 @@
KeyguardState.PRIMARY_BOUNCER,
KeyguardState.GONE,
KeyguardState.OCCLUDED -> false
- KeyguardState.LOCKSCREEN,
+ KeyguardState.LOCKSCREEN -> statusBarState == StatusBarState.KEYGUARD
KeyguardState.ALTERNATE_BOUNCER -> true
}
}
- val transition: Flow<TransitionViewModel> =
+ private val keyguardStateTransition =
merge(
toAlternateBouncer,
toLockscreen,
fadeOut,
)
+
+ private val dialogHideAffordancesAlphaMultiplier: Flow<Float> =
+ udfpsKeyguardInteractor.dialogHideAffordancesRequest.map { hideAffordances ->
+ if (hideAffordances) {
+ 0f
+ } else {
+ 1f
+ }
+ }
+
+ private val alphaMultiplier: Flow<Float> =
+ combine(
+ transitionInteractor.startedKeyguardState,
+ dialogHideAffordancesAlphaMultiplier,
+ udfpsKeyguardInteractor.shadeExpansion,
+ udfpsKeyguardInteractor.qsProgress,
+ ) { startedKeyguardState, dialogHideAffordancesAlphaMultiplier, shadeExpansion, qsProgress
+ ->
+ if (startedKeyguardState == KeyguardState.ALTERNATE_BOUNCER) {
+ 1f
+ } else {
+ dialogHideAffordancesAlphaMultiplier * (1f - shadeExpansion) * (1f - qsProgress)
+ }
+ }
+
+ val transition: Flow<TransitionViewModel> =
+ combine(
+ alphaMultiplier,
+ keyguardStateTransition,
+ ) { alphaMultiplier, keyguardStateTransition ->
+ TransitionViewModel(
+ alpha = keyguardStateTransition.alpha * alphaMultiplier,
+ scale = keyguardStateTransition.scale,
+ color = keyguardStateTransition.color,
+ )
+ }
val visible: Flow<Boolean> = transition.map { it.alpha != 0f }
}
@@ -123,12 +173,15 @@
val context: Context,
transitionInteractor: KeyguardTransitionInteractor,
interactor: UdfpsKeyguardInteractor,
+ keyguardInteractor: KeyguardInteractor,
) :
UdfpsLockscreenViewModel(
context,
android.R.attr.textColorPrimary,
com.android.internal.R.attr.materialColorOnPrimaryFixed,
transitionInteractor,
+ interactor,
+ keyguardInteractor,
) {
val dozeAmount: Flow<Float> = interactor.dozeAmount
val burnInOffsets: Flow<BurnInOffsets> = interactor.burnInOffsets
@@ -147,12 +200,16 @@
constructor(
val context: Context,
transitionInteractor: KeyguardTransitionInteractor,
+ interactor: UdfpsKeyguardInteractor,
+ keyguardInteractor: KeyguardInteractor,
) :
UdfpsLockscreenViewModel(
context,
com.android.internal.R.attr.colorSurface,
com.android.internal.R.attr.materialColorPrimaryFixed,
transitionInteractor,
+ interactor,
+ keyguardInteractor,
)
data class TransitionViewModel(
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index 68cdfb6..373f705 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -4,7 +4,7 @@
import android.hardware.face.FaceSensorPropertiesInternal
import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.FaceAuthLog
@@ -240,7 +240,7 @@
)
}
- fun hardwareError(errorStatus: ErrorAuthenticationStatus) {
+ fun hardwareError(errorStatus: ErrorFaceAuthenticationStatus) {
logBuffer.log(
TAG,
DEBUG,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index 8d3c6d5..8f884d24 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -30,6 +30,9 @@
import android.os.ResultReceiver
import android.os.UserHandle
import android.view.ViewGroup
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider
import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider
@@ -54,7 +57,11 @@
/** This is used to override the dependency in a screenshot test */
@VisibleForTesting
private val listControllerFactory: ((userHandle: UserHandle) -> ResolverListController)?
-) : ChooserActivity(), MediaProjectionAppSelectorView, MediaProjectionAppSelectorResultHandler {
+) :
+ ChooserActivity(),
+ MediaProjectionAppSelectorView,
+ MediaProjectionAppSelectorResultHandler,
+ LifecycleOwner {
@Inject
constructor(
@@ -62,6 +69,8 @@
activityLauncher: AsyncActivityLauncher
) : this(componentFactory, activityLauncher, listControllerFactory = null)
+ private val lifecycleRegistry = LifecycleRegistry(this)
+ override val lifecycle = lifecycleRegistry
private lateinit var configurationController: ConfigurationController
private lateinit var controller: MediaProjectionAppSelectorController
private lateinit var recentsViewController: MediaProjectionRecentsViewController
@@ -75,7 +84,9 @@
override fun getLayoutResource() = R.layout.media_projection_app_selector
public override fun onCreate(bundle: Bundle?) {
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
component = componentFactory.create(activity = this, view = this, resultHandler = this)
+ component.lifecycleObservers.forEach { lifecycle.addObserver(it) }
// Create a separate configuration controller for this activity as the configuration
// might be different from the global one
@@ -96,6 +107,26 @@
controller.init()
}
+ override fun onStart() {
+ super.onStart()
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
+ }
+
+ override fun onResume() {
+ super.onResume()
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
+ }
+
+ override fun onPause() {
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+ super.onPause()
+ }
+
+ override fun onStop() {
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
+ super.onStop()
+ }
+
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
configurationController.onConfigurationChanged(newConfig)
@@ -152,6 +183,8 @@
}
override fun onDestroy() {
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+ component.lifecycleObservers.forEach { lifecycle.removeObserver(it) }
// onDestroy is also called when an app is selected, in that case we only want to send
// RECORD_CONTENT_TASK but not RECORD_CANCEL
if (!taskSelected) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
index 6ad36ca..c90a3c1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
@@ -144,7 +144,7 @@
val oldKey: String?,
val controller: MediaController?,
val localMediaManager: LocalMediaManager,
- val muteAwaitConnectionManager: MediaMuteAwaitConnectionManager?
+ val muteAwaitConnectionManager: MediaMuteAwaitConnectionManager,
) :
LocalMediaManager.DeviceCallback,
MediaController.Callback(),
@@ -180,7 +180,7 @@
if (!started) {
localMediaManager.registerCallback(this)
localMediaManager.startScan()
- muteAwaitConnectionManager?.startListening()
+ muteAwaitConnectionManager.startListening()
playbackType = controller?.playbackInfo?.playbackType ?: PLAYBACK_TYPE_UNKNOWN
playbackVolumeControlId = controller?.playbackInfo?.volumeControlId
controller?.registerCallback(this)
@@ -198,7 +198,7 @@
controller?.unregisterCallback(this)
localMediaManager.stopScan()
localMediaManager.unregisterCallback(this)
- muteAwaitConnectionManager?.stopListening()
+ muteAwaitConnectionManager.stopListening()
configurationController.removeCallback(configListener)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index 09aef88..f2db088 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -34,15 +34,6 @@
return enabled || featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS)
}
- /** Check whether we support displaying information about mute await connections. */
- fun areMuteAwaitConnectionsEnabled() = featureFlags.isEnabled(Flags.MEDIA_MUTE_AWAIT)
-
- /**
- * Check whether we enable support for nearby media devices. See
- * [android.app.StatusBarManager.registerNearbyMediaDevicesProvider] for more information.
- */
- fun areNearbyMediaDevicesEnabled() = featureFlags.isEnabled(Flags.MEDIA_NEARBY_DEVICES)
-
/**
* If true, keep active media controls for the lifetime of the MediaSession, regardless of
* whether the underlying notification was dismissed
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 46efac5..888cd0b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -23,10 +23,7 @@
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
import com.android.systemui.media.controls.ui.MediaHost;
import com.android.systemui.media.controls.ui.MediaHostStatesManager;
-import com.android.systemui.media.controls.util.MediaFlags;
import com.android.systemui.media.dream.dagger.MediaComplicationComponent;
-import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionCli;
-import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
import com.android.systemui.media.taptotransfer.MediaTttFlags;
import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogBuffer;
@@ -119,29 +116,4 @@
}
return Optional.of(helperLazy.get());
}
-
- /** */
- @Provides
- @SysUISingleton
- static Optional<MediaMuteAwaitConnectionCli> providesMediaMuteAwaitConnectionCli(
- MediaFlags mediaFlags,
- Lazy<MediaMuteAwaitConnectionCli> muteAwaitConnectionCliLazy
- ) {
- if (!mediaFlags.areMuteAwaitConnectionsEnabled()) {
- return Optional.empty();
- }
- return Optional.of(muteAwaitConnectionCliLazy.get());
- }
-
- /** */
- @Provides
- @SysUISingleton
- static Optional<NearbyMediaDevicesManager> providesNearbyMediaDevicesManager(
- MediaFlags mediaFlags,
- Lazy<NearbyMediaDevicesManager> nearbyMediaDevicesManagerLazy) {
- if (!mediaFlags.areNearbyMediaDevicesEnabled()) {
- return Optional.empty();
- }
- return Optional.of(nearbyMediaDevicesManagerLazy.get());
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogFactory.kt
index a1e9995..18d5103 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogFactory.kt
@@ -31,7 +31,6 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
-import java.util.Optional
import javax.inject.Inject
/**
@@ -46,7 +45,7 @@
private val notifCollection: CommonNotifCollection,
private val uiEventLogger: UiEventLogger,
private val dialogLaunchAnimator: DialogLaunchAnimator,
- private val nearbyMediaDevicesManagerOptional: Optional<NearbyMediaDevicesManager>,
+ private val nearbyMediaDevicesManager: NearbyMediaDevicesManager,
private val audioManager: AudioManager,
private val powerExemptionManager: PowerExemptionManager,
private val keyGuardManager: KeyguardManager,
@@ -62,7 +61,7 @@
val controller = MediaOutputController(context, packageName,
mediaSessionManager, lbm, starter, notifCollection,
- dialogLaunchAnimator, nearbyMediaDevicesManagerOptional, audioManager,
+ dialogLaunchAnimator, nearbyMediaDevicesManager, audioManager,
powerExemptionManager, keyGuardManager, featureFlags, userTracker)
val dialog =
MediaOutputBroadcastDialog(context, aboveStatusBar, broadcastSender, controller)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index f87f53c..83631b0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -100,7 +100,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -178,7 +177,7 @@
lbm, ActivityStarter starter,
CommonNotifCollection notifCollection,
DialogLaunchAnimator dialogLaunchAnimator,
- Optional<NearbyMediaDevicesManager> nearbyMediaDevicesManagerOptional,
+ NearbyMediaDevicesManager nearbyMediaDevicesManager,
AudioManager audioManager,
PowerExemptionManager powerExemptionManager,
KeyguardManager keyGuardManager,
@@ -199,7 +198,7 @@
mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
mDialogLaunchAnimator = dialogLaunchAnimator;
- mNearbyMediaDevicesManager = nearbyMediaDevicesManagerOptional.orElse(null);
+ mNearbyMediaDevicesManager = nearbyMediaDevicesManager;
mColorItemContent = Utils.getColorStateListDefaultColor(mContext,
R.color.media_dialog_item_main_content);
mColorSeekbarProgress = Utils.getColorStateListDefaultColor(mContext,
@@ -826,13 +825,7 @@
}
List<RoutingSessionInfo> getActiveRemoteMediaDevices() {
- final List<RoutingSessionInfo> sessionInfos = new ArrayList<>();
- for (RoutingSessionInfo info : mLocalMediaManager.getActiveMediaSession()) {
- if (!info.isSystemSession()) {
- sessionInfos.add(info);
- }
- }
- return sessionInfos;
+ return new ArrayList<>(mLocalMediaManager.getRemoteRoutingSessions());
}
void adjustVolume(MediaDevice device, int volume) {
@@ -928,7 +921,7 @@
void launchMediaOutputBroadcastDialog(View mediaOutputDialog, BroadcastSender broadcastSender) {
MediaOutputController controller = new MediaOutputController(mContext, mPackageName,
mMediaSessionManager, mLocalBluetoothManager, mActivityStarter,
- mNotifCollection, mDialogLaunchAnimator, Optional.of(mNearbyMediaDevicesManager),
+ mNotifCollection, mDialogLaunchAnimator, mNearbyMediaDevicesManager,
mAudioManager, mPowerExemptionManager, mKeyGuardManager, mFeatureFlags,
mUserTracker);
MediaOutputBroadcastDialog dialog = new MediaOutputBroadcastDialog(mContext, true,
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index 4c168ec..af65937 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -33,7 +33,6 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
-import java.util.Optional
import javax.inject.Inject
/**
@@ -48,7 +47,7 @@
private val notifCollection: CommonNotifCollection,
private val uiEventLogger: UiEventLogger,
private val dialogLaunchAnimator: DialogLaunchAnimator,
- private val nearbyMediaDevicesManagerOptional: Optional<NearbyMediaDevicesManager>,
+ private val nearbyMediaDevicesManager: NearbyMediaDevicesManager,
private val audioManager: AudioManager,
private val powerExemptionManager: PowerExemptionManager,
private val keyGuardManager: KeyguardManager,
@@ -68,7 +67,7 @@
val controller = MediaOutputController(
context, packageName,
mediaSessionManager, lbm, starter, notifCollection,
- dialogLaunchAnimator, nearbyMediaDevicesManagerOptional, audioManager,
+ dialogLaunchAnimator, nearbyMediaDevicesManager, audioManager,
powerExemptionManager, keyGuardManager, featureFlags, userTracker)
val dialog =
MediaOutputDialog(context, aboveStatusBar, broadcastSender, controller,
diff --git a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerFactory.kt b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerFactory.kt
index e260894..97ec654 100644
--- a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerFactory.kt
@@ -21,14 +21,12 @@
import com.android.settingslib.media.LocalMediaManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.controls.util.MediaFlags
import java.util.concurrent.Executor
import javax.inject.Inject
/** Factory class to create [MediaMuteAwaitConnectionManager] instances. */
@SysUISingleton
class MediaMuteAwaitConnectionManagerFactory @Inject constructor(
- private val mediaFlags: MediaFlags,
private val context: Context,
private val logger: MediaMuteAwaitLogger,
@Main private val mainExecutor: Executor
@@ -36,10 +34,7 @@
private val deviceIconUtil = DeviceIconUtil()
/** Creates a [MediaMuteAwaitConnectionManager]. */
- fun create(localMediaManager: LocalMediaManager): MediaMuteAwaitConnectionManager? {
- if (!mediaFlags.areMuteAwaitConnectionsEnabled()) {
- return null
- }
+ fun create(localMediaManager: LocalMediaManager): MediaMuteAwaitConnectionManager {
return MediaMuteAwaitConnectionManager(
mainExecutor, localMediaManager, context, deviceIconUtil, logger
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
index 60504e4..8a565fa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
@@ -30,8 +30,4 @@
/** Check whether the flag for the receiver success state is enabled. */
fun isMediaTttReceiverSuccessRippleEnabled(): Boolean =
featureFlags.isEnabled(Flags.MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE)
-
- /** True if the media transfer chip can be dismissed via a gesture. */
- fun isMediaTttDismissGestureEnabled(): Boolean =
- featureFlags.isEnabled(Flags.MEDIA_TAP_TO_TRANSFER_DISMISS_GESTURE)
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 3088d8b..11538fa 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -20,6 +20,7 @@
import android.content.ComponentName
import android.content.Context
import android.os.UserHandle
+import androidx.lifecycle.DefaultLifecycleObserver
import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.MediaProjectionAppSelectorActivity
@@ -46,6 +47,7 @@
import dagger.Subcomponent
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import dagger.multibindings.IntoSet
import javax.inject.Qualifier
import javax.inject.Scope
import kotlinx.coroutines.CoroutineScope
@@ -100,6 +102,12 @@
@MediaProjectionAppSelectorScope
fun bindAppIconLoader(impl: IconLoaderLibAppIconLoader): AppIconLoader
+ @Binds
+ @IntoSet
+ fun taskPreviewSizeProviderAsLifecycleObserver(
+ impl: TaskPreviewSizeProvider
+ ): DefaultLifecycleObserver
+
companion object {
@Provides
@MediaProjectionAppSelector
@@ -166,4 +174,5 @@
@get:PersonalProfile val personalProfileUserHandle: UserHandle
@MediaProjectionAppSelector val configurationController: ConfigurationController
+ val lifecycleObservers: Set<DefaultLifecycleObserver>
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
index 89f66b7..864d35a 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
@@ -21,6 +21,8 @@
import android.graphics.Rect
import android.view.WindowInsets.Type
import android.view.WindowManager
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope
import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener
import com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen
@@ -35,18 +37,22 @@
constructor(
private val context: Context,
private val windowManager: WindowManager,
- configurationController: ConfigurationController
-) : CallbackController<TaskPreviewSizeListener>, ConfigurationListener {
+ private val configurationController: ConfigurationController,
+) : CallbackController<TaskPreviewSizeListener>, ConfigurationListener, DefaultLifecycleObserver {
/** Returns the size of the task preview on the screen in pixels */
val size: Rect = calculateSize()
private val listeners = arrayListOf<TaskPreviewSizeListener>()
- init {
+ override fun onCreate(owner: LifecycleOwner) {
configurationController.addCallback(this)
}
+ override fun onDestroy(owner: LifecycleOwner) {
+ configurationController.removeCallback(this)
+ }
+
override fun onConfigChanged(newConfig: Configuration) {
val newSize = calculateSize()
if (newSize != size) {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
index d652889..d949a2a 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
@@ -145,13 +145,10 @@
}
interface Callback {
- @JvmDefault
fun onFlagMicCameraChanged(flag: Boolean) {}
- @JvmDefault
fun onFlagLocationChanged(flag: Boolean) {}
- @JvmDefault
fun onFlagMediaProjectionChanged(flag: Boolean) {}
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index a676150..eb8ef9b 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -216,7 +216,6 @@
interface Callback : PrivacyConfig.Callback {
fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>)
- @JvmDefault
fun onFlagAllChanged(flag: Boolean) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/PrivacyChipDrawable.java b/packages/SystemUI/src/com/android/systemui/privacy/television/PrivacyChipDrawable.java
deleted file mode 100644
index 08911d4..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/television/PrivacyChipDrawable.java
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.privacy.television;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.util.Log;
-import android.util.MathUtils;
-import android.view.Gravity;
-
-import androidx.annotation.Keep;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.systemui.R;
-
-/**
- * Drawable that can go from being the background of the privacy icons to a small dot.
- * The icons are not included.
- */
-public class PrivacyChipDrawable extends Drawable {
- private static final String TAG = PrivacyChipDrawable.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- private final Paint mChipPaint;
- private final Paint mBgPaint;
- private final Rect mTmpRect = new Rect();
- private final Rect mBgRect = new Rect();
- private final RectF mTmpRectF = new RectF();
- private final Path mPath = new Path();
- private final Animator mCollapse;
- private final Animator mExpand;
- private final int mLayoutDirection;
- private final int mBgWidth;
- private final int mBgHeight;
- private final int mBgRadius;
- private final int mDotSize;
- private final float mExpandedChipRadius;
- private final float mCollapsedChipRadius;
-
- private final boolean mCollapseToDot;
-
- private boolean mIsExpanded = true;
- private float mCollapseProgress = 0f;
-
- public PrivacyChipDrawable(Context context, int chipColorRes, boolean collapseToDot) {
- mCollapseToDot = collapseToDot;
-
- mChipPaint = new Paint();
- mChipPaint.setStyle(Paint.Style.FILL);
- mChipPaint.setColor(context.getColor(chipColorRes));
- mChipPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
-
- mBgPaint = new Paint();
- mBgPaint.setStyle(Paint.Style.FILL);
- mBgPaint.setColor(context.getColor(R.color.privacy_chip_dot_bg_tint));
- mBgPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
-
- Resources res = context.getResources();
- mLayoutDirection = res.getConfiguration().getLayoutDirection();
- mBgWidth = res.getDimensionPixelSize(R.dimen.privacy_chip_dot_bg_width);
- mBgHeight = res.getDimensionPixelSize(R.dimen.privacy_chip_dot_bg_height);
- mBgRadius = res.getDimensionPixelSize(R.dimen.privacy_chip_dot_bg_radius);
- mDotSize = res.getDimensionPixelSize(R.dimen.privacy_chip_dot_size);
-
- mExpandedChipRadius = res.getDimensionPixelSize(R.dimen.privacy_chip_radius);
- mCollapsedChipRadius = res.getDimensionPixelSize(R.dimen.privacy_chip_dot_radius);
-
- mExpand = AnimatorInflater.loadAnimator(context, R.anim.tv_privacy_chip_expand);
- mExpand.setTarget(this);
- mCollapse = AnimatorInflater.loadAnimator(context, R.anim.tv_privacy_chip_collapse);
- mCollapse.setTarget(this);
- }
-
- /**
- * @return how far the chip is currently collapsed.
- * @see #setCollapseProgress(float)
- */
- @Keep
- public float getCollapseProgress() {
- return mCollapseProgress;
- }
-
- /**
- * Sets the collapsing progress of the chip to its collapsed state.
- * @param pct How far the chip is collapsed, in the range 0-1.
- * 0=fully expanded, 1=fully collapsed.
- */
- @Keep
- public void setCollapseProgress(float pct) {
- mCollapseProgress = pct;
- invalidateSelf();
- }
-
- @Override
- public void draw(@NonNull Canvas canvas) {
- if (mCollapseProgress > 0f) {
- // draw background
- getBackgroundBounds(mBgRect);
- mTmpRectF.set(mBgRect);
- canvas.drawRoundRect(mTmpRectF, mBgRadius, mBgRadius, mBgPaint);
- }
-
- getForegroundBounds(mTmpRectF);
- float radius = MathUtils.lerp(
- mExpandedChipRadius,
- mCollapseToDot ? mCollapsedChipRadius : mBgRadius,
- mCollapseProgress);
-
- canvas.drawRoundRect(mTmpRectF, radius, radius, mChipPaint);
- }
-
- private void getBackgroundBounds(Rect out) {
- Rect bounds = getBounds();
- Gravity.apply(Gravity.END, mBgWidth, mBgHeight, bounds, out, mLayoutDirection);
- }
-
- private void getCollapsedForegroundBounds(Rect out) {
- Rect bounds = getBounds();
- getBackgroundBounds(mBgRect);
- if (mCollapseToDot) {
- Gravity.apply(Gravity.CENTER, mDotSize, mDotSize, mBgRect, out);
- } else {
- out.set(bounds.left, mBgRect.top, bounds.right, mBgRect.bottom);
- }
- }
-
- private void getForegroundBounds(RectF out) {
- Rect bounds = getBounds();
- getCollapsedForegroundBounds(mTmpRect);
- lerpRect(bounds, mTmpRect, mCollapseProgress, out);
- }
-
- private void lerpRect(Rect start, Rect stop, float amount, RectF out) {
- float left = MathUtils.lerp(start.left, stop.left, amount);
- float top = MathUtils.lerp(start.top, stop.top, amount);
- float right = MathUtils.lerp(start.right, stop.right, amount);
- float bottom = MathUtils.lerp(start.bottom, stop.bottom, amount);
- out.set(left, top, right, bottom);
- }
-
- /**
- * Clips the given canvas to this chip's foreground shape.
- * @param canvas Canvas to clip.
- */
- public void clipToForeground(Canvas canvas) {
- getForegroundBounds(mTmpRectF);
- float radius = MathUtils.lerp(
- mExpandedChipRadius,
- mCollapseToDot ? mCollapsedChipRadius : mBgRadius,
- mCollapseProgress);
-
- mPath.reset();
- mPath.addRoundRect(mTmpRectF, radius, radius, Path.Direction.CW);
- canvas.clipPath(mPath);
- }
-
- @Override
- protected void onBoundsChange(@NonNull Rect bounds) {
- super.onBoundsChange(bounds);
- invalidateSelf();
- }
-
- @Override
- public void setAlpha(int alpha) {
- mChipPaint.setAlpha(alpha);
- mBgPaint.setAlpha(alpha);
- }
-
- /**
- * Transitions to a full chip.
- *
- * @param animate Whether to animate the change to a full chip, or expand instantly.
- */
- public void expand(boolean animate) {
- if (DEBUG) Log.d(TAG, "expanding");
- if (mIsExpanded) {
- return;
- }
- mIsExpanded = true;
- if (animate) {
- mCollapse.cancel();
- mExpand.start();
- } else {
- mCollapseProgress = 0f;
- invalidateSelf();
- }
- }
-
- /**
- * Starts the animation to a dot.
- */
- public void collapse() {
- if (DEBUG) Log.d(TAG, "collapsing");
- if (!mIsExpanded) {
- return;
- }
- mIsExpanded = false;
- mExpand.cancel();
- mCollapse.start();
- }
-
- @Override
- public void setColorFilter(@Nullable ColorFilter colorFilter) {
- // no-op
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/PrivacyItemsChip.java b/packages/SystemUI/src/com/android/systemui/privacy/television/PrivacyItemsChip.java
deleted file mode 100644
index 158a591..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/television/PrivacyItemsChip.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * 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.privacy.television;
-
-import android.annotation.ColorRes;
-import android.annotation.IntDef;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.R;
-import com.android.systemui.privacy.PrivacyType;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-/**
- * View that shows indicator icons for privacy items.
- */
-public class PrivacyItemsChip extends FrameLayout {
- private static final String TAG = "PrivacyItemsChip";
- private static final boolean DEBUG = false;
-
- /**
- * Configuration for a PrivacyItemsChip's appearance.
- */
- public static class ChipConfig {
- public final List<PrivacyType> privacyTypes;
- @ColorRes
- public final int colorRes;
- public final boolean collapseToDot;
-
- /**
- * @param privacyTypes Privacy types to show icons for, in order.
- * @param colorRes Color resource for the chip's foreground color.
- * @param collapseToDot Whether to collapse the chip in to a dot,
- * or just collapse it into a smaller chip with icons still visible.
- */
- public ChipConfig(@NonNull List<PrivacyType> privacyTypes, int colorRes,
- boolean collapseToDot) {
- this.privacyTypes = privacyTypes;
- this.colorRes = colorRes;
- this.collapseToDot = collapseToDot;
- }
- }
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"STATE_"}, value = {
- STATE_NOT_SHOWN,
- STATE_EXPANDED,
- STATE_COLLAPSED,
- })
- public @interface State {
- }
-
- private static final int STATE_NOT_SHOWN = 0;
- private static final int STATE_EXPANDED = 1;
- private static final int STATE_COLLAPSED = 2;
-
- private final ChipConfig mConfig;
- private final int mIconSize;
- private final int mCollapsedIconSize;
- private final int mIconMarginHorizontal;
- private final PrivacyChipDrawable mChipBackgroundDrawable;
- private final List<ImageView> mIcons = new ArrayList<>();
-
- @State
- private int mState = STATE_NOT_SHOWN;
-
- public PrivacyItemsChip(@NonNull Context context, @NonNull ChipConfig config) {
- super(context);
- mConfig = config;
- setVisibility(View.GONE);
-
- Resources res = context.getResources();
- mIconSize = res.getDimensionPixelSize(R.dimen.privacy_chip_icon_size);
- mCollapsedIconSize = res.getDimensionPixelSize(R.dimen.privacy_chip_collapsed_icon_size);
- mIconMarginHorizontal =
- res.getDimensionPixelSize(R.dimen.privacy_chip_icon_margin_in_between);
-
- LayoutInflater.from(context).inflate(R.layout.tv_ongoing_privacy_chip, this);
- LinearLayout iconsContainer = findViewById(R.id.icons_container);
-
- mChipBackgroundDrawable = new PrivacyChipDrawable(
- context, config.colorRes, config.collapseToDot);
- mChipBackgroundDrawable.setCallback(new Drawable.Callback() {
- @Override
- public void invalidateDrawable(@NonNull Drawable who) {
- invalidate();
- }
-
- @Override
- public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
- }
-
- @Override
- public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
- }
- });
-
- setBackground(mChipBackgroundDrawable);
-
- for (PrivacyType type : config.privacyTypes) {
- ImageView typeIconView = new ImageView(context);
- Drawable icon = type.getIcon(context);
- icon.mutate().setTint(context.getColor(R.color.privacy_icon_tint));
-
- typeIconView.setImageDrawable(icon);
- typeIconView.setScaleType(ImageView.ScaleType.FIT_CENTER);
- mIcons.add(typeIconView);
- iconsContainer.addView(typeIconView, mIconSize, mIconSize);
- LinearLayout.LayoutParams lp =
- (LinearLayout.LayoutParams) typeIconView.getLayoutParams();
- lp.leftMargin = mIconMarginHorizontal;
- lp.rightMargin = mIconMarginHorizontal;
- typeIconView.setVisibility(View.GONE);
- }
- }
-
- /**
- * Sets the active privacy types, and expands the chip if there are active items and the chip is
- * currently collapsed, or hides the chip if there are no active items.
- *
- * @param types The set of active privacy types. Only types configured in {@link ChipConfig}
- * are shown.
- */
- public void expandForTypes(Set<PrivacyType> types) {
- if (DEBUG) Log.d(TAG, "expandForTypes, state=" + stateToString(mState));
-
- boolean hasActiveTypes = false;
-
- for (int i = 0; i < mConfig.privacyTypes.size(); i++) {
- PrivacyType type = mConfig.privacyTypes.get(i);
- ImageView icon = mIcons.get(i);
- boolean isTypeActive = types.contains(type);
- hasActiveTypes = hasActiveTypes || isTypeActive;
-
- icon.setVisibility(isTypeActive ? View.VISIBLE : View.GONE);
-
- // Set icon size to expanded size
- ViewGroup.LayoutParams lp = icon.getLayoutParams();
- lp.width = mIconSize;
- lp.height = mIconSize;
- icon.requestLayout();
- }
-
- if (hasActiveTypes) {
- if (DEBUG) Log.d(TAG, "Chip has active types, expanding");
- if (mState == STATE_NOT_SHOWN) {
- mChipBackgroundDrawable.expand(/* animate= */ false);
- } else if (mState == STATE_COLLAPSED) {
- mChipBackgroundDrawable.expand(/* animate= */ true);
- }
- setVisibility(View.VISIBLE);
- setState(STATE_EXPANDED);
- } else {
- if (DEBUG) Log.d(TAG, "Chip has no active types, hiding");
- setVisibility(View.GONE);
- setState(STATE_NOT_SHOWN);
- }
- }
-
- /**
- * Collapses this chip if currently expanded.
- */
- public void collapse() {
- if (DEBUG) Log.d(TAG, "collapse");
-
- if (mState != STATE_EXPANDED) {
- return;
- }
- setState(STATE_COLLAPSED);
-
- for (ImageView icon : mIcons) {
- if (mConfig.collapseToDot) {
- icon.setVisibility(View.GONE);
- } else {
- ViewGroup.LayoutParams lp = icon.getLayoutParams();
- lp.width = mCollapsedIconSize;
- lp.height = mCollapsedIconSize;
- icon.requestLayout();
- }
- }
-
- mChipBackgroundDrawable.collapse();
- }
-
- public boolean isExpanded() {
- return mState == STATE_EXPANDED;
- }
-
- private void setState(@State int state) {
- if (mState != state) {
- if (DEBUG) Log.d(TAG, "State changed: " + stateToString(state));
- mState = state;
- }
- }
-
- @Override
- protected void dispatchDraw(Canvas canvas) {
- mChipBackgroundDrawable.clipToForeground(canvas);
- super.dispatchDraw(canvas);
- }
-
- /**
- * Used in debug logs.
- */
- private static String stateToString(@State int state) {
- switch (state) {
- case STATE_NOT_SHOWN:
- return "NOT_SHOWN";
- case STATE_EXPANDED:
- return "EXPANDED";
- case STATE_COLLAPSED:
- return "COLLAPSED";
- default:
- return "INVALID";
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/TvPrivacyChipsController.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvPrivacyChipsController.java
deleted file mode 100644
index 5e4c797..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/television/TvPrivacyChipsController.java
+++ /dev/null
@@ -1,518 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.privacy.television;
-
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UiThread;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.transition.AutoTransition;
-import android.transition.ChangeBounds;
-import android.transition.Fade;
-import android.transition.Transition;
-import android.transition.TransitionManager;
-import android.transition.TransitionSet;
-import android.util.ArraySet;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.IWindowManager;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.WindowManager;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.privacy.PrivacyItem;
-import com.android.systemui.privacy.PrivacyItemController;
-import com.android.systemui.privacy.PrivacyType;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-import javax.inject.Inject;
-
-/**
- * A SystemUI component responsible for notifying the user whenever an application is
- * recording audio, camera, the screen, or accessing the location.
- */
-@SysUISingleton
-public class TvPrivacyChipsController
- implements CoreStartable, PrivacyItemController.Callback {
- private static final String TAG = "TvPrivacyChipsController";
- private static final boolean DEBUG = false;
-
- // This title is used in CameraMicIndicatorsPermissionTest and
- // RecognitionServiceMicIndicatorTest.
- private static final String LAYOUT_PARAMS_TITLE = "MicrophoneCaptureIndicator";
-
- // Chips configuration. We're not showing a location indicator on TV.
- static final List<PrivacyItemsChip.ChipConfig> CHIPS = Arrays.asList(
- new PrivacyItemsChip.ChipConfig(
- Collections.singletonList(PrivacyType.TYPE_MEDIA_PROJECTION),
- R.color.privacy_media_projection_chip,
- /* collapseToDot= */ false),
- new PrivacyItemsChip.ChipConfig(
- Arrays.asList(PrivacyType.TYPE_CAMERA, PrivacyType.TYPE_MICROPHONE),
- R.color.privacy_mic_cam_chip,
- /* collapseToDot= */ true)
- );
-
- // Avoid multiple messages after rapid changes such as starting/stopping both camera and mic.
- private static final int ACCESSIBILITY_ANNOUNCEMENT_DELAY_MS = 500;
-
- /**
- * Time to collect privacy item updates before applying them.
- * Since MediaProjection and AppOps come from different data sources,
- * PrivacyItem updates when screen & audio recording ends do not come at the same time.
- * Without this, if eg. MediaProjection ends first, you'd see the microphone chip expand and
- * almost immediately fade out as it is expanding. With this, the two chips disappear together.
- */
- private static final int PRIVACY_ITEM_DEBOUNCE_TIMEOUT_MS = 200;
-
- // How long chips stay expanded after an update.
- private static final int EXPANDED_DURATION_MS = 4000;
-
- private final Context mContext;
- private final Handler mUiThreadHandler = new Handler(Looper.getMainLooper());
- private final Runnable mCollapseRunnable = this::collapseChips;
- private final Runnable mUpdatePrivacyItemsRunnable = this::updateChipsAndAnnounce;
- private final Runnable mAccessibilityRunnable = this::makeAccessibilityAnnouncement;
-
- private final PrivacyItemController mPrivacyItemController;
- private final IWindowManager mIWindowManager;
- private final Rect[] mBounds = new Rect[4];
- private final TransitionSet mTransition;
- private final TransitionSet mCollapseTransition;
- private boolean mIsRtl;
-
- @Nullable
- private ViewGroup mChipsContainer;
- @Nullable
- private List<PrivacyItemsChip> mChips;
- @NonNull
- private List<PrivacyItem> mPrivacyItems = Collections.emptyList();
- @NonNull
- private final List<PrivacyItem> mItemsBeforeLastAnnouncement = new ArrayList<>();
-
- @Inject
- public TvPrivacyChipsController(Context context, PrivacyItemController privacyItemController,
- IWindowManager iWindowManager) {
- mContext = context;
- if (DEBUG) Log.d(TAG, "TvPrivacyChipsController running");
- mPrivacyItemController = privacyItemController;
- mIWindowManager = iWindowManager;
-
- Resources res = mContext.getResources();
- mIsRtl = res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
- updateStaticPrivacyIndicatorBounds();
-
- Interpolator collapseInterpolator = AnimationUtils.loadInterpolator(context,
- R.interpolator.tv_privacy_chip_collapse_interpolator);
- Interpolator expandInterpolator = AnimationUtils.loadInterpolator(context,
- R.interpolator.tv_privacy_chip_expand_interpolator);
-
- TransitionSet chipFadeTransition = new TransitionSet()
- .addTransition(new Fade(Fade.IN))
- .addTransition(new Fade(Fade.OUT));
- chipFadeTransition.setOrdering(TransitionSet.ORDERING_TOGETHER);
- chipFadeTransition.excludeTarget(ImageView.class, true);
-
- Transition chipBoundsExpandTransition = new ChangeBounds();
- chipBoundsExpandTransition.excludeTarget(ImageView.class, true);
- chipBoundsExpandTransition.setInterpolator(expandInterpolator);
-
- Transition chipBoundsCollapseTransition = new ChangeBounds();
- chipBoundsCollapseTransition.excludeTarget(ImageView.class, true);
- chipBoundsCollapseTransition.setInterpolator(collapseInterpolator);
-
- TransitionSet iconCollapseTransition = new AutoTransition();
- iconCollapseTransition.setOrdering(TransitionSet.ORDERING_TOGETHER);
- iconCollapseTransition.addTarget(ImageView.class);
- iconCollapseTransition.setInterpolator(collapseInterpolator);
-
- TransitionSet iconExpandTransition = new AutoTransition();
- iconExpandTransition.setOrdering(TransitionSet.ORDERING_TOGETHER);
- iconExpandTransition.addTarget(ImageView.class);
- iconExpandTransition.setInterpolator(expandInterpolator);
-
- mTransition = new TransitionSet()
- .addTransition(chipFadeTransition)
- .addTransition(chipBoundsExpandTransition)
- .addTransition(iconExpandTransition)
- .setOrdering(TransitionSet.ORDERING_TOGETHER)
- .setDuration(res.getInteger(R.integer.privacy_chip_animation_millis));
-
- mCollapseTransition = new TransitionSet()
- .addTransition(chipFadeTransition)
- .addTransition(chipBoundsCollapseTransition)
- .addTransition(iconCollapseTransition)
- .setOrdering(TransitionSet.ORDERING_TOGETHER)
- .setDuration(res.getInteger(R.integer.privacy_chip_animation_millis));
-
- Transition.TransitionListener transitionListener = new Transition.TransitionListener() {
- @Override
- public void onTransitionStart(Transition transition) {
- if (DEBUG) Log.v(TAG, "onTransitionStart");
- }
-
- @Override
- public void onTransitionEnd(Transition transition) {
- if (DEBUG) Log.v(TAG, "onTransitionEnd");
- if (mChips != null) {
- boolean hasVisibleChip = false;
- boolean hasExpandedChip = false;
- for (PrivacyItemsChip chip : mChips) {
- hasVisibleChip = hasVisibleChip || chip.getVisibility() == View.VISIBLE;
- hasExpandedChip = hasExpandedChip || chip.isExpanded();
- }
-
- if (!hasVisibleChip) {
- if (DEBUG) Log.d(TAG, "No chips visible anymore");
- removeIndicatorView();
- } else if (hasExpandedChip) {
- if (DEBUG) Log.d(TAG, "Has expanded chips");
- collapseLater();
- }
- }
- }
-
- @Override
- public void onTransitionCancel(Transition transition) {
- }
-
- @Override
- public void onTransitionPause(Transition transition) {
- }
-
- @Override
- public void onTransitionResume(Transition transition) {
- }
- };
-
- mTransition.addListener(transitionListener);
- mCollapseTransition.addListener(transitionListener);
- }
-
- @Override
- public void onConfigurationChanged(Configuration config) {
- boolean updatedRtl = config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
- if (mIsRtl == updatedRtl) {
- return;
- }
- mIsRtl = updatedRtl;
-
- // Update privacy chip location.
- if (mChipsContainer != null) {
- removeIndicatorView();
- createAndShowIndicator();
- }
- updateStaticPrivacyIndicatorBounds();
- }
-
- @Override
- public void start() {
- mPrivacyItemController.addCallback(this);
- }
-
- @UiThread
- @Override
- public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) {
- if (DEBUG) Log.d(TAG, "onPrivacyItemsChanged");
-
- List<PrivacyItem> filteredPrivacyItems = new ArrayList<>(privacyItems);
- if (filteredPrivacyItems.removeIf(
- privacyItem -> !isPrivacyTypeShown(privacyItem.getPrivacyType()))) {
- if (DEBUG) Log.v(TAG, "Removed privacy items we don't show");
- }
-
- // Do they have the same elements? (order doesn't matter)
- if (privacyItems.size() == mPrivacyItems.size() && mPrivacyItems.containsAll(
- privacyItems)) {
- if (DEBUG) Log.d(TAG, "No change to relevant privacy items");
- return;
- }
-
- mPrivacyItems = privacyItems;
-
- if (!mUiThreadHandler.hasCallbacks(mUpdatePrivacyItemsRunnable)) {
- mUiThreadHandler.postDelayed(mUpdatePrivacyItemsRunnable,
- PRIVACY_ITEM_DEBOUNCE_TIMEOUT_MS);
- }
- }
-
- private boolean isPrivacyTypeShown(@NonNull PrivacyType type) {
- for (PrivacyItemsChip.ChipConfig chip : CHIPS) {
- if (chip.privacyTypes.contains(type)) {
- return true;
- }
- }
- return false;
- }
-
- @UiThread
- private void updateChipsAndAnnounce() {
- updateChips();
- postAccessibilityAnnouncement();
- }
-
- private void updateStaticPrivacyIndicatorBounds() {
- Resources res = mContext.getResources();
- int mMaxExpandedWidth = res.getDimensionPixelSize(R.dimen.privacy_chips_max_width);
- int mMaxExpandedHeight = res.getDimensionPixelSize(R.dimen.privacy_chip_height);
- int mChipMarginTotal = 2 * res.getDimensionPixelSize(R.dimen.privacy_chip_margin);
-
- final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
- Rect screenBounds = windowManager.getCurrentWindowMetrics().getBounds();
- mBounds[0] = new Rect(
- mIsRtl ? screenBounds.left
- : screenBounds.right - mMaxExpandedWidth,
- screenBounds.top,
- mIsRtl ? screenBounds.left + mMaxExpandedWidth
- : screenBounds.right,
- screenBounds.top + mChipMarginTotal + mMaxExpandedHeight
- );
-
- if (DEBUG) Log.v(TAG, "privacy indicator bounds: " + mBounds[0].toShortString());
-
- try {
- mIWindowManager.updateStaticPrivacyIndicatorBounds(mContext.getDisplayId(), mBounds);
- } catch (RemoteException e) {
- Log.w(TAG, "could not update privacy indicator bounds");
- }
- }
-
- @UiThread
- private void updateChips() {
- if (DEBUG) Log.d(TAG, "updateChips: " + mPrivacyItems.size() + " privacy items");
-
- if (mChipsContainer == null) {
- if (!mPrivacyItems.isEmpty()) {
- createAndShowIndicator();
- }
- return;
- }
-
- Set<PrivacyType> activePrivacyTypes = new ArraySet<>();
- mPrivacyItems.forEach(item -> activePrivacyTypes.add(item.getPrivacyType()));
-
- TransitionManager.beginDelayedTransition(mChipsContainer, mTransition);
- mChips.forEach(chip -> chip.expandForTypes(activePrivacyTypes));
- }
-
- /**
- * Collapse the chip {@link #EXPANDED_DURATION_MS} from now.
- */
- private void collapseLater() {
- mUiThreadHandler.removeCallbacks(mCollapseRunnable);
- if (DEBUG) Log.d(TAG, "Chips will collapse in " + EXPANDED_DURATION_MS + "ms");
- mUiThreadHandler.postDelayed(mCollapseRunnable, EXPANDED_DURATION_MS);
- }
-
- private void collapseChips() {
- if (DEBUG) Log.d(TAG, "collapseChips");
- if (mChipsContainer == null) {
- return;
- }
-
- boolean hasExpandedChip = false;
- for (PrivacyItemsChip chip : mChips) {
- hasExpandedChip |= chip.isExpanded();
- }
-
- if (mChipsContainer != null && hasExpandedChip) {
- TransitionManager.beginDelayedTransition(mChipsContainer, mCollapseTransition);
- for (PrivacyItemsChip chip : mChips) {
- chip.collapse();
- }
- }
- }
-
- @UiThread
- private void createAndShowIndicator() {
- if (DEBUG) Log.i(TAG, "Creating privacy indicators");
-
- Context privacyChipContext = new ContextThemeWrapper(mContext, R.style.PrivacyChip);
- mChips = new ArrayList<>();
- mChipsContainer = (ViewGroup) LayoutInflater.from(privacyChipContext)
- .inflate(R.layout.tv_privacy_chip_container, null);
-
- int chipMargins = privacyChipContext.getResources()
- .getDimensionPixelSize(R.dimen.privacy_chip_margin);
- LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
- lp.setMarginStart(chipMargins);
- lp.setMarginEnd(chipMargins);
-
- for (PrivacyItemsChip.ChipConfig chipConfig : CHIPS) {
- PrivacyItemsChip chip = new PrivacyItemsChip(privacyChipContext, chipConfig);
- mChipsContainer.addView(chip, lp);
- mChips.add(chip);
- }
-
- final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
- windowManager.addView(mChipsContainer, getWindowLayoutParams());
-
- final ViewGroup container = mChipsContainer;
- mChipsContainer.getViewTreeObserver()
- .addOnGlobalLayoutListener(
- new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- if (DEBUG) Log.v(TAG, "Chips container laid out");
- container.getViewTreeObserver().removeOnGlobalLayoutListener(this);
- updateChips();
- }
- });
- }
-
- private WindowManager.LayoutParams getWindowLayoutParams() {
- final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
- WRAP_CONTENT,
- WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
- PixelFormat.TRANSLUCENT);
- layoutParams.gravity = Gravity.TOP | (mIsRtl ? Gravity.LEFT : Gravity.RIGHT);
- layoutParams.setTitle(LAYOUT_PARAMS_TITLE);
- layoutParams.packageName = mContext.getPackageName();
- return layoutParams;
- }
-
- @UiThread
- private void removeIndicatorView() {
- if (DEBUG) Log.d(TAG, "removeIndicatorView");
- mUiThreadHandler.removeCallbacks(mCollapseRunnable);
-
- final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
- if (windowManager != null && mChipsContainer != null) {
- windowManager.removeView(mChipsContainer);
- }
-
- mChipsContainer = null;
- mChips = null;
- }
-
- /**
- * Schedules the accessibility announcement to be made after {@link
- * #ACCESSIBILITY_ANNOUNCEMENT_DELAY_MS} (if possible). This is so that only one announcement is
- * made instead of two separate ones if both the camera and the mic are started/stopped.
- */
- @UiThread
- private void postAccessibilityAnnouncement() {
- mUiThreadHandler.removeCallbacks(mAccessibilityRunnable);
-
- if (mPrivacyItems.size() == 0) {
- // Announce immediately since announcement cannot be made once the chip is gone.
- makeAccessibilityAnnouncement();
- } else {
- mUiThreadHandler.postDelayed(mAccessibilityRunnable,
- ACCESSIBILITY_ANNOUNCEMENT_DELAY_MS);
- }
- }
-
- private void makeAccessibilityAnnouncement() {
- if (mChipsContainer == null) {
- return;
- }
-
- boolean cameraWasRecording = listContainsPrivacyType(mItemsBeforeLastAnnouncement,
- PrivacyType.TYPE_CAMERA);
- boolean cameraIsRecording = listContainsPrivacyType(mPrivacyItems,
- PrivacyType.TYPE_CAMERA);
- boolean micWasRecording = listContainsPrivacyType(mItemsBeforeLastAnnouncement,
- PrivacyType.TYPE_MICROPHONE);
- boolean micIsRecording = listContainsPrivacyType(mPrivacyItems,
- PrivacyType.TYPE_MICROPHONE);
-
- boolean screenWasRecording = listContainsPrivacyType(mItemsBeforeLastAnnouncement,
- PrivacyType.TYPE_MEDIA_PROJECTION);
- boolean screenIsRecording = listContainsPrivacyType(mPrivacyItems,
- PrivacyType.TYPE_MEDIA_PROJECTION);
-
- int announcement = 0;
- if (!cameraWasRecording && cameraIsRecording && !micWasRecording && micIsRecording) {
- // Both started
- announcement = R.string.mic_and_camera_recording_announcement;
- } else if (cameraWasRecording && !cameraIsRecording && micWasRecording && !micIsRecording) {
- // Both stopped
- announcement = R.string.mic_camera_stopped_recording_announcement;
- } else {
- // Did the camera start or stop?
- if (cameraWasRecording && !cameraIsRecording) {
- announcement = R.string.camera_stopped_recording_announcement;
- } else if (!cameraWasRecording && cameraIsRecording) {
- announcement = R.string.camera_recording_announcement;
- }
-
- // Announce camera changes now since we might need a second announcement about the mic.
- if (announcement != 0) {
- mChipsContainer.announceForAccessibility(mContext.getString(announcement));
- announcement = 0;
- }
-
- // Did the mic start or stop?
- if (micWasRecording && !micIsRecording) {
- announcement = R.string.mic_stopped_recording_announcement;
- } else if (!micWasRecording && micIsRecording) {
- announcement = R.string.mic_recording_announcement;
- }
- }
-
- if (announcement != 0) {
- mChipsContainer.announceForAccessibility(mContext.getString(announcement));
- }
-
- if (!screenWasRecording && screenIsRecording) {
- mChipsContainer.announceForAccessibility(
- mContext.getString(R.string.screen_recording_announcement));
- } else if (screenWasRecording && !screenIsRecording) {
- mChipsContainer.announceForAccessibility(
- mContext.getString(R.string.screen_stopped_recording_announcement));
- }
-
- mItemsBeforeLastAnnouncement.clear();
- mItemsBeforeLastAnnouncement.addAll(mPrivacyItems);
- }
-
- private boolean listContainsPrivacyType(List<PrivacyItem> list, PrivacyType privacyType) {
- for (PrivacyItem item : list) {
- if (item.getPrivacyType() == privacyType) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
index 995c6a4..33c47cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
@@ -26,7 +26,7 @@
import javax.inject.Inject
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.shade.ShadeModule.Companion.SHADE_HEADER
+import com.android.systemui.shade.ShadeViewProviderModule.Companion.SHADE_HEADER
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import javax.inject.Named
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ChevronImageView.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ChevronImageView.kt
new file mode 100644
index 0000000..8fd1d6f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ChevronImageView.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tileimpl
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.ImageView
+
+class ChevronImageView(context: Context, attrs: AttributeSet?) : ImageView(context, attrs) {
+
+ override fun resolveLayoutDirection(): Boolean {
+ val previousLayoutDirection = layoutDirection
+ return super.resolveLayoutDirection().also { resolved ->
+ if (resolved && layoutDirection != previousLayoutDirection) {
+ onRtlPropertiesChanged(layoutDirection)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index 5aa5fee..f9324a9 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -16,19 +16,25 @@
package com.android.systemui.scene.ui.view
+import android.view.Gravity
+import android.view.View
import android.view.ViewGroup
+import android.widget.FrameLayout
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
+import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
import com.android.systemui.compose.ComposeFacade
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
+import java.time.Instant
import kotlinx.coroutines.launch
object SceneWindowRootViewBinder {
@@ -77,6 +83,9 @@
)
)
+ val legacyView = view.requireViewById<View>(R.id.legacy_window_root)
+ view.addView(createVisibilityToggleView(legacyView))
+
launch {
viewModel.isVisible.collect { isVisible ->
onVisibilityChangedInternal(isVisible)
@@ -89,4 +98,28 @@
}
}
}
+
+ private var clickCount = 0
+ private var lastClick = Instant.now()
+
+ /**
+ * A temporary UI to toggle on/off the visibility of the given [otherView]. It is toggled by
+ * tapping 5 times in quick succession on the device camera (top center).
+ */
+ // TODO(b/291321285): Remove this when the Flexiglass UI is mature enough to turn off legacy
+ // SysUI altogether.
+ private fun createVisibilityToggleView(otherView: View): View {
+ val toggleView = View(otherView.context)
+ toggleView.layoutParams = FrameLayout.LayoutParams(200, 200, Gravity.CENTER_HORIZONTAL)
+ toggleView.setOnClickListener {
+ val now = Instant.now()
+ clickCount = if (now.minusSeconds(2) > lastClick) 1 else clickCount + 1
+ if (clickCount == 5) {
+ otherView.isVisible = !otherView.isVisible
+ clickCount = 0
+ }
+ lastClick = now
+ }
+ return toggleView
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvSensorPrivacyChangedActivity.java b/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvSensorPrivacyChangedActivity.java
deleted file mode 100644
index 731b177..0000000
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvSensorPrivacyChangedActivity.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * 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.sensorprivacy.television;
-
-import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
-import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
-
-import android.annotation.DimenRes;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.drawable.Animatable;
-import android.graphics.drawable.Drawable;
-import android.hardware.SensorPrivacyManager;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
-import com.android.systemui.tv.TvBottomSheetActivity;
-import com.android.systemui.util.settings.GlobalSettings;
-
-import javax.inject.Inject;
-
-/**
- * Bottom sheet that is shown when the camera/mic sensors privacy state changed
- * by the global software toggle or physical privacy switch.
- */
-public class TvSensorPrivacyChangedActivity extends TvBottomSheetActivity {
-
- private static final String TAG = TvSensorPrivacyChangedActivity.class.getSimpleName();
-
- private static final int ALL_SENSORS = Integer.MAX_VALUE;
-
- private int mSensor = -1;
- private int mToggleType = -1;
-
- private final GlobalSettings mGlobalSettings;
- private final IndividualSensorPrivacyController mSensorPrivacyController;
- private IndividualSensorPrivacyController.Callback mSensorPrivacyCallback;
- private TextView mTitle;
- private TextView mContent;
- private ImageView mIcon;
- private ImageView mSecondIcon;
- private Button mPositiveButton;
- private Button mCancelButton;
-
- @Inject
- public TvSensorPrivacyChangedActivity(
- IndividualSensorPrivacyController individualSensorPrivacyController,
- GlobalSettings globalSettings) {
- mSensorPrivacyController = individualSensorPrivacyController;
- mGlobalSettings = globalSettings;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- getWindow().addSystemFlags(
- WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
-
- boolean allSensors = getIntent().getBooleanExtra(SensorPrivacyManager.EXTRA_ALL_SENSORS,
- false);
- if (allSensors) {
- mSensor = ALL_SENSORS;
- } else {
- mSensor = getIntent().getIntExtra(SensorPrivacyManager.EXTRA_SENSOR, -1);
- }
-
- mToggleType = getIntent().getIntExtra(SensorPrivacyManager.EXTRA_TOGGLE_TYPE, -1);
-
- if (mSensor == -1 || mToggleType == -1) {
- Log.v(TAG, "Invalid extras");
- finish();
- return;
- }
-
- // Do not show for software toggles
- if (mToggleType == SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE) {
- finish();
- return;
- }
-
- mSensorPrivacyCallback = (sensor, blocked) -> {
- updateUI();
- };
-
- initUI();
- }
-
- private void initUI() {
- mTitle = findViewById(R.id.bottom_sheet_title);
- mContent = findViewById(R.id.bottom_sheet_body);
- mIcon = findViewById(R.id.bottom_sheet_icon);
- // mic icon if both icons are shown
- mSecondIcon = findViewById(R.id.bottom_sheet_second_icon);
- mPositiveButton = findViewById(R.id.bottom_sheet_positive_button);
- mCancelButton = findViewById(R.id.bottom_sheet_negative_button);
-
- mCancelButton.setText(android.R.string.cancel);
- mCancelButton.setOnClickListener(v -> finish());
-
- updateUI();
- }
-
- private void updateUI() {
- final Resources resources = getResources();
- setIconTint(resources.getBoolean(R.bool.config_unblockHwSensorIconEnableTint));
- setIconSize(R.dimen.unblock_hw_sensor_icon_width, R.dimen.unblock_hw_sensor_icon_height);
-
- switch (mSensor) {
- case CAMERA:
- updateUiForCameraUpdate(
- mSensorPrivacyController.isSensorBlockedByHardwareToggle(CAMERA));
- break;
- case MICROPHONE:
- default:
- updateUiForMicUpdate(
- mSensorPrivacyController.isSensorBlockedByHardwareToggle(MICROPHONE));
- break;
- }
-
- // Start animation if drawable is animated
- Drawable iconDrawable = mIcon.getDrawable();
- if (iconDrawable instanceof Animatable) {
- ((Animatable) iconDrawable).start();
- }
-
- mPositiveButton.setVisibility(View.GONE);
- mCancelButton.setText(android.R.string.ok);
- }
-
- private void updateUiForMicUpdate(boolean blocked) {
- if (blocked) {
- mTitle.setText(R.string.sensor_privacy_mic_turned_off_dialog_title);
- if (isExplicitUserInteractionAudioBypassAllowed()) {
- mContent.setText(R.string.sensor_privacy_mic_blocked_with_exception_dialog_content);
- } else {
- mContent.setText(R.string.sensor_privacy_mic_blocked_no_exception_dialog_content);
- }
- mIcon.setImageResource(R.drawable.unblock_hw_sensor_microphone);
- mSecondIcon.setVisibility(View.GONE);
- } else {
- mTitle.setText(R.string.sensor_privacy_mic_turned_on_dialog_title);
- mContent.setText(R.string.sensor_privacy_mic_unblocked_dialog_content);
- mIcon.setImageResource(com.android.internal.R.drawable.ic_mic_allowed);
- mSecondIcon.setVisibility(View.GONE);
- }
- }
-
- private void updateUiForCameraUpdate(boolean blocked) {
- if (blocked) {
- mTitle.setText(R.string.sensor_privacy_camera_turned_off_dialog_title);
- mContent.setText(R.string.sensor_privacy_camera_blocked_dialog_content);
- mIcon.setImageResource(R.drawable.unblock_hw_sensor_camera);
- mSecondIcon.setVisibility(View.GONE);
- } else {
- mTitle.setText(R.string.sensor_privacy_camera_turned_on_dialog_title);
- mContent.setText(R.string.sensor_privacy_camera_unblocked_dialog_content);
- mIcon.setImageResource(com.android.internal.R.drawable.ic_camera_allowed);
- mSecondIcon.setVisibility(View.GONE);
- }
- }
-
- private void setIconTint(boolean enableTint) {
- final Resources resources = getResources();
-
- if (enableTint) {
- final ColorStateList iconTint = resources.getColorStateList(
- R.color.bottom_sheet_icon_color, getTheme());
- mIcon.setImageTintList(iconTint);
- mSecondIcon.setImageTintList(iconTint);
- } else {
- mIcon.setImageTintList(null);
- mSecondIcon.setImageTintList(null);
- }
-
- mIcon.invalidate();
- mSecondIcon.invalidate();
- }
-
- private void setIconSize(@DimenRes int widthRes, @DimenRes int heightRes) {
- final Resources resources = getResources();
- final int iconWidth = resources.getDimensionPixelSize(widthRes);
- final int iconHeight = resources.getDimensionPixelSize(heightRes);
-
- mIcon.getLayoutParams().width = iconWidth;
- mIcon.getLayoutParams().height = iconHeight;
- mIcon.invalidate();
-
- mSecondIcon.getLayoutParams().width = iconWidth;
- mSecondIcon.getLayoutParams().height = iconHeight;
- mSecondIcon.invalidate();
- }
-
- private boolean isExplicitUserInteractionAudioBypassAllowed() {
- return mGlobalSettings.getInt(
- Settings.Global.RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO_ENABLED, 1) == 1;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- updateUI();
- mSensorPrivacyController.addCallback(mSensorPrivacyCallback);
- }
-
- @Override
- public void onPause() {
- mSensorPrivacyController.removeCallback(mSensorPrivacyCallback);
- super.onPause();
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java b/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java
deleted file mode 100644
index 1b9657f..0000000
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.sensorprivacy.television;
-
-import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
-import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
-import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
-import static android.hardware.SensorPrivacyManager.Sources.OTHER;
-
-import android.annotation.DimenRes;
-import android.app.AppOpsManager;
-import android.app.role.RoleManager;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.drawable.Animatable;
-import android.graphics.drawable.Drawable;
-import android.hardware.SensorPrivacyManager;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.util.Log;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
-import com.android.systemui.tv.TvBottomSheetActivity;
-
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * Bottom sheet that is shown when the camera/mic sensors are blocked by the global software toggle
- * or physical privacy switch.
- */
-public class TvUnblockSensorActivity extends TvBottomSheetActivity {
-
- private static final String TAG = TvUnblockSensorActivity.class.getSimpleName();
- private static final String ACTION_MANAGE_CAMERA_PRIVACY =
- "android.settings.MANAGE_CAMERA_PRIVACY";
- private static final String ACTION_MANAGE_MICROPHONE_PRIVACY =
- "android.settings.MANAGE_MICROPHONE_PRIVACY";
-
- private static final int ALL_SENSORS = Integer.MAX_VALUE;
-
- private int mSensor = -1;
-
- private final AppOpsManager mAppOpsManager;
- private final RoleManager mRoleManager;
- private final IndividualSensorPrivacyController mSensorPrivacyController;
- private IndividualSensorPrivacyController.Callback mSensorPrivacyCallback;
- private TextView mTitle;
- private TextView mContent;
- private ImageView mIcon;
- private ImageView mSecondIcon;
- private Button mPositiveButton;
- private Button mCancelButton;
-
- @Inject
- public TvUnblockSensorActivity(
- IndividualSensorPrivacyController individualSensorPrivacyController,
- AppOpsManager appOpsManager, RoleManager roleManager) {
- mSensorPrivacyController = individualSensorPrivacyController;
- mAppOpsManager = appOpsManager;
- mRoleManager = roleManager;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- getWindow().addSystemFlags(
- WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
-
- boolean allSensors = getIntent().getBooleanExtra(SensorPrivacyManager.EXTRA_ALL_SENSORS,
- false);
- if (allSensors) {
- mSensor = ALL_SENSORS;
- } else {
- mSensor = getIntent().getIntExtra(SensorPrivacyManager.EXTRA_SENSOR, -1);
- }
-
- if (mSensor == -1) {
- Log.v(TAG, "Invalid extras");
- finish();
- return;
- }
-
- mSensorPrivacyCallback = (sensor, blocked) -> {
- if (mSensor == ALL_SENSORS && !mSensorPrivacyController.isSensorBlocked(CAMERA)
- && !mSensorPrivacyController.isSensorBlocked(MICROPHONE)) {
- showToastAndFinish();
- } else if (this.mSensor == sensor && !blocked) {
- showToastAndFinish();
- } else {
- updateUI();
- }
- };
-
- initUI();
- }
-
- private void showToastAndFinish() {
- final int toastMsgResId;
- switch(mSensor) {
- case MICROPHONE:
- toastMsgResId = R.string.sensor_privacy_mic_unblocked_toast_content;
- break;
- case CAMERA:
- toastMsgResId = R.string.sensor_privacy_camera_unblocked_toast_content;
- break;
- case ALL_SENSORS:
- default:
- toastMsgResId = R.string.sensor_privacy_mic_camera_unblocked_toast_content;
- break;
- }
- showToastAndFinish(toastMsgResId);
- }
-
- private void showToastAndFinish(int toastMsgResId) {
- Toast.makeText(this, toastMsgResId, Toast.LENGTH_SHORT).show();
- finish();
- }
-
- private boolean isBlockedByHardwareToggle() {
- if (mSensor == ALL_SENSORS) {
- return mSensorPrivacyController.isSensorBlockedByHardwareToggle(CAMERA)
- || mSensorPrivacyController.isSensorBlockedByHardwareToggle(MICROPHONE);
- } else {
- return mSensorPrivacyController.isSensorBlockedByHardwareToggle(mSensor);
- }
- }
-
- private void initUI() {
- mTitle = findViewById(R.id.bottom_sheet_title);
- mContent = findViewById(R.id.bottom_sheet_body);
- mIcon = findViewById(R.id.bottom_sheet_icon);
- // mic icon if both icons are shown
- mSecondIcon = findViewById(R.id.bottom_sheet_second_icon);
- mPositiveButton = findViewById(R.id.bottom_sheet_positive_button);
- mCancelButton = findViewById(R.id.bottom_sheet_negative_button);
-
- mCancelButton.setText(android.R.string.cancel);
- mCancelButton.setOnClickListener(v -> finish());
-
- updateUI();
- }
-
- private void updateUI() {
- if (isHTTAccessDisabled()) {
- updateUiForHTT();
- } else if (isBlockedByHardwareToggle()) {
- updateUiForHardwareToggle();
- } else {
- updateUiForSoftwareToggle();
- }
- }
-
- private void updateUiForHardwareToggle() {
- final Resources resources = getResources();
-
- boolean micBlocked = (mSensor == MICROPHONE || mSensor == ALL_SENSORS)
- && mSensorPrivacyController.isSensorBlockedByHardwareToggle(MICROPHONE);
- boolean cameraBlocked = (mSensor == CAMERA || mSensor == ALL_SENSORS)
- && mSensorPrivacyController.isSensorBlockedByHardwareToggle(CAMERA);
-
- setIconTint(resources.getBoolean(R.bool.config_unblockHwSensorIconEnableTint));
- setIconSize(R.dimen.unblock_hw_sensor_icon_width, R.dimen.unblock_hw_sensor_icon_height);
-
- if (micBlocked && cameraBlocked) {
- mTitle.setText(R.string.sensor_privacy_start_use_mic_camera_blocked_dialog_title);
- mContent.setText(
- R.string.sensor_privacy_start_use_mic_camera_blocked_dialog_content);
- mIcon.setImageResource(R.drawable.unblock_hw_sensor_all);
-
- Drawable secondIconDrawable = resources.getDrawable(
- R.drawable.unblock_hw_sensor_all_second, getTheme());
- if (secondIconDrawable == null) {
- mSecondIcon.setVisibility(View.GONE);
- } else {
- mSecondIcon.setImageDrawable(secondIconDrawable);
- }
- } else if (cameraBlocked) {
- mTitle.setText(R.string.sensor_privacy_start_use_camera_blocked_dialog_title);
- mContent.setText(R.string.sensor_privacy_start_use_camera_blocked_dialog_content);
- mIcon.setImageResource(R.drawable.unblock_hw_sensor_camera);
- mSecondIcon.setVisibility(View.GONE);
- } else if (micBlocked) {
- mTitle.setText(R.string.sensor_privacy_start_use_mic_blocked_dialog_title);
- mContent.setText(R.string.sensor_privacy_start_use_mic_blocked_dialog_content);
- mIcon.setImageResource(R.drawable.unblock_hw_sensor_microphone);
- mSecondIcon.setVisibility(View.GONE);
- }
-
- // Start animation if drawable is animated
- Drawable iconDrawable = mIcon.getDrawable();
- if (iconDrawable instanceof Animatable) {
- ((Animatable) iconDrawable).start();
- }
-
- mPositiveButton.setVisibility(View.GONE);
- mCancelButton.setText(android.R.string.ok);
- }
-
- private void updateUiForSoftwareToggle() {
- setIconTint(true);
- setIconSize(R.dimen.bottom_sheet_icon_size, R.dimen.bottom_sheet_icon_size);
-
- switch (mSensor) {
- case MICROPHONE:
- mTitle.setText(R.string.sensor_privacy_start_use_mic_blocked_dialog_title);
- mContent.setText(R.string.sensor_privacy_start_use_mic_dialog_content);
- mIcon.setImageResource(com.android.internal.R.drawable.perm_group_microphone);
- mSecondIcon.setVisibility(View.GONE);
- break;
- case CAMERA:
- mTitle.setText(R.string.sensor_privacy_start_use_camera_blocked_dialog_title);
- mContent.setText(R.string.sensor_privacy_start_use_camera_dialog_content);
- mIcon.setImageResource(com.android.internal.R.drawable.perm_group_camera);
- mSecondIcon.setVisibility(View.GONE);
- break;
- case ALL_SENSORS:
- default:
- mTitle.setText(R.string.sensor_privacy_start_use_mic_camera_blocked_dialog_title);
- mContent.setText(R.string.sensor_privacy_start_use_mic_camera_dialog_content);
- mIcon.setImageResource(com.android.internal.R.drawable.perm_group_camera);
- mSecondIcon.setImageResource(
- com.android.internal.R.drawable.perm_group_microphone);
- break;
- }
-
- mPositiveButton.setText(
- com.android.internal.R.string.sensor_privacy_start_use_dialog_turn_on_button);
- mPositiveButton.setOnClickListener(v -> {
- if (mSensor == ALL_SENSORS) {
- mSensorPrivacyController.setSensorBlocked(OTHER, CAMERA, false);
- mSensorPrivacyController.setSensorBlocked(OTHER, MICROPHONE, false);
- } else {
- mSensorPrivacyController.setSensorBlocked(OTHER, mSensor, false);
- }
- });
- }
-
- private void updateUiForHTT() {
- setIconTint(true);
- setIconSize(R.dimen.bottom_sheet_icon_size, R.dimen.bottom_sheet_icon_size);
-
- mTitle.setText(R.string.sensor_privacy_start_use_mic_blocked_dialog_title);
- mContent.setText(R.string.sensor_privacy_htt_blocked_dialog_content);
- mIcon.setImageResource(com.android.internal.R.drawable.perm_group_microphone);
- mSecondIcon.setVisibility(View.GONE);
-
- mPositiveButton.setText(R.string.sensor_privacy_dialog_open_settings);
- mPositiveButton.setOnClickListener(v -> {
- Intent openPrivacySettings = new Intent(ACTION_MANAGE_MICROPHONE_PRIVACY);
- ActivityInfo activityInfo = openPrivacySettings.resolveActivityInfo(getPackageManager(),
- MATCH_SYSTEM_ONLY);
- if (activityInfo == null) {
- showToastAndFinish(com.android.internal.R.string.noApplications);
- } else {
- startActivity(openPrivacySettings);
- finish();
- }
- });
- }
-
- private void setIconTint(boolean enableTint) {
- final Resources resources = getResources();
-
- if (enableTint) {
- final ColorStateList iconTint = resources.getColorStateList(
- R.color.bottom_sheet_icon_color, getTheme());
- mIcon.setImageTintList(iconTint);
- mSecondIcon.setImageTintList(iconTint);
- } else {
- mIcon.setImageTintList(null);
- mSecondIcon.setImageTintList(null);
- }
-
- mIcon.invalidate();
- mSecondIcon.invalidate();
- }
-
- private void setIconSize(@DimenRes int widthRes, @DimenRes int heightRes) {
- final Resources resources = getResources();
- final int iconWidth = resources.getDimensionPixelSize(widthRes);
- final int iconHeight = resources.getDimensionPixelSize(heightRes);
-
- mIcon.getLayoutParams().width = iconWidth;
- mIcon.getLayoutParams().height = iconHeight;
- mIcon.invalidate();
-
- mSecondIcon.getLayoutParams().width = iconWidth;
- mSecondIcon.getLayoutParams().height = iconHeight;
- mSecondIcon.invalidate();
- }
-
- private boolean isHTTAccessDisabled() {
- String pkg = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
- List<String> assistantPkgs = mRoleManager.getRoleHolders(RoleManager.ROLE_ASSISTANT);
- if (!assistantPkgs.contains(pkg)) {
- return false;
- }
-
- return (mAppOpsManager.checkOpNoThrow(
- AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO, UserHandle.myUserId(),
- pkg) != AppOpsManager.MODE_ALLOWED);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- updateUI();
- mSensorPrivacyController.addCallback(mSensorPrivacyCallback);
- }
-
- @Override
- public void onPause() {
- mSensorPrivacyController.removeCallback(mSensorPrivacyCallback);
- super.onPause();
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
index bb7f721..468a75d 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
@@ -52,12 +52,12 @@
interface Callback {
/** Notifies that a display has been added. */
- @JvmDefault fun onDisplayAdded(displayId: Int) {}
+ fun onDisplayAdded(displayId: Int) {}
/** Notifies that a display has been removed. */
- @JvmDefault fun onDisplayRemoved(displayId: Int) {}
+ fun onDisplayRemoved(displayId: Int) {}
/** Notifies a display has been changed */
- @JvmDefault fun onDisplayChanged(displayId: Int) {}
+ fun onDisplayChanged(displayId: Int) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index 33a3125..93a3e90 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -71,7 +71,6 @@
* Same as {@link onUserChanging(Int, Context, CountDownLatch)} but the latch will be
* auto-decremented after the completion of this method.
*/
- @JvmDefault
fun onUserChanging(newUser: Int, userContext: Context) {}
/**
@@ -82,7 +81,6 @@
* user switch duration. When overriding this method, countDown() MUST be called on the
* latch once execution is complete.
*/
- @JvmDefault
fun onUserChanging(newUser: Int, userContext: Context, latch: CountDownLatch) {
onUserChanging(newUser, userContext)
latch.countDown()
@@ -93,13 +91,11 @@
* Override this method to run things after the screen is unfrozen for the user switch.
* Please see {@link #onUserChanging} if you need to hide jank.
*/
- @JvmDefault
fun onUserChanged(newUser: Int, userContext: Context) {}
/**
* Notifies that the current user's profiles have changed.
*/
- @JvmDefault
fun onProfilesChanged(profiles: List<@JvmSuppressWildcards UserInfo>) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 9399d48..202d6e6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1169,6 +1169,9 @@
private void updateViewControllers(KeyguardStatusView keyguardStatusView,
FrameLayout userAvatarView,
KeyguardUserSwitcherView keyguardUserSwitcherView) {
+ if (mKeyguardStatusViewController != null) {
+ mKeyguardStatusViewController.onDestroy();
+ }
// Re-associate the KeyguardStatusViewController
KeyguardStatusViewComponent statusViewComponent =
mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index bb2be66..b328c55 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -543,7 +543,6 @@
state.forceUserActivity,
state.launchingActivityFromNotification,
state.mediaBackdropShowing,
- state.wallpaperSupportsAmbientMode,
state.windowNotTouchable,
state.componentsForcingTopUi,
state.forceOpenTokens,
@@ -734,12 +733,6 @@
apply(mCurrentState);
}
- @Override
- public void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {
- mCurrentState.wallpaperSupportsAmbientMode = supportsAmbientMode;
- apply(mCurrentState);
- }
-
/**
* @param state The {@link StatusBarStateController} of the status bar.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
index 7812f07..d252943 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
@@ -46,7 +46,6 @@
@JvmField var forceUserActivity: Boolean = false,
@JvmField var launchingActivityFromNotification: Boolean = false,
@JvmField var mediaBackdropShowing: Boolean = false,
- @JvmField var wallpaperSupportsAmbientMode: Boolean = false,
@JvmField var windowNotTouchable: Boolean = false,
@JvmField var componentsForcingTopUi: MutableSet<String> = mutableSetOf(),
@JvmField var forceOpenTokens: MutableSet<Any> = mutableSetOf(),
@@ -84,7 +83,6 @@
forceUserActivity.toString(),
launchingActivityFromNotification.toString(),
mediaBackdropShowing.toString(),
- wallpaperSupportsAmbientMode.toString(),
windowNotTouchable.toString(),
componentsForcingTopUi.toString(),
forceOpenTokens.toString(),
@@ -124,7 +122,6 @@
forceUserActivity: Boolean,
launchingActivity: Boolean,
backdropShowing: Boolean,
- wallpaperSupportsAmbientMode: Boolean,
notTouchable: Boolean,
componentsForcingTopUi: MutableSet<String>,
forceOpenTokens: MutableSet<Any>,
@@ -153,7 +150,6 @@
this.forceUserActivity = forceUserActivity
this.launchingActivityFromNotification = launchingActivity
this.mediaBackdropShowing = backdropShowing
- this.wallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode
this.windowNotTouchable = notTouchable
this.componentsForcingTopUi.clear()
this.componentsForcingTopUi.addAll(componentsForcingTopUi)
@@ -200,7 +196,6 @@
"forceUserActivity",
"launchingActivity",
"backdropShowing",
- "wallpaperSupportsAmbientMode",
"notTouchable",
"componentsForcingTopUi",
"forceOpenTokens",
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
new file mode 100644
index 0000000..7a803867
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+
+/** Fulfills dependencies on the shade with empty implementations for variants with no shade. */
+@Module
+abstract class ShadeEmptyImplModule {
+ @Binds
+ @SysUISingleton
+ abstract fun bindsShadeViewController(svc: ShadeViewControllerEmptyImpl): ShadeViewController
+
+ @Binds
+ @SysUISingleton
+ abstract fun bindsShadeController(sc: ShadeControllerEmptyImpl): ShadeController
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index 8b89ff4..529f12e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -54,7 +54,7 @@
import com.android.systemui.shade.ShadeHeaderController.Companion.LARGE_SCREEN_HEADER_TRANSITION_ID
import com.android.systemui.shade.ShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT
import com.android.systemui.shade.ShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
-import com.android.systemui.shade.ShadeModule.Companion.SHADE_HEADER
+import com.android.systemui.shade.ShadeViewProviderModule.Companion.SHADE_HEADER
import com.android.systemui.shade.carrier.ShadeCarrierGroup
import com.android.systemui.shade.carrier.ShadeCarrierGroupController
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index be21df1..89aaaaf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -16,293 +16,21 @@
package com.android.systemui.shade
-import android.annotation.SuppressLint
-import android.content.ContentResolver
-import android.os.Handler
-import android.view.LayoutInflater
-import android.view.ViewStub
-import androidx.constraintlayout.motion.widget.MotionLayout
-import com.android.keyguard.LockIconView
-import com.android.systemui.CoreStartable
-import com.android.systemui.R
-import com.android.systemui.battery.BatteryMeterView
-import com.android.systemui.battery.BatteryMeterViewController
-import com.android.systemui.biometrics.AuthRippleController
-import com.android.systemui.biometrics.AuthRippleView
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.privacy.OngoingPrivacyChip
-import com.android.systemui.scene.ui.view.WindowRootView
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.LightRevealScrim
-import com.android.systemui.statusbar.NotificationShelf
-import com.android.systemui.statusbar.NotificationShelfController
-import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent
-import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
-import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
-import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
-import com.android.systemui.statusbar.phone.StatusBarLocation
-import com.android.systemui.statusbar.phone.StatusIconContainer
-import com.android.systemui.statusbar.phone.TapAgainView
-import com.android.systemui.statusbar.policy.BatteryController
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.tuner.TunerService
+
import dagger.Binds
import dagger.Module
-import dagger.Provides
-import dagger.multibindings.ClassKey
-import dagger.multibindings.IntoMap
-import javax.inject.Named
-import javax.inject.Provider
/** Module for classes related to the notification shade. */
-@Module(includes = [StartShadeModule::class])
+@Module(includes = [StartShadeModule::class, ShadeViewProviderModule::class])
abstract class ShadeModule {
-
- @Binds
- @IntoMap
- @ClassKey(AuthRippleController::class)
- abstract fun bindAuthRippleController(controller: AuthRippleController): CoreStartable
-
@Binds
@SysUISingleton
abstract fun bindsShadeViewController(
notificationPanelViewController: NotificationPanelViewController
): ShadeViewController
- companion object {
- const val SHADE_HEADER = "large_screen_shade_header"
-
- @SuppressLint("InflateParams") // Root views don't have parents.
- @Provides
- @SysUISingleton
- fun providesWindowRootView(
- layoutInflater: LayoutInflater,
- featureFlags: FeatureFlags,
- ): WindowRootView {
- return if (
- featureFlags.isEnabled(Flags.SCENE_CONTAINER) && ComposeFacade.isComposeAvailable()
- ) {
- layoutInflater.inflate(R.layout.scene_window_root, null)
- } else {
- layoutInflater.inflate(R.layout.super_notification_shade, null)
- }
- as WindowRootView?
- ?: throw IllegalStateException("Window root view could not be properly inflated")
- }
-
- @Provides
- @SysUISingleton
- // TODO(b/277762009): Do something similar to
- // {@link StatusBarWindowModule.InternalWindowView} so that only
- // {@link NotificationShadeWindowViewController} can inject this view.
- fun providesNotificationShadeWindowView(
- root: WindowRootView,
- featureFlags: FeatureFlags,
- ): NotificationShadeWindowView {
- if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
- return root.findViewById(R.id.legacy_window_root)
- }
- return root as NotificationShadeWindowView?
- ?: throw IllegalStateException("root view not a NotificationShadeWindowView")
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
- fun providesNotificationStackScrollLayout(
- notificationShadeWindowView: NotificationShadeWindowView,
- ): NotificationStackScrollLayout {
- return notificationShadeWindowView.findViewById(R.id.notification_stack_scroller)
- }
-
- @Provides
- @SysUISingleton
- fun providesNotificationShelfController(
- featureFlags: FeatureFlags,
- newImpl: Provider<NotificationShelfViewBinderWrapperControllerImpl>,
- notificationShelfComponentBuilder: NotificationShelfComponent.Builder,
- layoutInflater: LayoutInflater,
- notificationStackScrollLayout: NotificationStackScrollLayout,
- ): NotificationShelfController {
- return if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
- newImpl.get()
- } else {
- val shelfView =
- layoutInflater.inflate(
- R.layout.status_bar_notification_shelf,
- notificationStackScrollLayout,
- false
- ) as NotificationShelf
- val component =
- notificationShelfComponentBuilder.notificationShelf(shelfView).build()
- val notificationShelfController = component.notificationShelfController
- notificationShelfController.init()
- notificationShelfController
- }
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
- fun providesNotificationPanelView(
- notificationShadeWindowView: NotificationShadeWindowView,
- ): NotificationPanelView {
- return notificationShadeWindowView.findViewById(R.id.notification_panel)
- }
-
- /**
- * Constructs a new, unattached [KeyguardBottomAreaView].
- *
- * Note that this is explicitly _not_ a singleton, as we want to be able to reinflate it
- */
- @Provides
- fun providesKeyguardBottomAreaView(
- npv: NotificationPanelView,
- layoutInflater: LayoutInflater,
- ): KeyguardBottomAreaView {
- return layoutInflater.inflate(R.layout.keyguard_bottom_area, npv, false)
- as KeyguardBottomAreaView
- }
-
- @Provides
- @SysUISingleton
- fun providesLightRevealScrim(
- notificationShadeWindowView: NotificationShadeWindowView,
- ): LightRevealScrim {
- return notificationShadeWindowView.findViewById(R.id.light_reveal_scrim)
- }
-
- @Provides
- @SysUISingleton
- fun providesKeyguardRootView(
- notificationShadeWindowView: NotificationShadeWindowView,
- ): KeyguardRootView {
- return notificationShadeWindowView.findViewById(R.id.keyguard_root_view)
- }
-
- @Provides
- @SysUISingleton
- fun providesSharedNotificationContainer(
- notificationShadeWindowView: NotificationShadeWindowView,
- ): SharedNotificationContainer {
- return notificationShadeWindowView.findViewById(R.id.shared_notification_container)
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
- fun providesAuthRippleView(
- notificationShadeWindowView: NotificationShadeWindowView,
- ): AuthRippleView? {
- return notificationShadeWindowView.findViewById(R.id.auth_ripple)
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
- fun providesLockIconView(
- keyguardRootView: KeyguardRootView,
- notificationPanelView: NotificationPanelView,
- featureFlags: FeatureFlags
- ): LockIconView {
- if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
- return keyguardRootView.findViewById(R.id.lock_icon_view)
- } else {
- return notificationPanelView.findViewById(R.id.lock_icon_view)
- }
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
- fun providesTapAgainView(
- notificationPanelView: NotificationPanelView,
- ): TapAgainView {
- return notificationPanelView.findViewById(R.id.shade_falsing_tap_again)
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
- fun providesNotificationsQuickSettingsContainer(
- notificationShadeWindowView: NotificationShadeWindowView,
- ): NotificationsQuickSettingsContainer {
- return notificationShadeWindowView.findViewById(R.id.notification_container_parent)
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
- @Named(SHADE_HEADER)
- fun providesShadeHeaderView(
- notificationShadeWindowView: NotificationShadeWindowView,
- ): MotionLayout {
- val stub = notificationShadeWindowView.findViewById<ViewStub>(R.id.qs_header_stub)
- val layoutId = R.layout.combined_qs_header
- stub.layoutResource = layoutId
- return stub.inflate() as MotionLayout
- }
-
- @Provides
- @SysUISingleton
- fun providesCombinedShadeHeadersConstraintManager(): CombinedShadeHeadersConstraintManager {
- return CombinedShadeHeadersConstraintManagerImpl
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
- @Named(SHADE_HEADER)
- fun providesBatteryMeterView(@Named(SHADE_HEADER) view: MotionLayout): BatteryMeterView {
- return view.findViewById(R.id.batteryRemainingIcon)
- }
-
- @Provides
- @SysUISingleton
- @Named(SHADE_HEADER)
- fun providesBatteryMeterViewController(
- @Named(SHADE_HEADER) batteryMeterView: BatteryMeterView,
- userTracker: UserTracker,
- configurationController: ConfigurationController,
- tunerService: TunerService,
- @Main mainHandler: Handler,
- contentResolver: ContentResolver,
- batteryController: BatteryController,
- ): BatteryMeterViewController {
- return BatteryMeterViewController(
- batteryMeterView,
- StatusBarLocation.QS,
- userTracker,
- configurationController,
- tunerService,
- mainHandler,
- contentResolver,
- batteryController,
- )
- }
-
- @Provides
- @SysUISingleton
- @Named(SHADE_HEADER)
- fun providesOngoingPrivacyChip(
- @Named(SHADE_HEADER) header: MotionLayout,
- ): OngoingPrivacyChip {
- return header.findViewById(R.id.privacy_chip)
- }
-
- @Provides
- @SysUISingleton
- @Named(SHADE_HEADER)
- fun providesStatusIconContainer(
- @Named(SHADE_HEADER) header: MotionLayout,
- ): StatusIconContainer {
- return header.findViewById(R.id.statusIcons)
- }
- }
+ @Binds
+ @SysUISingleton
+ abstract fun bindsShadeController(shadeControllerImpl: ShadeControllerImpl): ShadeController
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
index 56bb1a6..5804040 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
@@ -29,12 +29,12 @@
interface ShadeStateEventsListener {
/** Invoked when the notification panel starts or stops collapsing. */
- @JvmDefault fun onPanelCollapsingChanged(isCollapsing: Boolean) {}
+ fun onPanelCollapsingChanged(isCollapsing: Boolean) {}
/**
* Invoked when the notification panel starts or stops launching an [android.app.Activity].
*/
- @JvmDefault fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) {}
+ fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) {}
/**
* Invoked when the "expand immediate" attribute changes.
@@ -45,6 +45,6 @@
* Another example is when full QS is showing, and we swipe up from the bottom. Instead of
* going to QQS, the panel fully collapses.
*/
- @JvmDefault fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {}
+ fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
new file mode 100644
index 0000000..fc6479e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+import android.annotation.SuppressLint
+import android.content.ContentResolver
+import android.os.Handler
+import android.view.LayoutInflater
+import android.view.ViewStub
+import androidx.constraintlayout.motion.widget.MotionLayout
+import com.android.keyguard.LockIconView
+import com.android.systemui.R
+import com.android.systemui.battery.BatteryMeterView
+import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.biometrics.AuthRippleView
+import com.android.systemui.compose.ComposeFacade
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.shared.model.SceneContainerConfig
+import com.android.systemui.scene.shared.model.SceneContainerNames
+import com.android.systemui.scene.ui.view.SceneWindowRootView
+import com.android.systemui.scene.ui.view.WindowRootView
+import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.statusbar.NotificationShelf
+import com.android.systemui.statusbar.NotificationShelfController
+import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent
+import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
+import com.android.systemui.statusbar.phone.StatusBarLocation
+import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.statusbar.phone.TapAgainView
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.tuner.TunerService
+import dagger.Module
+import dagger.Provides
+import javax.inject.Named
+import javax.inject.Provider
+
+/** Module for providing views related to the shade. */
+@Module
+abstract class ShadeViewProviderModule {
+ companion object {
+ const val SHADE_HEADER = "large_screen_shade_header"
+
+ @SuppressLint("InflateParams") // Root views don't have parents.
+ @Provides
+ @SysUISingleton
+ fun providesWindowRootView(
+ layoutInflater: LayoutInflater,
+ featureFlags: FeatureFlags,
+ @Named(SceneContainerNames.SYSTEM_UI_DEFAULT)
+ viewModelProvider: Provider<SceneContainerViewModel>,
+ @Named(SceneContainerNames.SYSTEM_UI_DEFAULT)
+ containerConfigProvider: Provider<SceneContainerConfig>,
+ @Named(SceneContainerNames.SYSTEM_UI_DEFAULT)
+ scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>,
+ ): WindowRootView {
+ return if (
+ featureFlags.isEnabled(Flags.SCENE_CONTAINER) && ComposeFacade.isComposeAvailable()
+ ) {
+ val sceneWindowRootView =
+ layoutInflater.inflate(R.layout.scene_window_root, null) as SceneWindowRootView
+ sceneWindowRootView.init(
+ viewModel = viewModelProvider.get(),
+ containerConfig = containerConfigProvider.get(),
+ scenes = scenesProvider.get(),
+ )
+ sceneWindowRootView
+ } else {
+ layoutInflater.inflate(R.layout.super_notification_shade, null)
+ }
+ as WindowRootView?
+ ?: throw IllegalStateException("Window root view could not be properly inflated")
+ }
+
+ @Provides
+ @SysUISingleton
+ // TODO(b/277762009): Do something similar to
+ // {@link StatusBarWindowModule.InternalWindowView} so that only
+ // {@link NotificationShadeWindowViewController} can inject this view.
+ fun providesNotificationShadeWindowView(
+ root: WindowRootView,
+ featureFlags: FeatureFlags,
+ ): NotificationShadeWindowView {
+ if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
+ return root.findViewById(R.id.legacy_window_root)
+ }
+ return root as NotificationShadeWindowView?
+ ?: throw IllegalStateException("root view not a NotificationShadeWindowView")
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ fun providesNotificationStackScrollLayout(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): NotificationStackScrollLayout {
+ return notificationShadeWindowView.findViewById(R.id.notification_stack_scroller)
+ }
+
+ @Provides
+ @SysUISingleton
+ fun providesNotificationShelfController(
+ featureFlags: FeatureFlags,
+ newImpl: Provider<NotificationShelfViewBinderWrapperControllerImpl>,
+ notificationShelfComponentBuilder: NotificationShelfComponent.Builder,
+ layoutInflater: LayoutInflater,
+ notificationStackScrollLayout: NotificationStackScrollLayout,
+ ): NotificationShelfController {
+ return if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ newImpl.get()
+ } else {
+ val shelfView =
+ layoutInflater.inflate(
+ R.layout.status_bar_notification_shelf,
+ notificationStackScrollLayout,
+ false
+ ) as NotificationShelf
+ val component =
+ notificationShelfComponentBuilder.notificationShelf(shelfView).build()
+ val notificationShelfController = component.notificationShelfController
+ notificationShelfController.init()
+ notificationShelfController
+ }
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ fun providesNotificationPanelView(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): NotificationPanelView {
+ return notificationShadeWindowView.findViewById(R.id.notification_panel)
+ }
+
+ /**
+ * Constructs a new, unattached [KeyguardBottomAreaView].
+ *
+ * Note that this is explicitly _not_ a singleton, as we want to be able to reinflate it
+ */
+ @Provides
+ fun providesKeyguardBottomAreaView(
+ npv: NotificationPanelView,
+ layoutInflater: LayoutInflater,
+ ): KeyguardBottomAreaView {
+ return layoutInflater.inflate(R.layout.keyguard_bottom_area, npv, false)
+ as KeyguardBottomAreaView
+ }
+
+ @Provides
+ @SysUISingleton
+ fun providesLightRevealScrim(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): LightRevealScrim {
+ return notificationShadeWindowView.findViewById(R.id.light_reveal_scrim)
+ }
+
+ @Provides
+ @SysUISingleton
+ fun providesKeyguardRootView(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): KeyguardRootView {
+ return notificationShadeWindowView.findViewById(R.id.keyguard_root_view)
+ }
+
+ @Provides
+ @SysUISingleton
+ fun providesSharedNotificationContainer(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): SharedNotificationContainer {
+ return notificationShadeWindowView.findViewById(R.id.shared_notification_container)
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ fun providesAuthRippleView(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): AuthRippleView? {
+ return notificationShadeWindowView.findViewById(R.id.auth_ripple)
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ fun providesLockIconView(
+ keyguardRootView: KeyguardRootView,
+ notificationPanelView: NotificationPanelView,
+ featureFlags: FeatureFlags
+ ): LockIconView {
+ if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
+ return keyguardRootView.findViewById(R.id.lock_icon_view)
+ } else {
+ return notificationPanelView.findViewById(R.id.lock_icon_view)
+ }
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ fun providesTapAgainView(
+ notificationPanelView: NotificationPanelView,
+ ): TapAgainView {
+ return notificationPanelView.findViewById(R.id.shade_falsing_tap_again)
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ fun providesNotificationsQuickSettingsContainer(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): NotificationsQuickSettingsContainer {
+ return notificationShadeWindowView.findViewById(R.id.notification_container_parent)
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ @Named(SHADE_HEADER)
+ fun providesShadeHeaderView(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): MotionLayout {
+ val stub = notificationShadeWindowView.findViewById<ViewStub>(R.id.qs_header_stub)
+ val layoutId = R.layout.combined_qs_header
+ stub.layoutResource = layoutId
+ return stub.inflate() as MotionLayout
+ }
+
+ @Provides
+ @SysUISingleton
+ fun providesCombinedShadeHeadersConstraintManager(): CombinedShadeHeadersConstraintManager {
+ return CombinedShadeHeadersConstraintManagerImpl
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ @Named(SHADE_HEADER)
+ fun providesBatteryMeterView(@Named(SHADE_HEADER) view: MotionLayout): BatteryMeterView {
+ return view.findViewById(R.id.batteryRemainingIcon)
+ }
+
+ @Provides
+ @SysUISingleton
+ @Named(SHADE_HEADER)
+ fun providesBatteryMeterViewController(
+ @Named(SHADE_HEADER) batteryMeterView: BatteryMeterView,
+ userTracker: UserTracker,
+ configurationController: ConfigurationController,
+ tunerService: TunerService,
+ @Main mainHandler: Handler,
+ contentResolver: ContentResolver,
+ batteryController: BatteryController,
+ ): BatteryMeterViewController {
+ return BatteryMeterViewController(
+ batteryMeterView,
+ StatusBarLocation.QS,
+ userTracker,
+ configurationController,
+ tunerService,
+ mainHandler,
+ contentResolver,
+ batteryController,
+ )
+ }
+
+ @Provides
+ @SysUISingleton
+ @Named(SHADE_HEADER)
+ fun providesOngoingPrivacyChip(
+ @Named(SHADE_HEADER) header: MotionLayout,
+ ): OngoingPrivacyChip {
+ return header.findViewById(R.id.privacy_chip)
+ }
+
+ @Provides
+ @SysUISingleton
+ @Named(SHADE_HEADER)
+ fun providesStatusIconContainer(
+ @Named(SHADE_HEADER) header: MotionLayout,
+ ): StatusIconContainer {
+ return header.findViewById(R.id.statusIcons)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt
index c50693c..15ec18c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt
@@ -17,6 +17,7 @@
package com.android.systemui.shade
import com.android.systemui.CoreStartable
+import com.android.systemui.biometrics.AuthRippleController
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
@@ -28,4 +29,9 @@
@IntoMap
@ClassKey(ShadeController::class)
abstract fun bind(shadeController: ShadeController): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(AuthRippleController::class)
+ abstract fun bindAuthRippleController(controller: AuthRippleController): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt
index 5d06f8d0..15ff31f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt
@@ -21,11 +21,12 @@
/**
* A temporary base class that's shared between our old status bar connectivity view implementations
- * ([StatusBarMobileView]) and our new status bar implementations ([ModernStatusBarWifiView],
- * [ModernStatusBarMobileView]).
+ * and our new status bar implementations ([ModernStatusBarWifiView], [ModernStatusBarMobileView]).
*
* Once our refactor is over, we should be able to delete this go-between class and the old view
* class.
+ *
+ * NOTE: the old classes are now deleted, and this class can be removed.
*/
abstract class BaseStatusBarFrameLayout
@JvmOverloads
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
index 4ec5f46..7a989cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
@@ -19,7 +19,6 @@
import android.view.View;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -52,7 +51,6 @@
mActivatableNotificationViewController = activatableNotificationViewController;
mKeyguardBypassController = keyguardBypassController;
mStatusBarStateController = statusBarStateController;
- mView.setSensitiveRevealAnimEnabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index fb88a96..763400b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -27,10 +27,12 @@
import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.hardware.display.DisplayManager;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
@@ -41,6 +43,7 @@
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.Log;
+import android.view.Display;
import android.view.View;
import android.widget.ImageView;
@@ -74,11 +77,15 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashSet;
+import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.stream.Collectors;
import dagger.Lazy;
@@ -138,6 +145,14 @@
private BackDropView mBackdrop;
private ImageView mBackdropFront;
private ImageView mBackdropBack;
+ private final Point mTmpDisplaySize = new Point();
+
+ private final DisplayManager mDisplayManager;
+ @Nullable
+ private List<String> mSmallerInternalDisplayUids;
+ private Display mCurrentDisplay;
+
+ private LockscreenWallpaper.WallpaperDrawable mWallapperDrawable;
private final MediaController.Callback mMediaListener = new MediaController.Callback() {
@Override
@@ -184,7 +199,8 @@
SysuiColorExtractor colorExtractor,
KeyguardStateController keyguardStateController,
DumpManager dumpManager,
- WallpaperManager wallpaperManager) {
+ WallpaperManager wallpaperManager,
+ DisplayManager displayManager) {
mContext = context;
mMediaArtworkProcessor = mediaArtworkProcessor;
mKeyguardBypassController = keyguardBypassController;
@@ -200,6 +216,7 @@
mStatusBarStateController = statusBarStateController;
mColorExtractor = colorExtractor;
mKeyguardStateController = keyguardStateController;
+ mDisplayManager = displayManager;
mIsLockscreenLiveWallpaperEnabled = wallpaperManager.isLockscreenLiveWallpaperEnabled();
setupNotifPipeline();
@@ -477,6 +494,48 @@
}
/**
+ * Notify lockscreen wallpaper drawable the current internal display.
+ */
+ public void onDisplayUpdated(Display display) {
+ Trace.beginSection("NotificationMediaManager#onDisplayUpdated");
+ mCurrentDisplay = display;
+ if (mWallapperDrawable != null) {
+ mWallapperDrawable.onDisplayUpdated(isOnSmallerInternalDisplays());
+ }
+ Trace.endSection();
+ }
+
+ private boolean isOnSmallerInternalDisplays() {
+ if (mSmallerInternalDisplayUids == null) {
+ mSmallerInternalDisplayUids = findSmallerInternalDisplayUids();
+ }
+ return mSmallerInternalDisplayUids.contains(mCurrentDisplay.getUniqueId());
+ }
+
+ private List<String> findSmallerInternalDisplayUids() {
+ if (mSmallerInternalDisplayUids != null) {
+ return mSmallerInternalDisplayUids;
+ }
+ List<Display> internalDisplays = Arrays.stream(mDisplayManager.getDisplays(
+ DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED))
+ .filter(display -> display.getType() == Display.TYPE_INTERNAL)
+ .collect(Collectors.toList());
+ if (internalDisplays.isEmpty()) {
+ return List.of();
+ }
+ Display largestDisplay = internalDisplays.stream()
+ .max(Comparator.comparingInt(this::getRealDisplayArea))
+ .orElse(internalDisplays.get(0));
+ internalDisplays.remove(largestDisplay);
+ return internalDisplays.stream().map(Display::getUniqueId).collect(Collectors.toList());
+ }
+
+ private int getRealDisplayArea(Display display) {
+ display.getRealSize(mTmpDisplaySize);
+ return mTmpDisplaySize.x * mTmpDisplaySize.y;
+ }
+
+ /**
* Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
*/
public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
@@ -551,7 +610,7 @@
mLockscreenWallpaper != null ? mLockscreenWallpaper.getBitmap() : null;
if (lockWallpaper != null) {
artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
- mBackdropBack.getResources(), lockWallpaper);
+ mBackdropBack.getResources(), lockWallpaper, isOnSmallerInternalDisplays());
// We're in the SHADE mode on the SIM screen - yet we still need to show
// the lockscreen wallpaper in that mode.
allowWhenShade = mStatusBarStateController.getState() == KEYGUARD;
@@ -611,6 +670,10 @@
mBackdropBack.setBackgroundColor(0xFFFFFFFF);
mBackdropBack.setImageDrawable(new ColorDrawable(c));
} else {
+ if (artworkDrawable instanceof LockscreenWallpaper.WallpaperDrawable) {
+ mWallapperDrawable =
+ (LockscreenWallpaper.WallpaperDrawable) artworkDrawable;
+ }
mBackdropBack.setImageDrawable(artworkDrawable);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 0e20df6..b624115 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -553,7 +553,6 @@
*/
fun onWallpaperZoomOutChanged(zoomOut: Float)
- @JvmDefault
fun onBlurRadiusChanged(blurRadius: Int) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index 47a4641..5ac542b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -112,9 +112,6 @@
/** Sets the state of whether heads up is showing or not. */
default void setHeadsUpShowing(boolean showing) {}
- /** Sets whether the wallpaper supports ambient mode or not. */
- default void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {}
-
/** Gets whether the wallpaper is showing or not. */
default boolean isShowingWallpaper() {
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 25a1dc6..3f37c60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -24,7 +24,6 @@
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.IndentingPrintWriter;
-import android.util.Log;
import android.util.MathUtils;
import android.view.View;
import android.view.ViewGroup;
@@ -40,6 +39,8 @@
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.R;
import com.android.systemui.animation.ShadeInterpolation;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -95,8 +96,10 @@
private float mCornerAnimationDistance;
private NotificationShelfController mController;
private float mActualWidth = -1;
- private boolean mSensitiveRevealAnimEnabled;
- private boolean mShelfRefactorFlagEnabled;
+ private final ViewRefactorFlag mSensitiveRevealAnim =
+ new ViewRefactorFlag(Flags.SENSITIVE_REVEAL_ANIM);
+ private final ViewRefactorFlag mShelfRefactor =
+ new ViewRefactorFlag(Flags.NOTIFICATION_SHELF_REFACTOR);
private boolean mCanModifyColorOfNotifications;
private boolean mCanInteract;
private NotificationStackScrollLayout mHostLayout;
@@ -130,7 +133,7 @@
public void bind(AmbientState ambientState,
NotificationStackScrollLayoutController hostLayoutController) {
- assertRefactorFlagDisabled();
+ mShelfRefactor.assertDisabled();
mAmbientState = ambientState;
mHostLayoutController = hostLayoutController;
hostLayoutController.setOnNotificationRemovedListener((child, isTransferInProgress) -> {
@@ -140,7 +143,7 @@
public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout,
NotificationRoundnessManager roundnessManager) {
- if (!checkRefactorFlagEnabled()) return;
+ if (!mShelfRefactor.expectEnabled()) return;
mAmbientState = ambientState;
mHostLayout = hostLayout;
mRoundnessManager = roundnessManager;
@@ -268,7 +271,7 @@
}
final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight();
- if (mSensitiveRevealAnimEnabled && viewState.hidden) {
+ if (mSensitiveRevealAnim.isEnabled() && viewState.hidden) {
// if the shelf is hidden, position it at the end of the stack (plus the clip
// padding), such that when it appears animated, it will smoothly move in from the
// bottom, without jump cutting any notifications
@@ -279,7 +282,7 @@
}
private int getSpeedBumpIndex() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.getSpeedBumpIndex();
} else {
return mHostLayoutController.getSpeedBumpIndex();
@@ -413,7 +416,7 @@
expandingAnimated, isLastChild, shelfClipStart);
// TODO(b/172289889) scale mPaddingBetweenElements with expansion amount
- if ((!mSensitiveRevealAnimEnabled && ((isLastChild && !child.isInShelf())
+ if ((!mSensitiveRevealAnim.isEnabled() && ((isLastChild && !child.isInShelf())
|| backgroundForceHidden)) || aboveShelf) {
notificationClipEnd = shelfStart + getIntrinsicHeight();
} else {
@@ -462,7 +465,7 @@
// if the shelf is visible, but if the shelf is hidden, it causes incorrect curling.
// notificationClipEnd handles the discrepancy between a visible and hidden shelf,
// so we use that when on the keyguard (and while animating away) to reduce curling.
- final float keyguardSafeShelfStart = !mSensitiveRevealAnimEnabled
+ final float keyguardSafeShelfStart = !mSensitiveRevealAnim.isEnabled()
&& mAmbientState.isOnKeyguard() ? notificationClipEnd : shelfStart;
updateCornerRoundnessOnScroll(anv, viewStart, keyguardSafeShelfStart);
}
@@ -504,7 +507,7 @@
}
private ExpandableView getHostLayoutChildAt(int index) {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return (ExpandableView) mHostLayout.getChildAt(index);
} else {
return mHostLayoutController.getChildAt(index);
@@ -512,7 +515,7 @@
}
private int getHostLayoutChildCount() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.getChildCount();
} else {
return mHostLayoutController.getChildCount();
@@ -520,7 +523,7 @@
}
private boolean canModifyColorOfNotifications() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mCanModifyColorOfNotifications && mAmbientState.isShadeExpanded();
} else {
return mController.canModifyColorOfNotifications();
@@ -583,7 +586,7 @@
}
private boolean isViewAffectedBySwipe(ExpandableView expandableView) {
- if (!mShelfRefactorFlagEnabled) {
+ if (!mShelfRefactor.isEnabled()) {
return mHostLayoutController.isViewAffectedBySwipe(expandableView);
} else {
return mRoundnessManager.isViewAffectedBySwipe(expandableView);
@@ -607,7 +610,7 @@
}
private View getHostLayoutTransientView(int index) {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.getTransientView(index);
} else {
return mHostLayoutController.getTransientView(index);
@@ -615,7 +618,7 @@
}
private int getHostLayoutTransientViewCount() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.getTransientViewCount();
} else {
return mHostLayoutController.getTransientViewCount();
@@ -961,7 +964,7 @@
@Override
public void onStateChanged(int newState) {
- assertRefactorFlagDisabled();
+ mShelfRefactor.assertDisabled();
mStatusBarState = newState;
updateInteractiveness();
}
@@ -975,7 +978,7 @@
}
private boolean canInteract() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mCanInteract;
} else {
return mStatusBarState == StatusBarState.KEYGUARD;
@@ -1018,32 +1021,18 @@
return false;
}
- private void assertRefactorFlagDisabled() {
- if (mShelfRefactorFlagEnabled) {
- NotificationShelfController.throwIllegalFlagStateError(false);
- }
- }
-
- private boolean checkRefactorFlagEnabled() {
- if (!mShelfRefactorFlagEnabled) {
- Log.wtf(TAG,
- "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is disabled.");
- }
- return mShelfRefactorFlagEnabled;
- }
-
public void setController(NotificationShelfController notificationShelfController) {
- assertRefactorFlagDisabled();
+ mShelfRefactor.assertDisabled();
mController = notificationShelfController;
}
public void setCanModifyColorOfNotifications(boolean canModifyColorOfNotifications) {
- if (!checkRefactorFlagEnabled()) return;
+ if (!mShelfRefactor.expectEnabled()) return;
mCanModifyColorOfNotifications = canModifyColorOfNotifications;
}
public void setCanInteract(boolean canInteract) {
- if (!checkRefactorFlagEnabled()) return;
+ if (!mShelfRefactor.expectEnabled()) return;
mCanInteract = canInteract;
updateInteractiveness();
}
@@ -1053,27 +1042,15 @@
}
private int getIndexOfViewInHostLayout(ExpandableView child) {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.indexOfChild(child);
} else {
return mHostLayoutController.indexOfChild(child);
}
}
- /**
- * Set whether the sensitive reveal animation feature flag is enabled
- * @param enabled true if enabled
- */
- public void setSensitiveRevealAnimEnabled(boolean enabled) {
- mSensitiveRevealAnimEnabled = enabled;
- }
-
- public void setRefactorFlagEnabled(boolean enabled) {
- mShelfRefactorFlagEnabled = enabled;
- }
-
public void requestRoundnessResetFor(ExpandableView child) {
- if (!checkRefactorFlagEnabled()) return;
+ if (!mShelfRefactor.expectEnabled()) return;
child.requestRoundnessReset(SHELF_SCROLL);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
index 1619dda..8a3e217 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
@@ -16,11 +16,8 @@
package com.android.systemui.statusbar
-import android.util.Log
import android.view.View
import android.view.View.OnClickListener
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
@@ -49,29 +46,4 @@
/** @see View.setOnClickListener */
fun setOnClickListener(listener: OnClickListener)
-
- companion object {
- @JvmStatic
- fun assertRefactorFlagDisabled(featureFlags: FeatureFlags) {
- if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
- throwIllegalFlagStateError(expected = false)
- }
- }
-
- @JvmStatic
- fun checkRefactorFlagEnabled(featureFlags: FeatureFlags): Boolean =
- featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR).also { enabled ->
- if (!enabled) {
- Log.wtf("NotifShelf", getErrorMessage(expected = true))
- }
- }
-
- @JvmStatic
- fun throwIllegalFlagStateError(expected: Boolean): Nothing =
- error(getErrorMessage(expected))
-
- private fun getErrorMessage(expected: Boolean): String =
- "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is " +
- if (expected) "disabled" else "enabled"
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
deleted file mode 100644
index d6f6c2c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * Copyright (C) 2018 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;
-
-import static com.android.systemui.plugins.DarkIconDispatcher.getTint;
-import static com.android.systemui.plugins.DarkIconDispatcher.isInAreas;
-import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT;
-import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN;
-import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.settingslib.graph.SignalDrawable;
-import com.android.systemui.DualToneHandler;
-import com.android.systemui.R;
-import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
-
-import java.util.ArrayList;
-
-/**
- * View group for the mobile icon in the status bar
- */
-public class StatusBarMobileView extends BaseStatusBarFrameLayout implements DarkReceiver,
- StatusIconDisplayable {
- private static final String TAG = "StatusBarMobileView";
-
- /// Used to show etc dots
- private StatusBarIconView mDotView;
- /// The main icon view
- private LinearLayout mMobileGroup;
- private String mSlot;
- private MobileIconState mState;
- private SignalDrawable mMobileDrawable;
- private View mInoutContainer;
- private ImageView mIn;
- private ImageView mOut;
- private ImageView mMobile, mMobileType, mMobileRoaming;
- private View mMobileRoamingSpace;
- @StatusBarIconView.VisibleState
- private int mVisibleState = STATE_HIDDEN;
- private DualToneHandler mDualToneHandler;
- private boolean mForceHidden;
-
- /**
- * Designated constructor
- *
- * This view is special, in that it is the only view in SystemUI that allows for a configuration
- * override on a MCC/MNC-basis. This means that for every mobile view inflated, we have to
- * construct a context with that override, since the resource system doesn't have a way to
- * handle this for us.
- *
- * @param context A context with resources configured by MCC/MNC
- * @param slot The string key defining which slot this icon refers to. Always "mobile" for the
- * mobile icon
- */
- public static StatusBarMobileView fromContext(
- Context context,
- String slot
- ) {
- LayoutInflater inflater = LayoutInflater.from(context);
- StatusBarMobileView v = (StatusBarMobileView)
- inflater.inflate(R.layout.status_bar_mobile_signal_group, null);
- v.setSlot(slot);
- v.init();
- v.setVisibleState(STATE_ICON);
- return v;
- }
-
- public StatusBarMobileView(Context context) {
- super(context);
- }
-
- public StatusBarMobileView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public StatusBarMobileView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- @Override
- public void getDrawingRect(Rect outRect) {
- super.getDrawingRect(outRect);
- float translationX = getTranslationX();
- float translationY = getTranslationY();
- outRect.left += translationX;
- outRect.right += translationX;
- outRect.top += translationY;
- outRect.bottom += translationY;
- }
-
- private void init() {
- mDualToneHandler = new DualToneHandler(getContext());
- mMobileGroup = findViewById(R.id.mobile_group);
- mMobile = findViewById(R.id.mobile_signal);
- mMobileType = findViewById(R.id.mobile_type);
- mMobileRoaming = findViewById(R.id.mobile_roaming);
- mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space);
- mIn = findViewById(R.id.mobile_in);
- mOut = findViewById(R.id.mobile_out);
- mInoutContainer = findViewById(R.id.inout_container);
-
- mMobileDrawable = new SignalDrawable(getContext());
- mMobile.setImageDrawable(mMobileDrawable);
-
- initDotView();
- }
-
- private void initDotView() {
- mDotView = new StatusBarIconView(mContext, mSlot, null);
- mDotView.setVisibleState(STATE_DOT);
-
- int width = mContext.getResources().getDimensionPixelSize(R.dimen.status_bar_icon_size_sp);
- LayoutParams lp = new LayoutParams(width, width);
- lp.gravity = Gravity.CENTER_VERTICAL | Gravity.START;
- addView(mDotView, lp);
- }
-
- public void applyMobileState(MobileIconState state) {
- boolean requestLayout = false;
- if (state == null) {
- requestLayout = getVisibility() != View.GONE;
- setVisibility(View.GONE);
- mState = null;
- } else if (mState == null) {
- requestLayout = true;
- mState = state.copy();
- initViewState();
- } else if (!mState.equals(state)) {
- requestLayout = updateState(state.copy());
- }
-
- if (requestLayout) {
- requestLayout();
- }
- }
-
- private void initViewState() {
- setContentDescription(mState.contentDescription);
- if (!mState.visible || mForceHidden) {
- mMobileGroup.setVisibility(View.GONE);
- } else {
- mMobileGroup.setVisibility(View.VISIBLE);
- }
- mMobileDrawable.setLevel(mState.strengthId);
- if (mState.typeId > 0) {
- mMobileType.setContentDescription(mState.typeContentDescription);
- mMobileType.setImageResource(mState.typeId);
- mMobileType.setVisibility(View.VISIBLE);
- } else {
- mMobileType.setVisibility(View.GONE);
- }
- mMobile.setVisibility(mState.showTriangle ? View.VISIBLE : View.GONE);
- mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
- mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
- mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
- mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE);
- mInoutContainer.setVisibility((mState.activityIn || mState.activityOut)
- ? View.VISIBLE : View.GONE);
- }
-
- private boolean updateState(MobileIconState state) {
- boolean needsLayout = false;
-
- setContentDescription(state.contentDescription);
- int newVisibility = state.visible && !mForceHidden ? View.VISIBLE : View.GONE;
- if (newVisibility != mMobileGroup.getVisibility() && STATE_ICON == mVisibleState) {
- mMobileGroup.setVisibility(newVisibility);
- needsLayout = true;
- }
- if (mState.strengthId != state.strengthId) {
- mMobileDrawable.setLevel(state.strengthId);
- }
- if (mState.typeId != state.typeId) {
- needsLayout |= state.typeId == 0 || mState.typeId == 0;
- if (state.typeId != 0) {
- mMobileType.setContentDescription(state.typeContentDescription);
- mMobileType.setImageResource(state.typeId);
- mMobileType.setVisibility(View.VISIBLE);
- } else {
- mMobileType.setVisibility(View.GONE);
- }
- }
-
- mMobile.setVisibility(state.showTriangle ? View.VISIBLE : View.GONE);
- mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
- mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
- mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
- mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE);
- mInoutContainer.setVisibility((state.activityIn || state.activityOut)
- ? View.VISIBLE : View.GONE);
-
- needsLayout |= state.roaming != mState.roaming
- || state.activityIn != mState.activityIn
- || state.activityOut != mState.activityOut
- || state.showTriangle != mState.showTriangle;
-
- mState = state;
- return needsLayout;
- }
-
- @Override
- public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) {
- float intensity = isInAreas(areas, this) ? darkIntensity : 0;
- mMobileDrawable.setTintList(
- ColorStateList.valueOf(mDualToneHandler.getSingleColor(intensity)));
- ColorStateList color = ColorStateList.valueOf(getTint(areas, this, tint));
- mIn.setImageTintList(color);
- mOut.setImageTintList(color);
- mMobileType.setImageTintList(color);
- mMobileRoaming.setImageTintList(color);
- mDotView.setDecorColor(tint);
- mDotView.setIconColor(tint, false);
- }
-
- @Override
- public String getSlot() {
- return mSlot;
- }
-
- public void setSlot(String slot) {
- mSlot = slot;
- }
-
- @Override
- public void setStaticDrawableColor(int color) {
- ColorStateList list = ColorStateList.valueOf(color);
- mMobileDrawable.setTintList(list);
- mIn.setImageTintList(list);
- mOut.setImageTintList(list);
- mMobileType.setImageTintList(list);
- mMobileRoaming.setImageTintList(list);
- mDotView.setDecorColor(color);
- }
-
- @Override
- public void setDecorColor(int color) {
- mDotView.setDecorColor(color);
- }
-
- @Override
- public boolean isIconVisible() {
- return mState.visible && !mForceHidden;
- }
-
- @Override
- public void setVisibleState(@StatusBarIconView.VisibleState int state, boolean animate) {
- if (state == mVisibleState) {
- return;
- }
-
- mVisibleState = state;
- switch (state) {
- case STATE_ICON:
- mMobileGroup.setVisibility(View.VISIBLE);
- mDotView.setVisibility(View.GONE);
- break;
- case STATE_DOT:
- mMobileGroup.setVisibility(View.INVISIBLE);
- mDotView.setVisibility(View.VISIBLE);
- break;
- case STATE_HIDDEN:
- default:
- mMobileGroup.setVisibility(View.INVISIBLE);
- mDotView.setVisibility(View.INVISIBLE);
- break;
- }
- }
-
- /**
- * Forces the state to be hidden (views will be GONE) and if necessary updates the layout.
- *
- * Makes sure that the {@link StatusBarIconController} cannot make it visible while this flag
- * is enabled.
- * @param forceHidden {@code true} if the icon should be GONE in its view regardless of its
- * state.
- * {@code false} if the icon should show as determined by its controller.
- */
- public void forceHidden(boolean forceHidden) {
- if (mForceHidden != forceHidden) {
- mForceHidden = forceHidden;
- updateState(mState);
- requestLayout();
- }
- }
-
- @Override
- @StatusBarIconView.VisibleState
- public int getVisibleState() {
- return mVisibleState;
- }
-
- @VisibleForTesting
- public MobileIconState getState() {
- return mState;
- }
-
- @Override
- public String toString() {
- return "StatusBarMobileView(slot=" + mSlot + " state=" + mState + ")";
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 9aa28c3..93b9ac6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -1282,7 +1282,7 @@
}
}
String sims = args.getString("sims");
- if (sims != null && !mStatusBarPipelineFlags.useNewMobileIcons()) {
+ if (sims != null) {
int num = MathUtils.constrain(Integer.parseInt(sims), 1, 8);
List<SubscriptionInfo> subs = new ArrayList<>();
if (num != mMobileSignalControllers.size()) {
@@ -1305,7 +1305,7 @@
mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected);
}
String mobile = args.getString("mobile");
- if (mobile != null && !mStatusBarPipelineFlags.useNewMobileIcons()) {
+ if (mobile != null) {
boolean show = mobile.equals("show");
String datatype = args.getString("datatype");
String slotString = args.getString("slot");
@@ -1390,7 +1390,7 @@
controller.notifyListeners();
}
String carrierNetworkChange = args.getString("carriernetworkchange");
- if (carrierNetworkChange != null && !mStatusBarPipelineFlags.useNewMobileIcons()) {
+ if (carrierNetworkChange != null) {
boolean show = carrierNetworkChange.equals("show");
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController controller = mMobileSignalControllers.valueAt(i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
index 599beec..6be407a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
@@ -29,7 +29,6 @@
*
* @param wifiIndicators a box type containing enough information to properly draw a Wi-Fi icon
*/
- @JvmDefault
fun setWifiIndicators(wifiIndicators: WifiIndicators) {}
/**
@@ -42,7 +41,6 @@
* NOTE: phones can have multiple subscriptions, so this [mobileDataIndicators] object should be
* indexed based on its [subId][MobileDataIndicators.subId]
*/
- @JvmDefault
fun setMobileDataIndicators(mobileDataIndicators: MobileDataIndicators) {}
/**
@@ -51,7 +49,6 @@
*
* @param subs a [SubscriptionInfo] for each subscription that we know about
*/
- @JvmDefault
fun setSubs(subs: List<@JvmSuppressWildcards SubscriptionInfo>) {}
/**
@@ -63,7 +60,6 @@
* @param show whether or not to show a "no sim" view
* @param simDetected whether any SIM is detected or not
*/
- @JvmDefault
fun setNoSims(show: Boolean, simDetected: Boolean) {}
/**
@@ -72,7 +68,6 @@
*
* @param icon an [IconState] for the current ethernet status
*/
- @JvmDefault
fun setEthernetIndicators(icon: IconState) {}
/**
@@ -80,7 +75,6 @@
*
* @param icon an [IconState] for the current airplane mode status
*/
- @JvmDefault
fun setIsAirplaneMode(icon: IconState) {}
/**
@@ -88,7 +82,6 @@
*
* @param enabled the current mobile data feature ennabled state
*/
- @JvmDefault
fun setMobileDataEnabled(enabled: Boolean) {}
/**
@@ -97,7 +90,6 @@
* @param noValidatedNetwork whether there is any validated network.
* @param noNetworksAvailable whether there is any WiFi networks available.
*/
- @JvmDefault
fun setConnectivityStatus(
noDefaultNetwork: Boolean,
noValidatedNetwork: Boolean,
@@ -109,7 +101,6 @@
* @param statusIcon the icon for the call indicator
* @param subId subscription ID for which to update the UI
*/
- @JvmDefault
fun setCallIndicator(statusIcon: IconState, subId: Int) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index e5ba3ce..1c7a186 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -19,6 +19,7 @@
import android.app.IActivityManager;
import android.app.WallpaperManager;
import android.content.Context;
+import android.hardware.display.DisplayManager;
import android.os.RemoteException;
import android.service.dreams.IDreamManager;
import android.util.Log;
@@ -146,7 +147,8 @@
SysuiColorExtractor colorExtractor,
KeyguardStateController keyguardStateController,
DumpManager dumpManager,
- WallpaperManager wallpaperManager) {
+ WallpaperManager wallpaperManager,
+ DisplayManager displayManager) {
return new NotificationMediaManager(
context,
centralSurfacesOptionalLazy,
@@ -162,7 +164,8 @@
colorExtractor,
keyguardStateController,
dumpManager,
- wallpaperManager);
+ wallpaperManager,
+ displayManager);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index 2a18f1f..ef90890 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -49,11 +49,10 @@
fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator? { return null }
// Best method name, change my mind
- @JvmDefault
fun onSystemStatusAnimationTransitionToPersistentDot(contentDescription: String?): Animator? {
return null
}
- @JvmDefault fun onHidePersistentDot(): Animator? { return null }
+ fun onHidePersistentDot(): Animator? { return null }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
index 577ad20..bac8982 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.notification
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.FlagResolver
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import javax.inject.Inject
@@ -25,12 +23,8 @@
class NotifPipelineFlags
@Inject
constructor(
- private val featureFlags: FeatureFlags,
- private val sysPropFlags: FlagResolver,
+ private val featureFlags: FeatureFlags
) {
fun isDevLoggingEnabled(): Boolean =
featureFlags.isEnabled(Flags.NOTIFICATION_PIPELINE_DEVELOPER_LOGGING)
-
- fun allowDismissOngoing(): Boolean =
- sysPropFlags.isEnabled(NotificationFlags.ALLOW_DISMISS_ONGOING)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 9ba2199..8d1e8d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -613,20 +613,20 @@
interface WakeUpListener {
/** Called whenever the notifications are fully hidden or shown */
- @JvmDefault fun onFullyHiddenChanged(isFullyHidden: Boolean) {}
+ fun onFullyHiddenChanged(isFullyHidden: Boolean) {}
/**
* Called whenever the pulseExpansion changes
*
* @param expandingChanged if the user has started or stopped expanding
*/
- @JvmDefault fun onPulseExpansionChanged(expandingChanged: Boolean) {}
+ fun onPulseExpansionChanged(expandingChanged: Boolean) {}
/**
* Called when the animator started by [scheduleDelayedDozeAmountAnimation] begins running
* after the start delay, or after it ends/is cancelled.
*/
- @JvmDefault fun onDelayedDozeAmountAnimationRunning(running: Boolean) {}
+ fun onDelayedDozeAmountAnimationRunning(running: Boolean) {}
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
index 1cf9c1e..8029f48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
@@ -3,10 +3,10 @@
import android.util.FloatProperty
import android.view.View
import androidx.annotation.FloatRange
-import com.android.systemui.Dependency
import com.android.systemui.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.ViewRefactorFlag
import com.android.systemui.statusbar.notification.stack.AnimationProperties
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import kotlin.math.abs
@@ -27,37 +27,31 @@
/** Current top roundness */
@get:FloatRange(from = 0.0, to = 1.0)
- @JvmDefault
val topRoundness: Float
get() = roundableState.topRoundness
/** Current bottom roundness */
@get:FloatRange(from = 0.0, to = 1.0)
- @JvmDefault
val bottomRoundness: Float
get() = roundableState.bottomRoundness
/** Max radius in pixel */
- @JvmDefault
val maxRadius: Float
get() = roundableState.maxRadius
/** Current top corner in pixel, based on [topRoundness] and [maxRadius] */
- @JvmDefault
val topCornerRadius: Float
get() =
- if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.topCornerRadius
+ if (roundableState.newHeadsUpAnim.isEnabled) roundableState.topCornerRadius
else topRoundness * maxRadius
/** Current bottom corner in pixel, based on [bottomRoundness] and [maxRadius] */
- @JvmDefault
val bottomCornerRadius: Float
get() =
- if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.bottomCornerRadius
+ if (roundableState.newHeadsUpAnim.isEnabled) roundableState.bottomCornerRadius
else bottomRoundness * maxRadius
/** Get and update the current radii */
- @JvmDefault
val updatedRadii: FloatArray
get() =
roundableState.radiiBuffer.also { radii ->
@@ -80,7 +74,6 @@
* @param sourceType the source from which the request for roundness comes.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestTopRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float,
sourceType: SourceType,
@@ -125,7 +118,6 @@
* @param sourceType the source from which the request for roundness comes.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestTopRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float,
sourceType: SourceType,
@@ -149,7 +141,6 @@
* @param sourceType the source from which the request for roundness comes.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestBottomRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float,
sourceType: SourceType,
@@ -194,7 +185,6 @@
* @param sourceType the source from which the request for roundness comes.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestBottomRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float,
sourceType: SourceType,
@@ -219,7 +209,6 @@
* @param animate true if it should animate to that value.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestRoundness(
@FloatRange(from = 0.0, to = 1.0) top: Float,
@FloatRange(from = 0.0, to = 1.0) bottom: Float,
@@ -246,7 +235,6 @@
* @param sourceType the source from which the request for roundness comes.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestRoundness(
@FloatRange(from = 0.0, to = 1.0) top: Float,
@FloatRange(from = 0.0, to = 1.0) bottom: Float,
@@ -270,7 +258,6 @@
* @param sourceType the source from which the request for roundness comes.
* @param animate true if it should animate to that value.
*/
- @JvmDefault
fun requestRoundnessReset(sourceType: SourceType, animate: Boolean) {
requestRoundness(top = 0f, bottom = 0f, sourceType = sourceType, animate = animate)
}
@@ -284,19 +271,16 @@
*
* @param sourceType the source from which the request for roundness comes.
*/
- @JvmDefault
fun requestRoundnessReset(sourceType: SourceType) {
requestRoundnessReset(sourceType = sourceType, animate = roundableState.targetView.isShown)
}
/** Apply the roundness changes, usually means invalidate the [RoundableState.targetView]. */
- @JvmDefault
fun applyRoundnessAndInvalidate() {
roundableState.targetView.invalidate()
}
/** @return true if top or bottom roundness is not zero. */
- @JvmDefault
fun hasRoundedCorner(): Boolean {
return topRoundness != 0f || bottomRoundness != 0f
}
@@ -307,7 +291,6 @@
*
* This method reuses the previous [radii] for performance reasons.
*/
- @JvmDefault
fun updateRadii(
topCornerRadius: Float,
bottomCornerRadius: Float,
@@ -335,13 +318,12 @@
internal val targetView: View,
private val roundable: Roundable,
maxRadius: Float,
- private val featureFlags: FeatureFlags = Dependency.get(FeatureFlags::class.java)
+ featureFlags: FeatureFlags? = null
) {
internal var maxRadius = maxRadius
private set
- internal val newHeadsUpAnimFlagEnabled
- get() = featureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS)
+ internal val newHeadsUpAnim = ViewRefactorFlag(featureFlags, Flags.IMPROVED_HUN_ANIMATIONS)
/** Animatable for top roundness */
private val topAnimatable = topAnimatable(roundable)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 7898736..affd2d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -791,28 +791,6 @@
return !mSbn.isOngoing() || !isLocked;
}
- /**
- * @return Can the underlying notification be individually dismissed?
- * @see #canViewBeDismissed()
- */
- // TODO: This logic doesn't belong on NotificationEntry. It should be moved to a controller
- // that can be added as a dependency to any class that needs to answer this question.
- public boolean legacyIsDismissableRecursive() {
- if (mSbn.isOngoing()) {
- return false;
- }
- List<NotificationEntry> children = getAttachedNotifChildren();
- if (children != null && children.size() > 0) {
- for (int i = 0; i < children.size(); i++) {
- NotificationEntry child = children.get(i);
- if (child.getSbn().isOngoing()) {
- return false;
- }
- }
- }
- return true;
- }
-
public boolean canViewBeDismissed() {
if (row == null) return true;
return row.canViewBeDismissed();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
index 1494574..93a34af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
@@ -48,6 +48,14 @@
private val mAlwaysExpandNonGroupedNotification =
context.resources.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications)
+ /**
+ * `true` if the first non-group expandable notification should be expanded automatically
+ * when possible. If `false`, then the first non-group expandable notification should not
+ * be expanded.
+ */
+ private val mAutoExpandFirstNotification =
+ context.resources.getBoolean(R.bool.config_autoExpandFirstNotification)
+
override fun attach(pipeline: NotifPipeline) {
pipeline.addOnBeforeRenderListListener(::onBeforeRenderList)
pipeline.addOnAfterRenderEntryListener(::onAfterRenderEntry)
@@ -61,8 +69,10 @@
private fun onAfterRenderEntry(entry: NotificationEntry, controller: NotifRowController) {
// If mAlwaysExpandNonGroupedNotification is false, then only expand the
- // very first notification and if it's not a child of grouped notifications.
- controller.setSystemExpanded(mAlwaysExpandNonGroupedNotification || entry == entryToExpand)
+ // very first notification if it's not a child of grouped notifications and when
+ // mAutoExpandFirstNotification is true.
+ controller.setSystemExpanded(mAlwaysExpandNonGroupedNotification ||
+ (mAutoExpandFirstNotification && entry == entryToExpand))
// Show/hide the feedback icon
controller.setFeedbackIcon(mAssistantFeedbackController.getFeedbackIcon(entry))
// Show the "alerted" bell icon
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 1896080..9ecf50e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.collection.inflation;
-import static com.android.systemui.flags.Flags.NOTIFICATION_INLINE_REPLY_ANIMATION;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -176,8 +175,6 @@
entry.setRow(row);
mNotifBindPipeline.manageRow(entry, row);
mPresenter.onBindRow(row);
- row.setInlineReplyAnimationFlagEnabled(
- mFeatureFlags.isEnabled(NOTIFICATION_INLINE_REPLY_ANIMATION));
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationDismissibilityProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationDismissibilityProviderImpl.kt
index b318252..78e9a74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationDismissibilityProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationDismissibilityProviderImpl.kt
@@ -20,7 +20,6 @@
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.util.asIndenting
import com.android.systemui.util.withIncreasedIndent
@@ -28,9 +27,7 @@
import javax.inject.Inject
@SysUISingleton
-class NotificationDismissibilityProviderImpl
-@Inject
-constructor(private val notifPipelineFlags: NotifPipelineFlags, dumpManager: DumpManager) :
+class NotificationDismissibilityProviderImpl @Inject constructor(dumpManager: DumpManager) :
NotificationDismissibilityProvider, Dumpable {
init {
@@ -43,11 +40,7 @@
private set
override fun isDismissable(entry: NotificationEntry): Boolean {
- return if (notifPipelineFlags.allowDismissOngoing()) {
- entry.key !in nonDismissableEntryKeys
- } else {
- entry.legacyIsDismissableRecursive()
- }
+ return entry.key !in nonDismissableEntryKeys
}
@Synchronized
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 908c11a..36a8e98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -566,7 +566,7 @@
@Override
public float getTopCornerRadius() {
- if (isNewHeadsUpAnimFlagEnabled()) {
+ if (mImprovedHunAnimation.isEnabled()) {
return super.getTopCornerRadius();
}
@@ -576,7 +576,7 @@
@Override
public float getBottomCornerRadius() {
- if (isNewHeadsUpAnimFlagEnabled()) {
+ if (mImprovedHunAnimation.isEnabled()) {
return super.getBottomCornerRadius();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index b34c281..42b99a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -77,6 +77,7 @@
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -275,7 +276,8 @@
private OnExpandClickListener mOnExpandClickListener;
private View.OnClickListener mOnFeedbackClickListener;
private Path mExpandingClipPath;
- private boolean mIsInlineReplyAnimationFlagEnabled = false;
+ private final ViewRefactorFlag mInlineReplyAnimation =
+ new ViewRefactorFlag(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION);
// Listener will be called when receiving a long click event.
// Use #setLongPressPosition to optionally assign positional data with the long press.
@@ -3121,10 +3123,6 @@
return showingLayout != null && showingLayout.requireRowToHaveOverlappingRendering();
}
- public void setInlineReplyAnimationFlagEnabled(boolean isEnabled) {
- mIsInlineReplyAnimationFlagEnabled = isEnabled;
- }
-
@Override
public void setActualHeight(int height, boolean notifyListeners) {
boolean changed = height != getActualHeight();
@@ -3144,7 +3142,7 @@
}
int contentHeight = Math.max(getMinHeight(), height);
for (NotificationContentView l : mLayouts) {
- if (mIsInlineReplyAnimationFlagEnabled) {
+ if (mInlineReplyAnimation.isEnabled()) {
l.setContentHeight(height);
} else {
l.setContentHeight(contentHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 7f23c1b..c8f13a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -28,10 +28,9 @@
import android.view.View;
import android.view.ViewOutlineProvider;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.statusbar.notification.RoundableState;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import com.android.systemui.util.DumpUtilsKt;
@@ -50,7 +49,8 @@
private float mOutlineAlpha = -1f;
private boolean mAlwaysRoundBothCorners;
private Path mTmpPath = new Path();
- private final FeatureFlags mFeatureFlags;
+ protected final ViewRefactorFlag mImprovedHunAnimation =
+ new ViewRefactorFlag(Flags.IMPROVED_HUN_ANIMATIONS);
/**
* {@code false} if the children views of the {@link ExpandableOutlineView} are translated when
@@ -126,7 +126,7 @@
return EMPTY_PATH;
}
float bottomRadius = mAlwaysRoundBothCorners ? getMaxRadius() : getBottomCornerRadius();
- if (!isNewHeadsUpAnimFlagEnabled() && (topRadius + bottomRadius > height)) {
+ if (!mImprovedHunAnimation.isEnabled() && (topRadius + bottomRadius > height)) {
float overShoot = topRadius + bottomRadius - height;
float currentTopRoundness = getTopRoundness();
float currentBottomRoundness = getBottomRoundness();
@@ -167,7 +167,6 @@
super(context, attrs);
setOutlineProvider(mProvider);
initDimens();
- mFeatureFlags = Dependency.get(FeatureFlags.class);
}
@Override
@@ -376,8 +375,4 @@
});
}
- // TODO(b/290365128) replace with ViewRefactorFlag
- protected boolean isNewHeadsUpAnimFlagEnabled() {
- return mFeatureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index 23a58d2..22a87a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -21,7 +21,6 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl
@@ -66,7 +65,7 @@
override fun setOnClickListener(listener: View.OnClickListener) = unsupported
private val unsupported: Nothing
- get() = NotificationShelfController.throwIllegalFlagStateError(expected = true)
+ get() = error("Code path not supported when NOTIFICATION_SHELF_REFACTOR is disabled")
}
/** Binds a [NotificationShelf] to its [view model][NotificationShelfViewModel]. */
@@ -80,8 +79,6 @@
) {
ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager)
shelf.apply {
- setRefactorFlagEnabled(true)
- setSensitiveRevealAnimEnabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM))
// TODO(278765923): Replace with eventual NotificationIconContainerViewBinder#bind()
notificationIconAreaController.setShelfIcons(shelfIcons)
repeatWhenAttached {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index b0f3f59..95e74f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -29,7 +29,6 @@
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
@@ -57,7 +56,6 @@
private final SectionProvider mSectionProvider;
private final BypassController mBypassController;
private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
- private final FeatureFlags mFeatureFlags;
/**
* Used to read bouncer states.
*/
@@ -261,13 +259,12 @@
@NonNull SectionProvider sectionProvider,
@NonNull BypassController bypassController,
@Nullable StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- @NonNull LargeScreenShadeInterpolator largeScreenShadeInterpolator,
- @NonNull FeatureFlags featureFlags) {
+ @NonNull LargeScreenShadeInterpolator largeScreenShadeInterpolator
+ ) {
mSectionProvider = sectionProvider;
mBypassController = bypassController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
- mFeatureFlags = featureFlags;
reload(context);
dumpManager.registerDumpable(this);
}
@@ -753,10 +750,6 @@
return mLargeScreenShadeInterpolator;
}
- public FeatureFlags getFeatureFlags() {
- return mFeatureFlags;
- }
-
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println("mTopPadding=" + mTopPadding);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index c1ceb3c..d71bc2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -89,6 +89,7 @@
import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.shade.ShadeController;
@@ -198,7 +199,8 @@
private Set<Integer> mDebugTextUsedYPositions;
private final boolean mDebugRemoveAnimation;
private final boolean mSensitiveRevealAnimEndabled;
- private boolean mAnimatedInsets;
+ private final ViewRefactorFlag mAnimatedInsets;
+ private final ViewRefactorFlag mShelfRefactor;
private int mContentHeight;
private float mIntrinsicContentHeight;
@@ -621,7 +623,9 @@
mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
mSensitiveRevealAnimEndabled = featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
- setAnimatedInsetsEnabled(featureFlags.isEnabled(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS));
+ mAnimatedInsets =
+ new ViewRefactorFlag(featureFlags, Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
+ mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
mSectionsManager = Dependency.get(NotificationSectionsManager.class);
mScreenOffAnimationController =
Dependency.get(ScreenOffAnimationController.class);
@@ -660,7 +664,7 @@
mGroupMembershipManager = Dependency.get(GroupMembershipManager.class);
mGroupExpansionManager = Dependency.get(GroupExpansionManager.class);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- if (mAnimatedInsets) {
+ if (mAnimatedInsets.isEnabled()) {
setWindowInsetsAnimationCallback(mInsetsCallback);
}
}
@@ -730,11 +734,6 @@
}
@VisibleForTesting
- void setAnimatedInsetsEnabled(boolean enabled) {
- mAnimatedInsets = enabled;
- }
-
- @VisibleForTesting
public void updateFooter() {
if (mFooterView == null) {
return;
@@ -1773,7 +1772,7 @@
return;
}
mForcedScroll = v;
- if (mAnimatedInsets) {
+ if (mAnimatedInsets.isEnabled()) {
updateForcedScroll();
} else {
scrollTo(v);
@@ -1822,7 +1821,7 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- if (!mAnimatedInsets) {
+ if (!mAnimatedInsets.isEnabled()) {
mBottomInset = insets.getInsets(WindowInsets.Type.ime()).bottom;
}
mWaterfallTopInset = 0;
@@ -1830,11 +1829,11 @@
if (cutout != null) {
mWaterfallTopInset = cutout.getWaterfallInsets().top;
}
- if (mAnimatedInsets && !mIsInsetAnimationRunning) {
+ if (mAnimatedInsets.isEnabled() && !mIsInsetAnimationRunning) {
// update bottom inset e.g. after rotation
updateBottomInset(insets);
}
- if (!mAnimatedInsets) {
+ if (!mAnimatedInsets.isEnabled()) {
int range = getScrollRange();
if (mOwnScrollY > range) {
// HACK: We're repeatedly getting staggered insets here while the IME is
@@ -2714,7 +2713,7 @@
* @param listener callback for notification removed
*/
public void setOnNotificationRemovedListener(OnNotificationRemovedListener listener) {
- NotificationShelfController.assertRefactorFlagDisabled(mAmbientState.getFeatureFlags());
+ mShelfRefactor.assertDisabled();
mOnNotificationRemovedListener = listener;
}
@@ -2727,7 +2726,7 @@
if (!mChildTransferInProgress) {
onViewRemovedInternal(expandableView, this);
}
- if (mAmbientState.getFeatureFlags().isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ if (mShelfRefactor.isEnabled()) {
mShelf.requestRoundnessResetFor(expandableView);
} else {
if (mOnNotificationRemovedListener != null) {
@@ -4943,18 +4942,12 @@
@Nullable
public ExpandableView getShelf() {
- if (NotificationShelfController.checkRefactorFlagEnabled(mAmbientState.getFeatureFlags())) {
- return mShelf;
- } else {
- return null;
- }
+ if (!mShelfRefactor.expectEnabled()) return null;
+ return mShelf;
}
public void setShelf(NotificationShelf shelf) {
- if (!NotificationShelfController.checkRefactorFlagEnabled(
- mAmbientState.getFeatureFlags())) {
- return;
- }
+ if (!mShelfRefactor.expectEnabled()) return;
int index = -1;
if (mShelf != null) {
index = indexOfChild(mShelf);
@@ -4968,7 +4961,7 @@
}
public void setShelfController(NotificationShelfController notificationShelfController) {
- NotificationShelfController.assertRefactorFlagDisabled(mAmbientState.getFeatureFlags());
+ mShelfRefactor.assertDisabled();
int index = -1;
if (mShelf != null) {
index = indexOfChild(mShelf);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index ef7375a..4668aa4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -65,6 +65,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.shared.model.KeyguardState;
@@ -205,6 +206,7 @@
private boolean mIsInTransitionToAod = false;
private final FeatureFlags mFeatureFlags;
+ private final ViewRefactorFlag mShelfRefactor;
private final NotificationTargetsHelper mNotificationTargetsHelper;
private final SecureSettings mSecureSettings;
private final NotificationDismissibilityProvider mDismissibilityProvider;
@@ -718,6 +720,7 @@
mShadeController = shadeController;
mNotifIconAreaController = notifIconAreaController;
mFeatureFlags = featureFlags;
+ mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
mNotificationTargetsHelper = notificationTargetsHelper;
mSecureSettings = secureSettings;
mDismissibilityProvider = dismissibilityProvider;
@@ -1432,7 +1435,7 @@
}
public void setShelfController(NotificationShelfController notificationShelfController) {
- NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags);
+ mShelfRefactor.assertDisabled();
mView.setShelfController(notificationShelfController);
}
@@ -1645,12 +1648,12 @@
}
public void setShelf(NotificationShelf shelf) {
- if (!NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) return;
+ if (!mShelfRefactor.expectEnabled()) return;
mView.setShelf(shelf);
}
public int getShelfHeight() {
- if (!NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) {
+ if (!mShelfRefactor.expectEnabled()) {
return 0;
}
ExpandableView shelf = mView.getShelf();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 2b9c3d3..acd6e49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -259,8 +259,6 @@
void readyForKeyguardDone();
- void setLockscreenUser(int newUserId);
-
void showKeyguard();
boolean hideKeyguard();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 5fb729c..c3c9a61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -51,7 +51,6 @@
import android.app.StatusBarManager;
import android.app.TaskInfo;
import android.app.UiModeManager;
-import android.app.WallpaperInfo;
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
@@ -980,16 +979,6 @@
createAndAddWindows(result);
- if (mWallpaperSupported) {
- // Make sure we always have the most current wallpaper info.
- IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
- mBroadcastDispatcher.registerReceiver(mWallpaperChangedReceiver, wallpaperChangedFilter,
- null /* handler */, UserHandle.ALL);
- mWallpaperChangedReceiver.onReceive(mContext, null);
- } else if (DEBUG) {
- Log.v(TAG, "start(): no wallpaper service ");
- }
-
// Set up the initial notification state. This needs to happen before CommandQueue.disable()
setUpPresenter();
@@ -2072,6 +2061,7 @@
void updateDisplaySize() {
mDisplay.getMetrics(mDisplayMetrics);
mDisplay.getSize(mCurrentDisplaySize);
+ mMediaManager.onDisplayUpdated(mDisplay);
if (DEBUG_GESTURES) {
mGestureRec.tag("display",
String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
@@ -2164,18 +2154,6 @@
};
/**
- * Notify the shade controller that the current user changed
- *
- * @param newUserId userId of the new user
- */
- @Override
- public void setLockscreenUser(int newUserId) {
- if (mWallpaperSupported) {
- mWallpaperChangedReceiver.onReceive(mContext, null);
- }
- }
-
- /**
* Reload some of our resources when the configuration changes.
*
* We don't reload everything when the configuration changes -- we probably
@@ -3549,33 +3527,6 @@
}
};
- /**
- * @deprecated See {@link com.android.systemui.wallpapers.data.repository.WallpaperRepository}
- * instead.
- */
- private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!mWallpaperSupported) {
- // Receiver should not have been registered at all...
- Log.wtf(TAG, "WallpaperManager not supported");
- return;
- }
- WallpaperInfo info = mWallpaperManager.getWallpaperInfoForUser(
- mUserTracker.getUserId());
- mWallpaperController.onWallpaperInfoUpdated(info);
-
- final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
- // If WallpaperInfo is null, it must be ImageWallpaper.
- final boolean supportsAmbientMode = deviceSupportsAodWallpaper
- && (info != null && info.supportsAmbientMode());
-
- mNotificationShadeWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
- mKeyguardViewMediator.setWallpaperSupportsAmbientMode(supportsAmbientMode);
- }
- };
-
private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
@Override
public void onConfigChanged(Configuration newConfig) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index 8e9f382..374543d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.phone;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.os.Bundle;
@@ -34,10 +33,7 @@
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.StatusIconDisplayable;
-import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger;
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView;
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
@@ -52,7 +48,6 @@
private static final String TAG = "DemoStatusIcons";
private final LinearLayout mStatusIcons;
- private final ArrayList<StatusBarMobileView> mMobileViews = new ArrayList<>();
private final ArrayList<ModernStatusBarMobileView> mModernMobileViews = new ArrayList<>();
private final int mIconSize;
@@ -91,7 +86,6 @@
}
public void remove() {
- mMobileViews.clear();
((ViewGroup) getParent()).removeView(this);
}
@@ -127,7 +121,6 @@
mDemoMode = false;
mStatusIcons.setVisibility(View.VISIBLE);
mModernMobileViews.clear();
- mMobileViews.clear();
setVisibility(View.GONE);
}
@@ -236,22 +229,6 @@
}
/**
- * Add a new mobile icon view
- */
- public void addMobileView(MobileIconState state, Context mobileContext) {
- Log.d(TAG, "addMobileView: ");
- StatusBarMobileView view = StatusBarMobileView
- .fromContext(mobileContext, state.slot);
-
- view.applyMobileState(state);
- view.setStaticDrawableColor(mColor);
-
- // mobile always goes at the end
- mMobileViews.add(view);
- addView(view, getChildCount(), createLayoutParams());
- }
-
- /**
* Add a {@link ModernStatusBarMobileView}
* @param mobileContext possibly mcc/mnc overridden mobile context
* @param subId the subscriptionId for this mobile view
@@ -285,8 +262,7 @@
// If we have mobile views, put wifi before them
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
- if (child instanceof StatusBarMobileView
- || child instanceof ModernStatusBarMobileView) {
+ if (child instanceof ModernStatusBarMobileView) {
viewIndex = i;
break;
}
@@ -297,26 +273,6 @@
addView(view, viewIndex, createLayoutParams());
}
- /**
- * Apply an update to a mobile icon view for the given {@link MobileIconState}. For
- * compatibility with {@link MobileContextProvider}, we have to recreate the view every time we
- * update it, since the context (and thus the {@link Configuration}) may have changed
- */
- public void updateMobileState(MobileIconState state, Context mobileContext) {
- Log.d(TAG, "updateMobileState: " + state);
-
- // The mobile config provided by MobileContextProvider could have changed; always recreate
- for (int i = 0; i < mMobileViews.size(); i++) {
- StatusBarMobileView view = mMobileViews.get(i);
- if (view.getState().subId == state.subId) {
- removeView(view);
- }
- }
-
- // Add the replacement or new icon
- addMobileView(state, mobileContext);
- }
-
public void onRemoveIcon(StatusIconDisplayable view) {
if (view.getSlot().equals("wifi")) {
if (view instanceof ModernStatusBarWifiView) {
@@ -324,12 +280,6 @@
removeView(mModernWifiView);
mModernWifiView = null;
}
- } else if (view instanceof StatusBarMobileView) {
- StatusBarMobileView mobileView = matchingMobileView(view);
- if (mobileView != null) {
- removeView(mobileView);
- mMobileViews.remove(mobileView);
- }
} else if (view instanceof ModernStatusBarMobileView) {
ModernStatusBarMobileView mobileView = matchingModernMobileView(
(ModernStatusBarMobileView) view);
@@ -340,21 +290,6 @@
}
}
- private StatusBarMobileView matchingMobileView(StatusIconDisplayable otherView) {
- if (!(otherView instanceof StatusBarMobileView)) {
- return null;
- }
-
- StatusBarMobileView v = (StatusBarMobileView) otherView;
- for (StatusBarMobileView view : mMobileViews) {
- if (view.getState().subId == v.getState().subId) {
- return view;
- }
- }
-
- return null;
- }
-
private ModernStatusBarMobileView matchingModernMobileView(ModernStatusBarMobileView other) {
for (ModernStatusBarMobileView v : mModernMobileViews) {
if (v.getSubId() == other.getSubId()) {
@@ -376,9 +311,6 @@
if (mModernWifiView != null) {
mModernWifiView.onDarkChanged(areas, darkIntensity, tint);
}
- for (StatusBarMobileView view : mMobileViews) {
- view.onDarkChanged(areas, darkIntensity, tint);
- }
for (ModernStatusBarMobileView view : mModernMobileViews) {
view.onDarkChanged(areas, darkIntensity, tint);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 7312db6..ed9722e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -311,6 +311,7 @@
@Override
public void dozeTimeTick() {
+ mDozeInteractor.dozeTimeTick();
mNotificationPanel.dozeTimeTick();
mAuthController.dozeTimeTick();
if (mAmbientIndicationContainer instanceof DozeReceiver) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index b2c39f7..92c786f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -306,19 +306,25 @@
/**
* Drawable that aligns left horizontally and center vertically (like ImageWallpaper).
+ *
+ * <p>Aligns to the center when showing on the smaller internal display of a multi display
+ * device.
*/
public static class WallpaperDrawable extends DrawableWrapper {
private final ConstantState mState;
private final Rect mTmpRect = new Rect();
+ private boolean mIsOnSmallerInternalDisplays;
- public WallpaperDrawable(Resources r, Bitmap b) {
- this(r, new ConstantState(b));
+ public WallpaperDrawable(Resources r, Bitmap b, boolean isOnSmallerInternalDisplays) {
+ this(r, new ConstantState(b), isOnSmallerInternalDisplays);
}
- private WallpaperDrawable(Resources r, ConstantState state) {
+ private WallpaperDrawable(Resources r, ConstantState state,
+ boolean isOnSmallerInternalDisplays) {
super(new BitmapDrawable(r, state.mBackground));
mState = state;
+ mIsOnSmallerInternalDisplays = isOnSmallerInternalDisplays;
}
@Override
@@ -357,10 +363,17 @@
}
dy = (vheight - dheight * scale) * 0.5f;
+ int offsetX = 0;
+ // Offset to show the center area of the wallpaper on a smaller display for multi
+ // display device
+ if (mIsOnSmallerInternalDisplays) {
+ offsetX = bounds.centerX() - (Math.round(dwidth * scale) / 2);
+ }
+
mTmpRect.set(
- bounds.left,
+ bounds.left + offsetX,
bounds.top + Math.round(dy),
- bounds.left + Math.round(dwidth * scale),
+ bounds.left + Math.round(dwidth * scale) + offsetX,
bounds.top + Math.round(dheight * scale + dy));
super.onBoundsChange(mTmpRect);
@@ -371,6 +384,17 @@
return mState;
}
+ /**
+ * Update bounds when the hosting display or the display size has changed.
+ *
+ * @param isOnSmallerInternalDisplays true if the drawable is on one of the internal
+ * displays with the smaller area.
+ */
+ public void onDisplayUpdated(boolean isOnSmallerInternalDisplays) {
+ mIsOnSmallerInternalDisplays = isOnSmallerInternalDisplays;
+ onBoundsChange(getBounds());
+ }
+
static class ConstantState extends Drawable.ConstantState {
private final Bitmap mBackground;
@@ -386,7 +410,7 @@
@Override
public Drawable newDrawable(@Nullable Resources res) {
- return new WallpaperDrawable(res, this);
+ return new WallpaperDrawable(res, this, /* isOnSmallerInternalDisplays= */ false);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index e18c9d8..0bf0f4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -24,6 +24,8 @@
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -88,7 +90,7 @@
private final ArrayList<Rect> mTintAreas = new ArrayList<>();
private final Context mContext;
- private final FeatureFlags mFeatureFlags;
+ private final ViewRefactorFlag mShelfRefactor;
private int mAodIconAppearTranslation;
@@ -120,12 +122,13 @@
Optional<Bubbles> bubblesOptional,
DemoModeController demoModeController,
DarkIconDispatcher darkIconDispatcher,
- FeatureFlags featureFlags, StatusBarWindowController statusBarWindowController,
+ FeatureFlags featureFlags,
+ StatusBarWindowController statusBarWindowController,
ScreenOffAnimationController screenOffAnimationController) {
mContrastColorUtil = ContrastColorUtil.getInstance(context);
mContext = context;
mStatusBarStateController = statusBarStateController;
- mFeatureFlags = featureFlags;
+ mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
mStatusBarStateController.addCallback(this);
mMediaManager = notificationMediaManager;
mDozeParameters = dozeParameters;
@@ -179,12 +182,12 @@
}
public void setupShelf(NotificationShelfController notificationShelfController) {
- NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags);
+ mShelfRefactor.assertDisabled();
mShelfIcons = notificationShelfController.getShelfIcons();
}
public void setShelfIcons(NotificationIconContainer icons) {
- if (NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) {
+ if (mShelfRefactor.expectEnabled()) {
mShelfIcons = icons;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 1d934d6..79151fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -548,7 +548,7 @@
final int iconResId = mUserManager.getUserStatusBarIconResId(userId);
// TODO(b/170249807, b/230779281): Handle non-managed-profile String
String accessibilityString = getManagedProfileAccessibilityString();
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
final boolean showIcon;
if (iconResId != Resources.ID_NULL && (!mKeyguardStateController.isShowing()
|| mKeyguardStateController.isOccluded())) {
@@ -629,6 +629,13 @@
}
@Override
+ public void appTransitionFinished(int displayId) {
+ if (mDisplayId == displayId) {
+ updateProfileIcon();
+ }
+ }
+
+ @Override
public void onKeyguardShowingChanged() {
updateProfileIcon();
}
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 42b0a4f..d5cb6b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -15,7 +15,6 @@
package com.android.systemui.statusbar.phone;
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON;
-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_NEW;
@@ -24,10 +23,8 @@
import android.os.Bundle;
import android.text.TextUtils;
import android.util.ArraySet;
-import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
@@ -41,12 +38,9 @@
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.BaseStatusBarFrameLayout;
import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconsBinder;
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView;
@@ -100,13 +94,10 @@
*/
void setNewWifiIcon();
- /** */
- void setMobileIcons(String slot, List<MobileIconState> states);
-
/**
- * This method completely replaces {@link #setMobileIcons} with the information from the new
- * mobile data pipeline. Icons will automatically keep their state up to date, so we don't have
- * to worry about funneling MobileIconState objects through anymore.
+ * Notify this class that there is a new set of mobile icons to display, keyed off of this list
+ * of subIds. The icons will be added and bound to the mobile data pipeline via
+ * {@link com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconBinder}.
*/
void setNewMobileIconSubIds(List<Integer> subIds);
/**
@@ -168,14 +159,12 @@
public DarkIconManager(
LinearLayout linearLayout,
StatusBarLocation location,
- StatusBarPipelineFlags statusBarPipelineFlags,
WifiUiAdapter wifiUiAdapter,
MobileUiAdapter mobileUiAdapter,
MobileContextProvider mobileContextProvider,
DarkIconDispatcher darkIconDispatcher) {
super(linearLayout,
location,
- statusBarPipelineFlags,
wifiUiAdapter,
mobileUiAdapter,
mobileContextProvider);
@@ -235,7 +224,6 @@
@SysUISingleton
public static class Factory {
- private final StatusBarPipelineFlags mStatusBarPipelineFlags;
private final WifiUiAdapter mWifiUiAdapter;
private final MobileContextProvider mMobileContextProvider;
private final MobileUiAdapter mMobileUiAdapter;
@@ -243,12 +231,10 @@
@Inject
public Factory(
- StatusBarPipelineFlags statusBarPipelineFlags,
WifiUiAdapter wifiUiAdapter,
MobileContextProvider mobileContextProvider,
MobileUiAdapter mobileUiAdapter,
DarkIconDispatcher darkIconDispatcher) {
- mStatusBarPipelineFlags = statusBarPipelineFlags;
mWifiUiAdapter = wifiUiAdapter;
mMobileContextProvider = mobileContextProvider;
mMobileUiAdapter = mobileUiAdapter;
@@ -259,7 +245,6 @@
return new DarkIconManager(
group,
location,
- mStatusBarPipelineFlags,
mWifiUiAdapter,
mMobileUiAdapter,
mMobileContextProvider,
@@ -277,14 +262,12 @@
public TintedIconManager(
ViewGroup group,
StatusBarLocation location,
- StatusBarPipelineFlags statusBarPipelineFlags,
WifiUiAdapter wifiUiAdapter,
MobileUiAdapter mobileUiAdapter,
MobileContextProvider mobileContextProvider
) {
super(group,
location,
- statusBarPipelineFlags,
wifiUiAdapter,
mobileUiAdapter,
mobileContextProvider);
@@ -319,19 +302,16 @@
@SysUISingleton
public static class Factory {
- private final StatusBarPipelineFlags mStatusBarPipelineFlags;
private final WifiUiAdapter mWifiUiAdapter;
private final MobileContextProvider mMobileContextProvider;
private final MobileUiAdapter mMobileUiAdapter;
@Inject
public Factory(
- StatusBarPipelineFlags statusBarPipelineFlags,
WifiUiAdapter wifiUiAdapter,
MobileUiAdapter mobileUiAdapter,
MobileContextProvider mobileContextProvider
) {
- mStatusBarPipelineFlags = statusBarPipelineFlags;
mWifiUiAdapter = wifiUiAdapter;
mMobileUiAdapter = mobileUiAdapter;
mMobileContextProvider = mobileContextProvider;
@@ -341,7 +321,6 @@
return new TintedIconManager(
group,
location,
- mStatusBarPipelineFlags,
mWifiUiAdapter,
mMobileUiAdapter,
mMobileContextProvider);
@@ -354,7 +333,6 @@
*/
class IconManager implements DemoModeCommandReceiver {
protected final ViewGroup mGroup;
- private final StatusBarPipelineFlags mStatusBarPipelineFlags;
private final MobileContextProvider mMobileContextProvider;
private final LocationBasedWifiViewModel mWifiViewModel;
private final MobileIconsViewModel mMobileIconsViewModel;
@@ -376,27 +354,21 @@
public IconManager(
ViewGroup group,
StatusBarLocation location,
- StatusBarPipelineFlags statusBarPipelineFlags,
WifiUiAdapter wifiUiAdapter,
MobileUiAdapter mobileUiAdapter,
MobileContextProvider mobileContextProvider
) {
mGroup = group;
- mStatusBarPipelineFlags = statusBarPipelineFlags;
mMobileContextProvider = mobileContextProvider;
mContext = group.getContext();
mLocation = location;
reloadDimens();
- 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.getMobileIconsViewModel();
- MobileIconsBinder.bind(mGroup, mMobileIconsViewModel);
- } else {
- mMobileIconsViewModel = null;
- }
+ // This starts the flow for the new pipeline, and will notify us of changes via
+ // {@link #setNewMobileIconIds}
+ mMobileIconsViewModel = mobileUiAdapter.getMobileIconsViewModel();
+ MobileIconsBinder.bind(mGroup, mMobileIconsViewModel);
mWifiViewModel = wifiUiAdapter.bindGroup(mGroup, mLocation);
}
@@ -449,9 +421,6 @@
case TYPE_WIFI_NEW:
return addNewWifiIcon(index, slot);
- case TYPE_MOBILE:
- return addMobileIcon(index, slot, holder.getMobileState());
-
case TYPE_MOBILE_NEW:
return addNewMobileIcon(index, slot, holder.getTag());
}
@@ -479,40 +448,12 @@
return view;
}
- @VisibleForTesting
- protected StatusIconDisplayable addMobileIcon(
- int index,
- String slot,
- MobileIconState state
- ) {
- if (mStatusBarPipelineFlags.useNewMobileIcons()) {
- throw new IllegalStateException("Attempting to add a mobile icon while the new "
- + "icons are enabled is not supported");
- }
-
- // Use the `subId` field as a key to query for the correct context
- StatusBarMobileView mobileView = onCreateStatusBarMobileView(state.subId, slot);
- mobileView.applyMobileState(state);
- mGroup.addView(mobileView, index, onCreateLayoutParams());
-
- if (mIsInDemoMode) {
- Context mobileContext = mMobileContextProvider
- .getMobileContextForSub(state.subId, mContext);
- mDemoStatusIcons.addMobileView(state, mobileContext);
- }
- return mobileView;
- }
protected StatusIconDisplayable addNewMobileIcon(
int index,
String slot,
int subId
) {
- if (!mStatusBarPipelineFlags.useNewMobileIcons()) {
- throw new IllegalStateException("Attempting to add a mobile icon using the new"
- + "pipeline, but the enabled flag is false.");
- }
-
BaseStatusBarFrameLayout view = onCreateModernStatusBarMobileView(slot, subId);
mGroup.addView(view, index, onCreateLayoutParams());
@@ -536,13 +477,6 @@
return ModernStatusBarWifiView.constructAndBind(mContext, slot, mWifiViewModel);
}
- private StatusBarMobileView onCreateStatusBarMobileView(int subId, String slot) {
- Context mobileContext = mMobileContextProvider.getMobileContextForSub(subId, mContext);
- StatusBarMobileView view = StatusBarMobileView
- .fromContext(mobileContext, slot);
- return view;
- }
-
private ModernStatusBarMobileView onCreateModernStatusBarMobileView(
String slot, int subId) {
Context mobileContext = mMobileContextProvider.getMobileContextForSub(subId, mContext);
@@ -568,15 +502,6 @@
com.android.internal.R.dimen.status_bar_icon_size_sp);
}
- private void setHeightAndCenter(ImageView imageView, int height) {
- ViewGroup.LayoutParams params = imageView.getLayoutParams();
- params.height = height;
- if (params instanceof LinearLayout.LayoutParams) {
- ((LinearLayout.LayoutParams) params).gravity = Gravity.CENTER_VERTICAL;
- }
- imageView.setLayoutParams(params);
- }
-
protected void onRemoveIcon(int viewIndex) {
if (mIsInDemoMode) {
mDemoStatusIcons.onRemoveIcon((StatusIconDisplayable) mGroup.getChildAt(viewIndex));
@@ -594,9 +519,6 @@
case TYPE_ICON:
onSetIcon(viewIndex, holder.getIcon());
return;
- case TYPE_MOBILE:
- onSetMobileIcon(viewIndex, holder.getMobileState());
- return;
case TYPE_MOBILE_NEW:
case TYPE_WIFI_NEW:
// Nothing, the new icons update themselves
@@ -606,23 +528,6 @@
}
}
- public void onSetMobileIcon(int viewIndex, MobileIconState state) {
- View view = mGroup.getChildAt(viewIndex);
- if (view instanceof StatusBarMobileView) {
- ((StatusBarMobileView) view).applyMobileState(state);
- } else {
- // ModernStatusBarMobileView automatically updates via the ViewModel
- throw new IllegalStateException("Cannot update ModernStatusBarMobileView outside of"
- + "the new pipeline");
- }
-
- if (mIsInDemoMode) {
- Context mobileContext = mMobileContextProvider
- .getMobileContextForSub(state.subId, mContext);
- mDemoStatusIcons.updateMobileState(state, mobileContext);
- }
- }
-
@Override
public void dispatchDemoCommand(String command, Bundle args) {
if (!mDemoable) {
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 d1a02d6..553cbc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -39,7 +39,6 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -215,41 +214,10 @@
/**
* 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.
- * @param slot da slot
- * @param iconStates All of the mobile icon states
+ * @param subIds list of subscription ID integers that provide the key to the icon to display.
*/
@Override
- public void setMobileIcons(String slot, List<MobileIconState> iconStates) {
- if (mStatusBarPipelineFlags.useNewMobileIcons()) {
- Log.d(TAG, "ignoring old pipeline callbacks, because the new mobile "
- + "icons are enabled");
- return;
- }
- Slot mobileSlot = mStatusBarIconList.getSlot(slot);
-
- // Reverse the sort order to show icons with left to right([Slot1][Slot2]..).
- // StatusBarIconList has UI design that first items go to the right of second items.
- Collections.reverse(iconStates);
-
- for (MobileIconState state : iconStates) {
- StatusBarIconHolder holder = mobileSlot.getHolderForTag(state.subId);
- if (holder == null) {
- holder = StatusBarIconHolder.fromMobileIconState(state);
- setIcon(slot, holder);
- } else {
- holder.setMobileState(state);
- handleSet(slot, holder);
- }
- }
- }
-
- @Override
public void setNewMobileIconSubIds(List<Integer> subIds) {
- if (!mStatusBarPipelineFlags.useNewMobileIcons()) {
- Log.d(TAG, "ignoring new pipeline callback, "
- + "since the new mobile icons are disabled");
- return;
- }
String slotName = mContext.getString(com.android.internal.R.string.status_bar_mobile);
Slot mobileSlot = mStatusBarIconList.getSlot(slotName);
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 01fd247..7048a78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
@@ -24,7 +24,6 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel;
import java.lang.annotation.Retention;
@@ -35,7 +34,6 @@
*/
public class StatusBarIconHolder {
public static final int TYPE_ICON = 0;
- public static final int TYPE_MOBILE = 2;
/**
* TODO (b/249790733): address this once the new pipeline is in place
* This type exists so that the new pipeline (see {@link MobileIconViewModel}) can be used
@@ -63,7 +61,6 @@
@IntDef({
TYPE_ICON,
- TYPE_MOBILE,
TYPE_MOBILE_NEW,
TYPE_WIFI_NEW
})
@@ -71,7 +68,6 @@
@interface IconType {}
private StatusBarIcon mIcon;
- private MobileIconState mMobileState;
private @IconType int mType = TYPE_ICON;
private int mTag = 0;
@@ -79,7 +75,6 @@
public static String getTypeString(@IconType int type) {
switch(type) {
case TYPE_ICON: return "ICON";
- case TYPE_MOBILE: return "MOBILE_OLD";
case TYPE_MOBILE_NEW: return "MOBILE_NEW";
case TYPE_WIFI_NEW: return "WIFI_NEW";
default: return "UNKNOWN";
@@ -103,15 +98,6 @@
return holder;
}
- /** */
- public static StatusBarIconHolder fromMobileIconState(MobileIconState state) {
- StatusBarIconHolder holder = new StatusBarIconHolder();
- holder.mMobileState = state;
- holder.mType = TYPE_MOBILE;
- holder.mTag = state.subId;
- return holder;
- }
-
/**
* ONLY for use with the new connectivity pipeline, where we only need a subscriptionID to
* determine icon ordering and building the correct view model
@@ -153,21 +139,10 @@
mIcon = icon;
}
- @Nullable
- public MobileIconState getMobileState() {
- return mMobileState;
- }
-
- public void setMobileState(MobileIconState state) {
- mMobileState = state;
- }
-
public boolean isVisible() {
switch (mType) {
case TYPE_ICON:
return mIcon.visible;
- case TYPE_MOBILE:
- return mMobileState.visible;
case TYPE_MOBILE_NEW:
case TYPE_WIFI_NEW:
// The new pipeline controls visibilities via the view model and view binder, so
@@ -188,10 +163,6 @@
mIcon.visible = visible;
break;
- case TYPE_MOBILE:
- mMobileState.visible = visible;
- break;
-
case TYPE_MOBILE_NEW:
case TYPE_WIFI_NEW:
// The new pipeline controls visibilities via the view model and view binder, so
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index e63fecd..ad8530d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -79,7 +79,6 @@
private final HeadsUpManagerPhone mHeadsUpManager;
private final AboveShelfObserver mAboveShelfObserver;
private final DozeScrimController mDozeScrimController;
- private final CentralSurfaces mCentralSurfaces;
private final NotificationsInteractor mNotificationsInteractor;
private final NotificationStackScrollLayoutController mNsslController;
private final LockscreenShadeTransitionController mShadeTransitionController;
@@ -107,7 +106,6 @@
NotificationShadeWindowController notificationShadeWindowController,
DynamicPrivacyController dynamicPrivacyController,
KeyguardStateController keyguardStateController,
- CentralSurfaces centralSurfaces,
NotificationsInteractor notificationsInteractor,
LockscreenShadeTransitionController shadeTransitionController,
PowerInteractor powerInteractor,
@@ -128,8 +126,6 @@
mQsController = quickSettingsController;
mHeadsUpManager = headsUp;
mDynamicPrivacyController = dynamicPrivacyController;
- // TODO: use KeyguardStateController#isOccluded to remove this dependency
- mCentralSurfaces = centralSurfaces;
mNotificationsInteractor = notificationsInteractor;
mNsslController = stackScrollerController;
mShadeTransitionController = shadeTransitionController;
@@ -208,7 +204,6 @@
// End old BaseStatusBar.userSwitched
mCommandQueue.animateCollapsePanels();
mMediaManager.clearCurrentMediaNotification();
- mCentralSurfaces.setLockscreenUser(newUserId);
updateMediaMetaData(true, false);
}
@@ -284,7 +279,7 @@
@Override
public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
- if (mCentralSurfaces.isOccluded()) {
+ if (mKeyguardStateController.isOccluded()) {
boolean devicePublic = mLockscreenUserManager
.isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
boolean userPublic = devicePublic
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 6919996..344e56c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.content.Context;
import android.os.Handler;
-import android.telephony.SubscriptionInfo;
import android.util.ArraySet;
import android.util.Log;
@@ -27,7 +26,6 @@
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.connectivity.IconState;
-import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.policy.SecurityController;
@@ -71,7 +69,6 @@
// Track as little state as possible, and only for padding purposes
private boolean mIsAirplaneMode = false;
- private ArrayList<MobileIconState> mMobileStates = new ArrayList<>();
private ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>();
private boolean mInitialized;
@@ -102,7 +99,7 @@
mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
}
- /** Call to initilaize and register this classw with the system. */
+ /** Call to initialize and register this class with the system. */
public void init() {
if (mInitialized) {
return;
@@ -189,34 +186,6 @@
CallIndicatorIconState.copyStates(mCallIndicatorStates));
}
- @Override
- public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
- if (DEBUG) {
- Log.d(TAG, "setMobileDataIndicators: " + indicators);
- }
- MobileIconState state = getState(indicators.subId);
- if (state == null) {
- return;
- }
-
- state.visible = indicators.statusIcon.visible && !mHideMobile;
- state.strengthId = indicators.statusIcon.icon;
- state.typeId = indicators.statusType;
- state.contentDescription = indicators.statusIcon.contentDescription;
- state.typeContentDescription = indicators.typeContentDescription;
- state.showTriangle = indicators.showTriangle;
- state.roaming = indicators.roaming;
- state.activityIn = indicators.activityIn && mActivityEnabled;
- state.activityOut = indicators.activityOut && mActivityEnabled;
-
- if (DEBUG) {
- Log.d(TAG, "MobileIconStates: "
- + (mMobileStates == null ? "" : mMobileStates.toString()));
- }
- // Always send a copy to maintain value type semantics
- mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates));
- }
-
private CallIndicatorIconState getNoCallingState(int subId) {
for (CallIndicatorIconState state : mCallIndicatorStates) {
if (state.subId == subId) {
@@ -227,74 +196,8 @@
return null;
}
- private MobileIconState getState(int subId) {
- for (MobileIconState state : mMobileStates) {
- if (state.subId == subId) {
- return state;
- }
- }
- Log.e(TAG, "Unexpected subscription " + subId);
- return null;
- }
-
- /**
- * It is expected that a call to setSubs will be immediately followed by setMobileDataIndicators
- * so we don't have to update the icon manager at this point, just remove the old ones
- * @param subs list of mobile subscriptions, displayed as mobile data indicators (max 8)
- */
- @Override
- public void setSubs(List<SubscriptionInfo> subs) {
- if (DEBUG) Log.d(TAG, "setSubs: " + (subs == null ? "" : subs.toString()));
- if (hasCorrectSubs(subs)) {
- return;
- }
-
- mIconController.removeAllIconsForSlot(mSlotMobile);
- mIconController.removeAllIconsForSlot(mSlotNoCalling);
- mIconController.removeAllIconsForSlot(mSlotCallStrength);
- mMobileStates.clear();
- List<CallIndicatorIconState> noCallingStates = new ArrayList<CallIndicatorIconState>();
- noCallingStates.addAll(mCallIndicatorStates);
- mCallIndicatorStates.clear();
- final int n = subs.size();
- for (int i = 0; i < n; i++) {
- mMobileStates.add(new MobileIconState(subs.get(i).getSubscriptionId()));
- boolean isNewSub = true;
- for (CallIndicatorIconState state : noCallingStates) {
- if (state.subId == subs.get(i).getSubscriptionId()) {
- mCallIndicatorStates.add(state);
- isNewSub = false;
- break;
- }
- }
- if (isNewSub) {
- mCallIndicatorStates.add(
- new CallIndicatorIconState(subs.get(i).getSubscriptionId()));
- }
- }
- }
-
- private boolean hasCorrectSubs(List<SubscriptionInfo> subs) {
- final int N = subs.size();
- if (N != mMobileStates.size()) {
- return false;
- }
- for (int i = 0; i < N; i++) {
- if (mMobileStates.get(i).subId != subs.get(i).getSubscriptionId()) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public void setNoSims(boolean show, boolean simDetected) {
- // Noop yay!
- }
-
@Override
public void setEthernetIndicators(IconState state) {
- boolean visible = state.visible && !mHideEthernet;
int resId = state.icon;
String description = state.contentDescription;
@@ -324,11 +227,6 @@
}
}
- @Override
- public void setMobileDataEnabled(boolean enabled) {
- // Don't care.
- }
-
/**
* Stores the statusbar state for no Calling & SMS.
*/
@@ -388,117 +286,4 @@
return outStates;
}
}
-
- private static abstract class SignalIconState {
- public boolean visible;
- public boolean activityOut;
- public boolean activityIn;
- public String slot;
- public String contentDescription;
-
- @Override
- public boolean equals(Object o) {
- // Skipping reference equality bc this should be more of a value type
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- SignalIconState that = (SignalIconState) o;
- return visible == that.visible &&
- activityOut == that.activityOut &&
- activityIn == that.activityIn &&
- Objects.equals(contentDescription, that.contentDescription) &&
- Objects.equals(slot, that.slot);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(visible, activityOut, slot);
- }
-
- protected void copyTo(SignalIconState other) {
- other.visible = visible;
- other.activityIn = activityIn;
- other.activityOut = activityOut;
- other.slot = slot;
- other.contentDescription = contentDescription;
- }
- }
-
- /**
- * A little different. This one delegates to SignalDrawable instead of a specific resId
- */
- public static class MobileIconState extends SignalIconState {
- public int subId;
- public int strengthId;
- public int typeId;
- public boolean showTriangle;
- public boolean roaming;
- public boolean needsLeadingPadding;
- public CharSequence typeContentDescription;
-
- private MobileIconState(int subId) {
- super();
- this.subId = subId;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- if (!super.equals(o)) {
- return false;
- }
- MobileIconState that = (MobileIconState) o;
- return subId == that.subId
- && strengthId == that.strengthId
- && typeId == that.typeId
- && showTriangle == that.showTriangle
- && roaming == that.roaming
- && needsLeadingPadding == that.needsLeadingPadding
- && Objects.equals(typeContentDescription, that.typeContentDescription);
- }
-
- @Override
- public int hashCode() {
-
- return Objects
- .hash(super.hashCode(), subId, strengthId, typeId, showTriangle, roaming,
- needsLeadingPadding, typeContentDescription);
- }
-
- public MobileIconState copy() {
- MobileIconState copy = new MobileIconState(this.subId);
- copyTo(copy);
- return copy;
- }
-
- public void copyTo(MobileIconState other) {
- super.copyTo(other);
- other.subId = subId;
- other.strengthId = strengthId;
- other.typeId = typeId;
- other.showTriangle = showTriangle;
- other.roaming = roaming;
- other.needsLeadingPadding = needsLeadingPadding;
- other.typeContentDescription = typeContentDescription;
- }
-
- private static List<MobileIconState> copyStates(List<MobileIconState> inStates) {
- ArrayList<MobileIconState> outStates = new ArrayList<>();
- for (MobileIconState state : inStates) {
- MobileIconState copy = new MobileIconState(state.subId);
- state.copyTo(copy);
- outStates.add(copy);
- }
-
- return outStates;
- }
-
- @Override public String toString() {
- return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId
- + ", showTriangle=" + showTriangle + ", roaming=" + roaming
- + ", typeId=" + typeId + ", visible=" + visible + ")";
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManagerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManagerExt.kt
new file mode 100644
index 0000000..fbc6b95
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManagerExt.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/** Whether dialogs are requesting for affordances to be hidden or not. */
+val SystemUIDialogManager.hideAffordancesRequest: Flow<Boolean>
+ get() = conflatedCallbackFlow {
+ val callback =
+ SystemUIDialogManager.Listener { hideAffordance ->
+ trySendWithFailureLogging(hideAffordance, "dialogHideAffordancesRequest")
+ }
+ registerListener(callback)
+ trySendWithFailureLogging(shouldHideAffordance(), "dialogHideAffordancesRequestInitial")
+ awaitClose { unregisterListener(callback) }
+ }
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 29829e4..6e51ed0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
@@ -18,8 +18,6 @@
import android.content.Context
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import javax.inject.Inject
/** All flagging methods related to the new status bar pipeline (see b/238425913). */
@@ -28,30 +26,10 @@
@Inject
constructor(
context: Context,
- private val featureFlags: FeatureFlags,
) {
private val mobileSlot = context.getString(com.android.internal.R.string.status_bar_mobile)
private val wifiSlot = context.getString(com.android.internal.R.string.status_bar_wifi)
- /** 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()
-
- /**
- * Returns true if we should apply some coloring to the icons that were rendered with the new
- * pipeline to help with debugging.
- */
- fun useDebugColoring(): Boolean =
- featureFlags.isEnabled(Flags.NEW_STATUS_BAR_ICONS_DEBUG_COLORING)
-
/**
* For convenience in the StatusBarIconController, we want to gate some actions based on slot
* name and the flag together.
@@ -59,5 +37,5 @@
* @return true if this icon is controlled by any of the status bar pipeline flags
*/
fun isIconControlledByFlags(slotName: String): Boolean =
- slotName == wifiSlot || (slotName == mobileSlot && useNewMobileIcons())
+ slotName == wifiSlot || slotName == mobileSlot
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 27cc64f..0e99c67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -19,12 +19,10 @@
import android.net.wifi.WifiManager
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.TableLogBufferFactory
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModelImpl
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepositoryImpl
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
@@ -46,6 +44,8 @@
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder
import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinderImpl
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModelImpl
import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositorySwitcher
@@ -58,9 +58,9 @@
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
-import kotlinx.coroutines.flow.Flow
import java.util.function.Supplier
import javax.inject.Named
+import kotlinx.coroutines.flow.Flow
@Module
abstract class StatusBarPipelineModule {
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 a05ab84..d7fcf48 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,7 +20,6 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.phone.StatusBarIconController
-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 java.io.PrintWriter
@@ -46,24 +45,18 @@
val mobileIconsViewModel: MobileIconsViewModel,
private val logger: MobileViewLogger,
@Application private val scope: CoroutineScope,
- private val statusBarPipelineFlags: StatusBarPipelineFlags,
) : CoreStartable {
private var isCollecting: Boolean = false
private var lastValue: List<Int>? = null
override fun start() {
- // 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()) {
- scope.launch {
- isCollecting = true
- mobileIconsViewModel.subscriptionIdsFlow.collectLatest {
- logger.logUiAdapterSubIdsSentToIconController(it)
- lastValue = it
- iconController.setNewMobileIconSubIds(it)
- }
+ // Start notifying the icon controller of subscriptions
+ scope.launch {
+ isCollecting = true
+ mobileIconsViewModel.subscriptionIdsFlow.collectLatest {
+ logger.logUiAdapterSubIdsSentToIconController(it)
+ lastValue = it
+ iconController.setNewMobileIconSubIds(it)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index a2a247a..c221109 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -183,16 +183,10 @@
}
override fun onIconTintChanged(newTint: Int) {
- if (viewModel.useDebugColoring) {
- return
- }
iconTint.value = newTint
}
override fun onDecorTintChanged(newTint: Int) {
- if (viewModel.useDebugColoring) {
- return
- }
decorTint.value = newTint
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt
index f775940..a51982c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt
@@ -18,7 +18,6 @@
import android.graphics.Color
import com.android.systemui.statusbar.phone.StatusBarLocation
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
/**
@@ -32,24 +31,14 @@
*/
abstract class LocationBasedMobileViewModel(
val commonImpl: MobileIconViewModelCommon,
- statusBarPipelineFlags: StatusBarPipelineFlags,
- debugTint: Int,
val locationName: String,
val verboseLogger: VerboseMobileViewLogger?,
) : MobileIconViewModelCommon by commonImpl {
- val useDebugColoring: Boolean = statusBarPipelineFlags.useDebugColoring()
-
- val defaultColor: Int =
- if (useDebugColoring) {
- debugTint
- } else {
- Color.WHITE
- }
+ val defaultColor: Int = Color.WHITE
companion object {
fun viewModelForLocation(
commonImpl: MobileIconViewModelCommon,
- statusBarPipelineFlags: StatusBarPipelineFlags,
verboseMobileViewLogger: VerboseMobileViewLogger,
loc: StatusBarLocation,
): LocationBasedMobileViewModel =
@@ -57,39 +46,31 @@
StatusBarLocation.HOME ->
HomeMobileIconViewModel(
commonImpl,
- statusBarPipelineFlags,
verboseMobileViewLogger,
)
- StatusBarLocation.KEYGUARD ->
- KeyguardMobileIconViewModel(commonImpl, statusBarPipelineFlags)
- StatusBarLocation.QS -> QsMobileIconViewModel(commonImpl, statusBarPipelineFlags)
+ StatusBarLocation.KEYGUARD -> KeyguardMobileIconViewModel(commonImpl)
+ StatusBarLocation.QS -> QsMobileIconViewModel(commonImpl)
}
}
}
class HomeMobileIconViewModel(
commonImpl: MobileIconViewModelCommon,
- statusBarPipelineFlags: StatusBarPipelineFlags,
verboseMobileViewLogger: VerboseMobileViewLogger,
) :
MobileIconViewModelCommon,
LocationBasedMobileViewModel(
commonImpl,
- statusBarPipelineFlags,
- debugTint = Color.CYAN,
locationName = "Home",
verboseMobileViewLogger,
)
class QsMobileIconViewModel(
commonImpl: MobileIconViewModelCommon,
- statusBarPipelineFlags: StatusBarPipelineFlags,
) :
MobileIconViewModelCommon,
LocationBasedMobileViewModel(
commonImpl,
- statusBarPipelineFlags,
- debugTint = Color.GREEN,
locationName = "QS",
// Only do verbose logging for the Home location.
verboseLogger = null,
@@ -97,13 +78,10 @@
class KeyguardMobileIconViewModel(
commonImpl: MobileIconViewModelCommon,
- statusBarPipelineFlags: StatusBarPipelineFlags,
) :
MobileIconViewModelCommon,
LocationBasedMobileViewModel(
commonImpl,
- statusBarPipelineFlags,
- debugTint = Color.MAGENTA,
locationName = "Keyguard",
// Only do verbose logging for the Home location.
verboseLogger = null,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 40b8c90..5cf887e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -98,7 +98,6 @@
val common = commonViewModelForSub(subId)
return LocationBasedMobileViewModel.viewModelForLocation(
common,
- statusBarPipelineFlags,
verboseLogger,
location,
)
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
index 6d71823..7a60d96 100644
--- 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
@@ -23,7 +23,6 @@
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.model.WifiIcon
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel.Companion.viewModelForLocation
@@ -46,7 +45,6 @@
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
@@ -60,8 +58,7 @@
statusBarIconGroup: ViewGroup,
location: StatusBarLocation,
): LocationBasedWifiViewModel {
- val locationViewModel =
- viewModelForLocation(wifiViewModel, statusBarPipelineFlags, location)
+ val locationViewModel = viewModelForLocation(wifiViewModel, location)
statusBarIconGroup.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
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 e819c4f..3082a66 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
@@ -157,16 +157,10 @@
}
override fun onIconTintChanged(newTint: Int) {
- if (viewModel.useDebugColoring) {
- return
- }
iconTint.value = newTint
}
override fun onDecorTintChanged(newTint: Int) {
- if (viewModel.useDebugColoring) {
- return
- }
decorTint.value = newTint
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt
index b731a41..cd5b92c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt
@@ -18,7 +18,6 @@
import android.graphics.Color
import com.android.systemui.statusbar.phone.StatusBarLocation
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
/**
* A view model for a wifi icon in a specific location. This allows us to control parameters that
@@ -27,18 +26,9 @@
* Must be subclassed for each distinct location.
*/
abstract class LocationBasedWifiViewModel(
- val commonImpl: WifiViewModelCommon,
- statusBarPipelineFlags: StatusBarPipelineFlags,
- debugTint: Int,
+ private val commonImpl: WifiViewModelCommon,
) : WifiViewModelCommon by commonImpl {
- val useDebugColoring: Boolean = statusBarPipelineFlags.useDebugColoring()
-
- val defaultColor: Int =
- if (useDebugColoring) {
- debugTint
- } else {
- Color.WHITE
- }
+ val defaultColor: Int = Color.WHITE
companion object {
/**
@@ -47,13 +37,12 @@
*/
fun viewModelForLocation(
commonImpl: WifiViewModelCommon,
- flags: StatusBarPipelineFlags,
location: StatusBarLocation,
): LocationBasedWifiViewModel =
when (location) {
- StatusBarLocation.HOME -> HomeWifiViewModel(commonImpl, flags)
- StatusBarLocation.KEYGUARD -> KeyguardWifiViewModel(commonImpl, flags)
- StatusBarLocation.QS -> QsWifiViewModel(commonImpl, flags)
+ StatusBarLocation.HOME -> HomeWifiViewModel(commonImpl)
+ StatusBarLocation.KEYGUARD -> KeyguardWifiViewModel(commonImpl)
+ StatusBarLocation.QS -> QsWifiViewModel(commonImpl)
}
}
}
@@ -64,23 +53,14 @@
*/
class HomeWifiViewModel(
commonImpl: WifiViewModelCommon,
- statusBarPipelineFlags: StatusBarPipelineFlags,
-) :
- WifiViewModelCommon,
- LocationBasedWifiViewModel(commonImpl, statusBarPipelineFlags, debugTint = Color.CYAN)
+) : WifiViewModelCommon, LocationBasedWifiViewModel(commonImpl)
/** A view model for the wifi icon shown on keyguard (lockscreen). */
class KeyguardWifiViewModel(
commonImpl: WifiViewModelCommon,
- statusBarPipelineFlags: StatusBarPipelineFlags,
-) :
- WifiViewModelCommon,
- LocationBasedWifiViewModel(commonImpl, statusBarPipelineFlags, debugTint = Color.MAGENTA)
+) : WifiViewModelCommon, LocationBasedWifiViewModel(commonImpl)
/** A view model for the wifi icon shown in quick settings (when the shade is pulled down). */
class QsWifiViewModel(
commonImpl: WifiViewModelCommon,
- statusBarPipelineFlags: StatusBarPipelineFlags,
-) :
- WifiViewModelCommon,
- LocationBasedWifiViewModel(commonImpl, statusBarPipelineFlags, debugTint = Color.GREEN)
+) : WifiViewModelCommon, LocationBasedWifiViewModel(commonImpl)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/tv/OWNERS
deleted file mode 100644
index a601e9b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/OWNERS
+++ /dev/null
@@ -1,8 +0,0 @@
-# Android TV Core Framework
-rgl@google.com
-valiiftime@google.com
-galinap@google.com
-patrikf@google.com
-robhor@google.com
-sergeynv@google.com
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
deleted file mode 100644
index d35d340..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2012 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.tv;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.CoreStartable;
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyboardShortcuts;
-
-import dagger.Lazy;
-
-import javax.inject.Inject;
-
-/**
- * Status bar implementation for "large screen" products that mostly present no on-screen nav.
- * Serves as a collection of UI components, rather than showing its own UI.
- */
-@SysUISingleton
-public class TvStatusBar implements CoreStartable, CommandQueue.Callbacks {
-
- private static final String ACTION_SHOW_PIP_MENU =
- "com.android.wm.shell.pip.tv.notification.action.SHOW_PIP_MENU";
- private static final String SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF";
-
- private final Context mContext;
- private final CommandQueue mCommandQueue;
- private final Lazy<AssistManager> mAssistManagerLazy;
-
- @Inject
- public TvStatusBar(Context context, CommandQueue commandQueue,
- Lazy<AssistManager> assistManagerLazy) {
- mContext = context;
- mCommandQueue = commandQueue;
- mAssistManagerLazy = assistManagerLazy;
- }
-
- @Override
- public void start() {
- final IStatusBarService barService = IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- mCommandQueue.addCallback(this);
- try {
- barService.registerStatusBar(mCommandQueue);
- } catch (RemoteException ex) {
- // If the system process isn't there we're doomed anyway.
- }
- }
-
- @Override
- public void startAssist(Bundle args) {
- mAssistManagerLazy.get().startAssist(args);
- }
-
- @Override
- public void showPictureInPictureMenu() {
- mContext.sendBroadcast(
- new Intent(ACTION_SHOW_PIP_MENU).setPackage(mContext.getPackageName()),
- SYSTEMUI_PERMISSION);
- }
-
- @Override
- public void toggleKeyboardShortcutsMenu(int deviceId) {
- KeyboardShortcuts.show(mContext, deviceId);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt b/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
deleted file mode 100644
index b938c90..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:JvmName("VpnStatusObserver")
-
-package com.android.systemui.statusbar.tv
-
-import android.app.Notification
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.content.Context
-import com.android.internal.messages.nano.SystemMessageProto
-import com.android.internal.net.VpnConfig
-import com.android.systemui.CoreStartable
-import com.android.systemui.R
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.policy.SecurityController
-import javax.inject.Inject
-
-/**
- * Observes if a vpn connection is active and displays a notification to the user
- */
-@SysUISingleton
-class VpnStatusObserver @Inject constructor(
- private val context: Context,
- private val securityController: SecurityController
-) : CoreStartable,
- SecurityController.SecurityControllerCallback {
-
- private var vpnConnected = false
- private val notificationManager = NotificationManager.from(context)
- private val notificationChannel = createNotificationChannel()
- private val vpnConnectedNotificationBuilder = createVpnConnectedNotificationBuilder()
- private val vpnDisconnectedNotification = createVpnDisconnectedNotification()
-
- private val vpnIconId: Int
- get() = if (securityController.isVpnBranded) {
- R.drawable.stat_sys_branded_vpn
- } else {
- R.drawable.stat_sys_vpn_ic
- }
-
- private val vpnName: String?
- get() = securityController.primaryVpnName ?: securityController.workProfileVpnName
-
- override fun start() {
- // register callback to vpn state changes
- securityController.addCallback(this)
- }
-
- override fun onStateChanged() {
- securityController.isVpnEnabled.let { newVpnConnected ->
- if (vpnConnected != newVpnConnected) {
- if (newVpnConnected) {
- notifyVpnConnected()
- } else {
- notifyVpnDisconnected()
- }
- vpnConnected = newVpnConnected
- }
- }
- }
-
- private fun notifyVpnConnected() = notificationManager.notify(
- NOTIFICATION_TAG,
- SystemMessageProto.SystemMessage.NOTE_VPN_STATUS,
- createVpnConnectedNotification()
- )
-
- private fun notifyVpnDisconnected() = notificationManager.run {
- // remove existing connected notification
- cancel(NOTIFICATION_TAG, SystemMessageProto.SystemMessage.NOTE_VPN_STATUS)
- // show the disconnected notification only for a short while
- notify(NOTIFICATION_TAG, SystemMessageProto.SystemMessage.NOTE_VPN_DISCONNECTED,
- vpnDisconnectedNotification)
- }
-
- private fun createNotificationChannel() =
- NotificationChannel(
- NOTIFICATION_CHANNEL_TV_VPN,
- NOTIFICATION_CHANNEL_TV_VPN,
- NotificationManager.IMPORTANCE_HIGH
- ).also {
- notificationManager.createNotificationChannel(it)
- }
-
- private fun createVpnConnectedNotification() =
- vpnConnectedNotificationBuilder
- .apply {
- vpnName?.let {
- setContentText(
- context.getString(
- R.string.notification_disclosure_vpn_text, it
- )
- )
- }
- }
- .build()
-
- private fun createVpnConnectedNotificationBuilder() =
- Notification.Builder(context, NOTIFICATION_CHANNEL_TV_VPN)
- .setSmallIcon(vpnIconId)
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .setCategory(Notification.CATEGORY_SYSTEM)
- .extend(Notification.TvExtender())
- .setOngoing(true)
- .setContentTitle(context.getString(R.string.notification_vpn_connected))
- .setContentIntent(VpnConfig.getIntentForStatusPanel(context))
-
- private fun createVpnDisconnectedNotification() =
- Notification.Builder(context, NOTIFICATION_CHANNEL_TV_VPN)
- .setSmallIcon(vpnIconId)
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .setCategory(Notification.CATEGORY_SYSTEM)
- .extend(Notification.TvExtender())
- .setTimeoutAfter(VPN_DISCONNECTED_NOTIFICATION_TIMEOUT_MS)
- .setContentTitle(context.getString(R.string.notification_vpn_disconnected))
- .build()
-
- companion object {
- const val NOTIFICATION_CHANNEL_TV_VPN = "VPN Status"
- val NOTIFICATION_TAG: String = VpnStatusObserver::class.java.simpleName
-
- private const val TAG = "TvVpnNotification"
- private const val VPN_DISCONNECTED_NOTIFICATION_TIMEOUT_MS = 5_000L
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java
deleted file mode 100644
index fd7c30f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2020 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.tv.notifications;
-
-import android.app.BroadcastOptions;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.service.notification.StatusBarNotification;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.systemui.R;
-
-/**
- * Adapter for the VerticalGridView of the TvNotificationsPanelView.
- */
-public class TvNotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
- private static final String TAG = "TvNotificationAdapter";
- private SparseArray<StatusBarNotification> mNotifications;
-
- public TvNotificationAdapter() {
- setHasStableIds(true);
- }
-
- @NonNull
- @Override
- public TvNotificationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.tv_notification_item,
- parent, false);
- return new TvNotificationViewHolder(view);
- }
-
- @Override
- public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
- if (mNotifications == null) {
- Log.e(TAG, "Could not bind view holder because the notification is missing");
- return;
- }
-
- TvNotificationViewHolder holder = (TvNotificationViewHolder) viewHolder;
- Notification notification = mNotifications.valueAt(position).getNotification();
- holder.mTitle.setText(notification.extras.getString(Notification.EXTRA_TITLE));
- holder.mDetails.setText(notification.extras.getString(Notification.EXTRA_TEXT));
- holder.mPendingIntent = notification.contentIntent;
- }
-
- @Override
- public int getItemCount() {
- return mNotifications == null ? 0 : mNotifications.size();
- }
-
- @Override
- public long getItemId(int position) {
- // the item id is the notification id
- return mNotifications.keyAt(position);
- }
-
- /**
- * Updates the notifications and calls notifyDataSetChanged().
- */
- public void setNotifications(SparseArray<StatusBarNotification> notifications) {
- this.mNotifications = notifications;
- notifyDataSetChanged();
- }
-
- private static class TvNotificationViewHolder extends RecyclerView.ViewHolder implements
- View.OnClickListener {
- final TextView mTitle;
- final TextView mDetails;
- PendingIntent mPendingIntent;
-
- protected TvNotificationViewHolder(View itemView) {
- super(itemView);
- mTitle = itemView.findViewById(R.id.tv_notification_title);
- mDetails = itemView.findViewById(R.id.tv_notification_details);
- itemView.setOnClickListener(this);
- }
-
- @Override
- public void onClick(View v) {
- try {
- if (mPendingIntent != null) {
- BroadcastOptions options = BroadcastOptions.makeBasic();
- options.setInteractive(true);
- options.setPendingIntentBackgroundActivityStartMode(
- BroadcastOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- mPendingIntent.send(options.toBundle());
- }
- } catch (PendingIntent.CanceledException e) {
- Log.d(TAG, "Pending intent canceled for : " + mPendingIntent);
- }
- }
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
deleted file mode 100644
index b92725b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2020 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.tv.notifications;
-
-import android.annotation.Nullable;
-import android.app.Notification;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.systemui.CoreStartable;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.NotificationListener;
-
-import javax.inject.Inject;
-
-/**
- * Keeps track of the notifications on TV.
- */
-@SysUISingleton
-public class TvNotificationHandler implements CoreStartable,
- NotificationListener.NotificationHandler {
- private static final String TAG = "TvNotificationHandler";
- private final NotificationListener mNotificationListener;
- private final SparseArray<StatusBarNotification> mNotifications = new SparseArray<>();
- @Nullable
- private Listener mUpdateListener;
-
- @Inject
- public TvNotificationHandler(NotificationListener notificationListener) {
- mNotificationListener = notificationListener;
- }
-
- public SparseArray<StatusBarNotification> getCurrentNotifications() {
- return mNotifications;
- }
-
- public void setTvNotificationListener(Listener listener) {
- mUpdateListener = listener;
- }
-
- @Override
- public void start() {
- mNotificationListener.addNotificationHandler(this);
- mNotificationListener.registerAsSystemService();
- }
-
- @Override
- public void onNotificationPosted(StatusBarNotification sbn,
- NotificationListenerService.RankingMap rankingMap) {
- if (!new Notification.TvExtender(sbn.getNotification()).isAvailableOnTv()) {
- Log.v(TAG, "Notification not added because it isn't relevant for tv");
- return;
- }
-
- mNotifications.put(sbn.getId(), sbn);
- if (mUpdateListener != null) {
- mUpdateListener.notificationsUpdated(mNotifications);
- }
- Log.d(TAG, "Notification added");
- }
-
- @Override
- public void onNotificationRemoved(StatusBarNotification sbn,
- NotificationListenerService.RankingMap rankingMap) {
-
- if (mNotifications.contains(sbn.getId())) {
- mNotifications.remove(sbn.getId());
- Log.d(TAG, "Notification removed");
-
- if (mUpdateListener != null) {
- mUpdateListener.notificationsUpdated(mNotifications);
- }
- }
- }
-
- @Override
- public void onNotificationRemoved(StatusBarNotification sbn,
- NotificationListenerService.RankingMap rankingMap, int reason) {
- onNotificationRemoved(sbn, rankingMap);
- }
-
- @Override
- public void onNotificationRankingUpdate(NotificationListenerService.RankingMap rankingMap) {
- // noop
- }
-
- @Override
- public void onNotificationsInitialized() {
- // noop
- }
-
- /**
- * Get notified when the notifications are updated.
- */
- interface Listener {
- void notificationsUpdated(SparseArray<StatusBarNotification> sbns);
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
deleted file mode 100644
index dbbd0b8..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2020 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.tv.notifications;
-
-import android.Manifest;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.UserHandle;
-import android.util.Log;
-
-import com.android.systemui.CoreStartable;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.CommandQueue;
-
-import javax.inject.Inject;
-
-/**
- * Offers control methods for the notification panel handler on TV devices.
- */
-@SysUISingleton
-public class TvNotificationPanel implements CoreStartable, CommandQueue.Callbacks {
- private static final String TAG = "TvNotificationPanel";
- private final Context mContext;
- private final CommandQueue mCommandQueue;
- private final String mNotificationHandlerPackage;
-
- @Inject
- public TvNotificationPanel(Context context, CommandQueue commandQueue) {
- mContext = context;
- mCommandQueue = commandQueue;
- mNotificationHandlerPackage = mContext.getResources().getString(
- com.android.internal.R.string.config_notificationHandlerPackage);
- }
-
- @Override
- public void start() {
- mCommandQueue.addCallback(this);
- }
-
- @Override
- public void togglePanel() {
- if (!mNotificationHandlerPackage.isEmpty()) {
- startNotificationHandlerActivity(
- new Intent(NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL));
- } else {
- openInternalNotificationPanel(
- NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL);
- }
- }
-
- @Override
- public void animateExpandNotificationsPanel() {
- if (!mNotificationHandlerPackage.isEmpty()) {
- startNotificationHandlerActivity(
- new Intent(NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL));
- } else {
- openInternalNotificationPanel(
- NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL);
- }
- }
-
- @Override
- public void animateCollapsePanels(int flags, boolean force) {
- if (!mNotificationHandlerPackage.isEmpty()
- && (flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) {
- Intent closeNotificationIntent = new Intent(
- NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL);
- closeNotificationIntent.setPackage(mNotificationHandlerPackage);
- mContext.sendBroadcastAsUser(closeNotificationIntent, UserHandle.CURRENT);
- } else {
- openInternalNotificationPanel(
- NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL);
- }
- }
-
- private void openInternalNotificationPanel(String action) {
- Intent intent = new Intent(mContext, TvNotificationPanelActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
- intent.setAction(action);
- mContext.startActivityAsUser(intent, UserHandle.SYSTEM);
- }
-
- /**
- * Starts the activity intent if all of the following are true
- * <ul>
- * <li> the notification handler package is a system component </li>
- * <li> the provided intent is handled by the notification handler package </li>
- * <li> the notification handler requests the
- * {@link android.Manifest.permission#STATUS_BAR_SERVICE} permission for the given intent</li>
- * </ul>
- *
- * @param intent The intent for starting the desired activity
- */
- private void startNotificationHandlerActivity(Intent intent) {
- intent.setPackage(mNotificationHandlerPackage);
- PackageManager pm = mContext.getPackageManager();
- ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_SYSTEM_ONLY);
- if (ri != null && ri.activityInfo != null) {
- if (ri.activityInfo.permission != null && ri.activityInfo.permission.equals(
- Manifest.permission.STATUS_BAR_SERVICE)) {
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
- } else {
- Log.e(TAG,
- "Not launching notification handler activity: Notification handler does "
- + "not require the STATUS_BAR_SERVICE permission for intent "
- + intent.getAction());
- }
- } else {
- Log.e(TAG,
- "Not launching notification handler activity: Could not resolve activityInfo "
- + "for intent "
- + intent.getAction());
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java
deleted file mode 100644
index b325b10..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2020 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.tv.notifications;
-
-import android.annotation.NonNull;
-import android.app.Activity;
-import android.app.NotificationManager;
-import android.content.Intent;
-import android.graphics.drawable.ColorDrawable;
-import android.os.Bundle;
-import android.service.notification.StatusBarNotification;
-import android.util.SparseArray;
-import android.view.Gravity;
-import android.view.View;
-
-import androidx.leanback.widget.VerticalGridView;
-
-import com.android.systemui.R;
-
-import java.util.function.Consumer;
-
-import javax.inject.Inject;
-
-/**
- * This Activity shows a notification panel for tv. It is used if no other app (e.g. a launcher) can
- * be found to show the notifications.
- */
-public class TvNotificationPanelActivity extends Activity implements
- TvNotificationHandler.Listener {
- private final TvNotificationHandler mTvNotificationHandler;
- private TvNotificationAdapter mTvNotificationAdapter;
- private VerticalGridView mNotificationListView;
- private View mNotificationPlaceholder;
- private boolean mPanelAlreadyOpen = false;
- private final Consumer<Boolean> mBlurConsumer = this::enableBlur;
-
- @Inject
- public TvNotificationPanelActivity(TvNotificationHandler tvNotificationHandler) {
- super();
- mTvNotificationHandler = tvNotificationHandler;
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if (maybeClosePanel(getIntent())) {
- return;
- }
- mPanelAlreadyOpen = true;
-
- setContentView(R.layout.tv_notification_panel);
-
- mNotificationPlaceholder = findViewById(R.id.no_tv_notifications);
- mTvNotificationAdapter = new TvNotificationAdapter();
-
- mNotificationListView = findViewById(R.id.notifications_list);
- mNotificationListView.setAdapter(mTvNotificationAdapter);
- mNotificationListView.setColumnWidth(R.dimen.tv_notification_panel_width);
-
- mTvNotificationHandler.setTvNotificationListener(this);
- notificationsUpdated(mTvNotificationHandler.getCurrentNotifications());
- }
-
- @Override
- public void notificationsUpdated(@NonNull SparseArray<StatusBarNotification> notificationList) {
- mTvNotificationAdapter.setNotifications(notificationList);
-
- boolean noNotifications = notificationList.size() == 0;
- mNotificationListView.setVisibility(noNotifications ? View.GONE : View.VISIBLE);
- mNotificationPlaceholder.setVisibility(noNotifications ? View.VISIBLE : View.GONE);
- }
-
- @Override
- public void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- maybeClosePanel(intent);
- }
-
- /**
- * Handles intents from onCreate and onNewIntent.
- *
- * @return true if the panel is being closed, false if it is being opened
- */
- private boolean maybeClosePanel(Intent intent) {
- if (NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL.equals(intent.getAction())
- || (mPanelAlreadyOpen
- && NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL.equals(
- intent.getAction()))) {
- finish();
- return true;
- }
- return false;
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- getWindow().setGravity(Gravity.END);
- getWindowManager().addCrossWindowBlurEnabledListener(mBlurConsumer);
- }
-
- private void enableBlur(boolean enabled) {
- if (enabled) {
- int blurRadius = getResources().getDimensionPixelSize(
- R.dimen.tv_notification_blur_radius);
- getWindow().setBackgroundDrawable(
- new ColorDrawable(getColor(R.color.tv_notification_blur_background_color)));
- getWindow().setBackgroundBlurRadius(blurRadius);
- } else {
- getWindow().setBackgroundDrawable(
- new ColorDrawable(getColor(R.color.tv_notification_default_background_color)));
- getWindow().setBackgroundBlurRadius(0);
- }
- }
-
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- getWindowManager().removeCrossWindowBlurEnabledListener(mBlurConsumer);
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- mTvNotificationHandler.setTvNotificationListener(null);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index 7ed56e7..7aeba66 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -88,7 +88,7 @@
private val chipbarAnimator: ChipbarAnimator,
private val falsingManager: FalsingManager,
private val falsingCollector: FalsingCollector,
- private val swipeChipbarAwayGestureHandler: SwipeChipbarAwayGestureHandler?,
+ private val swipeChipbarAwayGestureHandler: SwipeChipbarAwayGestureHandler,
private val viewUtil: ViewUtil,
private val vibratorHelper: VibratorHelper,
wakeLockBuilder: WakeLock.Builder,
@@ -289,10 +289,6 @@
}
private fun updateGestureListening() {
- if (swipeChipbarAwayGestureHandler == null) {
- return
- }
-
val currentDisplayInfo = getCurrentDisplayInfo()
if (currentDisplayInfo != null && currentDisplayInfo.info.allowSwipeToDismiss) {
swipeChipbarAwayGestureHandler.setViewFetcher { currentDisplayInfo.view }
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt
index 9dbc4b3..80de523 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt
@@ -19,10 +19,12 @@
import android.content.Context
import android.view.MotionEvent
import android.view.View
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.settings.DisplayTracker
import com.android.systemui.statusbar.gesture.SwipeUpGestureHandler
import com.android.systemui.statusbar.gesture.SwipeUpGestureLogger
import com.android.systemui.util.boundsOnScreen
+import javax.inject.Inject
/**
* A class to detect when a user has swiped the chipbar away.
@@ -30,7 +32,10 @@
* Effectively [SysUISingleton]. But, this shouldn't be created if the gesture isn't enabled. See
* [TemporaryDisplayModule.provideSwipeChipbarAwayGestureHandler].
*/
-class SwipeChipbarAwayGestureHandler(
+@SysUISingleton
+class SwipeChipbarAwayGestureHandler
+@Inject
+constructor(
context: Context,
displayTracker: DisplayTracker,
logger: SwipeUpGestureLogger,
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt
index cae1308..2d05573 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt
@@ -16,14 +16,9 @@
package com.android.systemui.temporarydisplay.dagger
-import android.content.Context
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
-import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.settings.DisplayTracker
-import com.android.systemui.statusbar.gesture.SwipeUpGestureLogger
-import com.android.systemui.temporarydisplay.chipbar.SwipeChipbarAwayGestureHandler
import dagger.Module
import dagger.Provides
@@ -36,20 +31,5 @@
fun provideChipbarLogBuffer(factory: LogBufferFactory): LogBuffer {
return factory.create("ChipbarLog", 40)
}
-
- @Provides
- @SysUISingleton
- fun provideSwipeChipbarAwayGestureHandler(
- mediaTttFlags: MediaTttFlags,
- context: Context,
- displayTracker: DisplayTracker,
- logger: SwipeUpGestureLogger,
- ): SwipeChipbarAwayGestureHandler? {
- return if (mediaTttFlags.isMediaTttDismissGestureEnabled()) {
- SwipeChipbarAwayGestureHandler(context, displayTracker, logger)
- } else {
- null
- }
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 5d09e06..a501e87 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -20,6 +20,11 @@
import com.android.systemui.Dependency;
+/**
+ * @deprecated Don't use this class to listen to Secure Settings. Use {@code SecureSettings} instead
+ * or {@code SettingsObserver} to be able to specify the handler.
+ */
+@Deprecated
public abstract class TunerService {
public static final String ACTION_CLEAR = "com.android.systemui.action.CLEAR_TUNER";
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 8cfe2ea..ccc0a79 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -56,7 +56,11 @@
/**
+ * @deprecated Don't use this class to listen to Secure Settings. Use {@code SecureSettings} instead
+ * or {@code SettingsObserver} to be able to specify the handler.
+ * This class will interact with SecureSettings using the main looper.
*/
+@Deprecated
@SysUISingleton
public class TunerServiceImpl extends TunerService {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TVSystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/tv/TVSystemUICoreStartableModule.kt
deleted file mode 100644
index 573fbf7..0000000
--- a/packages/SystemUI/src/com/android/systemui/tv/TVSystemUICoreStartableModule.kt
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.tv
-
-import com.android.systemui.CoreStartable
-import com.android.systemui.SliceBroadcastRelayHandler
-import com.android.systemui.accessibility.WindowMagnification
-import com.android.systemui.dagger.qualifiers.PerUser
-import com.android.systemui.globalactions.GlobalActionsComponent
-import com.android.systemui.keyboard.KeyboardUI
-import com.android.systemui.media.RingtonePlayer
-import com.android.systemui.media.systemsounds.HomeSoundEffectController
-import com.android.systemui.power.PowerUI
-import com.android.systemui.privacy.television.TvPrivacyChipsController
-import com.android.systemui.shortcut.ShortcutKeyDispatcher
-import com.android.systemui.statusbar.notification.InstantAppNotifier
-import com.android.systemui.statusbar.tv.TvStatusBar
-import com.android.systemui.statusbar.tv.VpnStatusObserver
-import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler
-import com.android.systemui.statusbar.tv.notifications.TvNotificationPanel
-import com.android.systemui.theme.ThemeOverlayController
-import com.android.systemui.toast.ToastUI
-import com.android.systemui.usb.StorageNotification
-import com.android.systemui.util.NotificationChannels
-import com.android.systemui.volume.VolumeUI
-import com.android.systemui.wmshell.WMShell
-import dagger.Binds
-import dagger.Module
-import dagger.multibindings.ClassKey
-import dagger.multibindings.IntoMap
-
-/**
- * Collection of {@link CoreStartable}s that should be run on TV.
- */
-@Module
-abstract class TVSystemUICoreStartableModule {
- /** Inject into GlobalActionsComponent. */
- @Binds
- @IntoMap
- @ClassKey(GlobalActionsComponent::class)
- abstract fun bindGlobalActionsComponent(sysui: GlobalActionsComponent): CoreStartable
-
- /** Inject into HomeSoundEffectController. */
- @Binds
- @IntoMap
- @ClassKey(HomeSoundEffectController::class)
- abstract fun bindHomeSoundEffectController(sysui: HomeSoundEffectController): CoreStartable
-
- /** Inject into InstantAppNotifier. */
- @Binds
- @IntoMap
- @ClassKey(InstantAppNotifier::class)
- abstract fun bindInstantAppNotifier(sysui: InstantAppNotifier): CoreStartable
-
- /** Inject into KeyboardUI. */
- @Binds
- @IntoMap
- @ClassKey(KeyboardUI::class)
- abstract fun bindKeyboardUI(sysui: KeyboardUI): CoreStartable
-
- /** Inject into NotificationChannels. */
- @Binds
- @IntoMap
- @ClassKey(NotificationChannels::class)
- @PerUser
- abstract fun bindNotificationChannels(sysui: NotificationChannels): CoreStartable
-
- /** Inject into PowerUI. */
- @Binds
- @IntoMap
- @ClassKey(PowerUI::class)
- abstract fun bindPowerUI(sysui: PowerUI): CoreStartable
-
- /** Inject into RingtonePlayer. */
- @Binds
- @IntoMap
- @ClassKey(RingtonePlayer::class)
- abstract fun bind(sysui: RingtonePlayer): CoreStartable
-
- /** Inject into ShortcutKeyDispatcher. */
- @Binds
- @IntoMap
- @ClassKey(ShortcutKeyDispatcher::class)
- abstract fun bindShortcutKeyDispatcher(sysui: ShortcutKeyDispatcher): CoreStartable
-
- /** Inject into SliceBroadcastRelayHandler. */
- @Binds
- @IntoMap
- @ClassKey(SliceBroadcastRelayHandler::class)
- abstract fun bindSliceBroadcastRelayHandler(sysui: SliceBroadcastRelayHandler): CoreStartable
-
- /** Inject into StorageNotification. */
- @Binds
- @IntoMap
- @ClassKey(StorageNotification::class)
- abstract fun bindStorageNotification(sysui: StorageNotification): CoreStartable
-
- /** Inject into ThemeOverlayController. */
- @Binds
- @IntoMap
- @ClassKey(ThemeOverlayController::class)
- abstract fun bindThemeOverlayController(sysui: ThemeOverlayController): CoreStartable
-
- /** Inject into ToastUI. */
- @Binds
- @IntoMap
- @ClassKey(ToastUI::class)
- abstract fun bindToastUI(service: ToastUI): CoreStartable
-
- /** Inject into TvNotificationHandler. */
- @Binds
- @IntoMap
- @ClassKey(TvNotificationHandler::class)
- abstract fun bindTvNotificationHandler(sysui: TvNotificationHandler): CoreStartable
-
- /** Inject into TvNotificationPanel. */
- @Binds
- @IntoMap
- @ClassKey(TvNotificationPanel::class)
- abstract fun bindTvNotificationPanel(sysui: TvNotificationPanel): CoreStartable
-
- /** Inject into TvPrivacyChipsController. */
- @Binds
- @IntoMap
- @ClassKey(TvPrivacyChipsController::class)
- abstract fun bindTvPrivacyChipsController(sysui: TvPrivacyChipsController): CoreStartable
-
- /** Inject into TvStatusBar. */
- @Binds
- @IntoMap
- @ClassKey(TvStatusBar::class)
- abstract fun bindTvStatusBar(sysui: TvStatusBar): CoreStartable
-
- /** Inject into VolumeUI. */
- @Binds
- @IntoMap
- @ClassKey(VolumeUI::class)
- abstract fun bindVolumeUI(sysui: VolumeUI): CoreStartable
-
- /** Inject into VpnStatusObserver. */
- @Binds
- @IntoMap
- @ClassKey(VpnStatusObserver::class)
- abstract fun bindVpnStatusObserver(sysui: VpnStatusObserver): CoreStartable
-
- /** Inject into WindowMagnification. */
- @Binds
- @IntoMap
- @ClassKey(WindowMagnification::class)
- abstract fun bindWindowMagnification(sysui: WindowMagnification): CoreStartable
-
- /** Inject into WMShell. */
- @Binds
- @IntoMap
- @ClassKey(WMShell::class)
- abstract fun bindWMShell(sysui: WMShell): CoreStartable
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvBottomSheetActivity.java b/packages/SystemUI/src/com/android/systemui/tv/TvBottomSheetActivity.java
deleted file mode 100644
index 90f2434..0000000
--- a/packages/SystemUI/src/com/android/systemui/tv/TvBottomSheetActivity.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.tv;
-
-import android.app.Activity;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.View;
-import android.view.WindowManager;
-
-import com.android.systemui.R;
-
-import java.util.Collections;
-import java.util.function.Consumer;
-
-/**
- * Generic bottom sheet with up to two icons in the beginning and two buttons.
- */
-public abstract class TvBottomSheetActivity extends Activity {
-
- private static final String TAG = TvBottomSheetActivity.class.getSimpleName();
- private Drawable mBackgroundWithBlur;
- private Drawable mBackgroundWithoutBlur;
-
- private final Consumer<Boolean> mBlurConsumer = this::onBlurChanged;
-
- private void onBlurChanged(boolean enabled) {
- Log.v(TAG, "blur enabled: " + enabled);
- getWindow().setBackgroundDrawable(enabled ? mBackgroundWithBlur : mBackgroundWithoutBlur);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.tv_bottom_sheet);
-
- overridePendingTransition(R.anim.tv_bottom_sheet_enter, 0);
-
- mBackgroundWithBlur = getResources()
- .getDrawable(R.drawable.bottom_sheet_background_with_blur);
- mBackgroundWithoutBlur = getResources().getDrawable(R.drawable.bottom_sheet_background);
-
- DisplayMetrics metrics = getResources().getDisplayMetrics();
- int screenWidth = metrics.widthPixels;
- int screenHeight = metrics.heightPixels;
- int marginPx = getResources().getDimensionPixelSize(R.dimen.bottom_sheet_margin);
-
- WindowManager.LayoutParams windowParams = getWindow().getAttributes();
- windowParams.width = screenWidth - marginPx * 2;
- windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
- windowParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
- windowParams.horizontalMargin = 0f;
- windowParams.verticalMargin = (float) marginPx / screenHeight;
- windowParams.format = PixelFormat.TRANSPARENT;
- windowParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
- windowParams.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
- windowParams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
- getWindow().setAttributes(windowParams);
- getWindow().setElevation(getWindow().getElevation() + 5);
- getWindow().setBackgroundBlurRadius(getResources().getDimensionPixelSize(
- R.dimen.bottom_sheet_background_blur_radius));
-
- final View rootView = findViewById(R.id.bottom_sheet);
- rootView.addOnLayoutChangeListener((view, l, t, r, b, oldL, oldT, oldR, oldB) -> {
- rootView.setUnrestrictedPreferKeepClearRects(
- Collections.singletonList(new Rect(0, 0, r - l, b - t)));
- });
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- getWindowManager().addCrossWindowBlurEnabledListener(mBlurConsumer);
- }
-
- @Override
- public void onDetachedFromWindow() {
- getWindowManager().removeCrossWindowBlurEnabledListener(mBlurConsumer);
- super.onDetachedFromWindow();
- }
-
- @Override
- public void finish() {
- super.finish();
- overridePendingTransition(0, R.anim.tv_bottom_sheet_exit);
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java
deleted file mode 100644
index 117cba7..0000000
--- a/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.tv;
-
-import com.android.systemui.dagger.GlobalModule;
-import com.android.systemui.dagger.GlobalRootComponent;
-
-import javax.inject.Singleton;
-
-import dagger.Component;
-
-/**
- * Root component for Dagger injection.
- */
-@Singleton
-@Component(modules = {GlobalModule.class})
-public interface TvGlobalRootComponent extends GlobalRootComponent {
- /**
- * Component Builder interface. This allows to bind Context instance in the component
- */
- @Component.Builder
- interface Builder extends GlobalRootComponent.Builder {
- TvGlobalRootComponent build();
- }
-
- @Override
- TvWMComponent.Builder getWMComponentBuilder();
-
- @Override
- TvSysUIComponent.Builder getSysUIComponent();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
deleted file mode 100644
index 82589d3..0000000
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.tv;
-
-import com.android.systemui.dagger.DefaultComponentBinder;
-import com.android.systemui.dagger.DependencyProvider;
-import com.android.systemui.dagger.SysUIComponent;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.SystemUIModule;
-import com.android.systemui.globalactions.ShutdownUiModule;
-import com.android.systemui.keyguard.dagger.KeyguardModule;
-import com.android.systemui.recents.RecentsModule;
-import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule;
-import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
-import com.android.systemui.statusbar.notification.row.NotificationRowModule;
-import com.android.systemui.wallpapers.dagger.NoopWallpaperModule;
-
-import dagger.Subcomponent;
-
-/**
- * Dagger Subcomponent for Core SysUI.
- */
-@SysUISingleton
-@Subcomponent(modules = {
- CentralSurfacesDependenciesModule.class,
- DefaultComponentBinder.class,
- DependencyProvider.class,
- KeyguardModule.class,
- NoopWallpaperModule.class,
- NotificationRowModule.class,
- NotificationsModule.class,
- RecentsModule.class,
- ShutdownUiModule.class,
- SystemUIModule.class,
- TvSystemUIBinder.class,
- TVSystemUICoreStartableModule.class,
- TvSystemUIModule.class})
-public interface TvSysUIComponent extends SysUIComponent {
-
- /**
- * Builder for a SysUIComponent.
- */
- @Subcomponent.Builder
- interface Builder extends SysUIComponent.Builder {
- TvSysUIComponent build();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIInitializer.java
deleted file mode 100644
index fabbb2c..0000000
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIInitializer.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.tv;
-
-import android.content.Context;
-
-import com.android.systemui.SystemUIInitializer;
-import com.android.systemui.dagger.GlobalRootComponent;
-
-/**
- * TV variant {@link SystemUIInitializer}, that substitutes default {@link GlobalRootComponent} for
- * {@link TvGlobalRootComponent}
- */
-public class TvSystemUIInitializer extends SystemUIInitializer {
- public TvSystemUIInitializer(Context context) {
- super(context);
- }
-
- @Override
- protected GlobalRootComponent.Builder getGlobalRootComponentBuilder() {
- return DaggerTvGlobalRootComponent.builder();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
deleted file mode 100644
index 8cf71a0..0000000
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.tv;
-
-import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
-import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
-
-import android.content.Context;
-import android.hardware.SensorPrivacyManager;
-import android.os.Handler;
-
-import com.android.internal.logging.UiEventLogger;
-import com.android.keyguard.KeyguardViewController;
-import com.android.systemui.dagger.ReferenceSystemUIModule;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dock.DockManager;
-import com.android.systemui.dock.DockManagerImpl;
-import com.android.systemui.doze.DozeHost;
-import com.android.systemui.navigationbar.gestural.GestureModule;
-import com.android.systemui.plugins.qs.QSFactory;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.dagger.PowerModule;
-import com.android.systemui.privacy.MediaProjectionPrivacyItemMonitor;
-import com.android.systemui.privacy.PrivacyItemMonitor;
-import com.android.systemui.qs.dagger.QSModule;
-import com.android.systemui.qs.tileimpl.QSFactoryImpl;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsImplementation;
-import com.android.systemui.screenshot.ReferenceScreenshotModule;
-import com.android.systemui.settings.dagger.MultiUserUtilsModule;
-import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shade.ShadeControllerEmptyImpl;
-import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyboardShortcutsModule;
-import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.events.StatusBarEventsModule;
-import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
-import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
-import com.android.systemui.statusbar.phone.DozeServiceHost;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-import com.android.systemui.statusbar.policy.AospPolicyModule;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
-import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
-import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerImpl;
-import com.android.systemui.statusbar.policy.SensorPrivacyController;
-import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
-import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler;
-import com.android.systemui.volume.dagger.VolumeModule;
-
-import dagger.Binds;
-import dagger.Module;
-import dagger.Provides;
-import dagger.multibindings.IntoSet;
-
-import javax.inject.Named;
-
-/**
- * A TV specific version of {@link ReferenceSystemUIModule}.
- *
- * Code here should be specific to the TV variant of SystemUI and will not be included in other
- * variants of SystemUI.
- */
-@Module(
- includes = {
- AospPolicyModule.class,
- GestureModule.class,
- MultiUserUtilsModule.class,
- PowerModule.class,
- QSModule.class,
- ReferenceScreenshotModule.class,
- StatusBarEventsModule.class,
- VolumeModule.class,
- KeyboardShortcutsModule.class
- }
-)
-public abstract class TvSystemUIModule {
-
- @SysUISingleton
- @Provides
- @Named(LEAK_REPORT_EMAIL_NAME)
- static String provideLeakReportEmail() {
- return "";
- }
-
- @Binds
- abstract NotificationLockscreenUserManager bindNotificationLockscreenUserManager(
- NotificationLockscreenUserManagerImpl notificationLockscreenUserManager);
-
- @Provides
- @SysUISingleton
- static SensorPrivacyController provideSensorPrivacyController(
- SensorPrivacyManager sensorPrivacyManager) {
- SensorPrivacyController spC = new SensorPrivacyControllerImpl(sensorPrivacyManager);
- spC.init();
- return spC;
- }
-
- @Provides
- @SysUISingleton
- static IndividualSensorPrivacyController provideIndividualSensorPrivacyController(
- SensorPrivacyManager sensorPrivacyManager) {
- IndividualSensorPrivacyController spC = new IndividualSensorPrivacyControllerImpl(
- sensorPrivacyManager);
- spC.init();
- return spC;
- }
-
- @Binds
- @SysUISingleton
- abstract QSFactory bindQSFactory(QSFactoryImpl qsFactoryImpl);
-
- @Binds
- abstract DockManager bindDockManager(DockManagerImpl dockManager);
-
- @Binds
- abstract ShadeController provideShadeController(ShadeControllerEmptyImpl shadeController);
-
- @SysUISingleton
- @Provides
- @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
- static boolean provideAllowNotificationLongPress() {
- return true;
- }
-
- @SysUISingleton
- @Provides
- static HeadsUpManagerPhone provideHeadsUpManagerPhone(
- Context context,
- HeadsUpManagerLogger headsUpManagerLogger,
- StatusBarStateController statusBarStateController,
- KeyguardBypassController bypassController,
- GroupMembershipManager groupManager,
- VisualStabilityProvider visualStabilityProvider,
- ConfigurationController configurationController,
- @Main Handler handler,
- AccessibilityManagerWrapper accessibilityManagerWrapper,
- UiEventLogger uiEventLogger,
- ShadeExpansionStateManager shadeExpansionStateManager) {
- return new HeadsUpManagerPhone(
- context,
- headsUpManagerLogger,
- statusBarStateController,
- bypassController,
- groupManager,
- visualStabilityProvider,
- configurationController,
- handler,
- accessibilityManagerWrapper,
- uiEventLogger,
- shadeExpansionStateManager
- );
- }
-
- @Binds
- abstract HeadsUpManager bindHeadsUpManagerPhone(HeadsUpManagerPhone headsUpManagerPhone);
-
- @Provides
- @SysUISingleton
- static Recents provideRecents(Context context, RecentsImplementation recentsImplementation,
- CommandQueue commandQueue) {
- return new Recents(context, recentsImplementation, commandQueue);
- }
-
- @SysUISingleton
- @Provides
- static DeviceProvisionedController providesDeviceProvisionedController(
- DeviceProvisionedControllerImpl deviceProvisionedController) {
- deviceProvisionedController.init();
- return deviceProvisionedController;
- }
-
- @Binds
- abstract KeyguardViewController bindKeyguardViewController(
- StatusBarKeyguardViewManager statusBarKeyguardViewManager);
-
- @Binds
- abstract NotificationShadeWindowController bindNotificationShadeController(
- NotificationShadeWindowControllerImpl notificationShadeWindowController);
-
- @Binds
- abstract DozeHost provideDozeHost(DozeServiceHost dozeServiceHost);
-
- @Provides
- @SysUISingleton
- static TvNotificationHandler provideTvNotificationHandler(
- NotificationListener notificationListener) {
- return new TvNotificationHandler(notificationListener);
- }
-
- /**
- * Binds {@link MediaProjectionPrivacyItemMonitor} into the set of {@link PrivacyItemMonitor}.
- */
- @Binds
- @IntoSet
- abstract PrivacyItemMonitor bindMediaProjectionPrivacyItemMonitor(
- MediaProjectionPrivacyItemMonitor mediaProjectionPrivacyItemMonitor);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java
deleted file mode 100644
index 8370615..0000000
--- a/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.tv;
-
-import com.android.systemui.dagger.WMComponent;
-import com.android.wm.shell.dagger.WMSingleton;
-import com.android.wm.shell.dagger.TvWMShellModule;
-
-import dagger.Subcomponent;
-
-
-/**
- * Dagger Subcomponent for WindowManager.
- */
-@WMSingleton
-@Subcomponent(modules = {TvWMShellModule.class})
-public interface TvWMComponent extends WMComponent {
-
- /**
- * Builder for a SysUIComponent.
- */
- @Subcomponent.Builder
- interface Builder extends WMComponent.Builder {
- TvWMComponent build();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbConfirmActivity.java b/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbConfirmActivity.java
deleted file mode 100644
index b03bedc..0000000
--- a/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbConfirmActivity.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.usb.tv;
-
-import com.android.systemui.R;
-
-/**
- * Dialog shown to confirm the package to start when a USB device or accessory is attached and there
- * is only one package that claims to handle this USB device or accessory.
- */
-public class TvUsbConfirmActivity extends TvUsbDialogActivity {
- private static final String TAG = TvUsbConfirmActivity.class.getSimpleName();
-
- @Override
- protected void onResume() {
- super.onResume();
- final int strId;
- if (mDialogHelper.isUsbDevice()) {
- boolean useRecordWarning = mDialogHelper.deviceHasAudioCapture()
- && !mDialogHelper.packageHasAudioRecordingPermission();
- strId = useRecordWarning
- ? R.string.usb_device_confirm_prompt_warn
- : R.string.usb_device_confirm_prompt;
- } else {
- // UsbAccessory case
- strId = R.string.usb_accessory_confirm_prompt;
- }
- CharSequence text = getString(strId, mDialogHelper.getAppName(),
- mDialogHelper.getDeviceDescription());
- initUI(mDialogHelper.getAppName(), text);
- }
-
- @Override
- void onConfirm() {
- mDialogHelper.grantUidAccessPermission();
- mDialogHelper.confirmDialogStartActivity();
- finish();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbDialogActivity.java b/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbDialogActivity.java
deleted file mode 100644
index 1c003ea..0000000
--- a/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbDialogActivity.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.usb.tv;
-
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-import com.android.systemui.tv.TvBottomSheetActivity;
-import com.android.systemui.usb.UsbDialogHelper;
-
-abstract class TvUsbDialogActivity extends TvBottomSheetActivity implements View.OnClickListener {
- private static final String TAG = TvUsbDialogActivity.class.getSimpleName();
- UsbDialogHelper mDialogHelper;
-
- @Override
- public final void onCreate(Bundle b) {
- super.onCreate(b);
- getWindow().addPrivateFlags(
- WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
- try {
- mDialogHelper = new UsbDialogHelper(getApplicationContext(), getIntent());
- } catch (IllegalStateException e) {
- Log.e(TAG, "unable to initialize", e);
- finish();
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mDialogHelper.registerUsbDisconnectedReceiver(this);
- }
-
- @Override
- protected void onPause() {
- if (mDialogHelper != null) {
- mDialogHelper.unregisterUsbDisconnectedReceiver(this);
- }
- super.onPause();
- }
-
- @Override
- public void onClick(View v) {
- if (v.getId() == R.id.bottom_sheet_positive_button) {
- onConfirm();
- } else {
- finish();
- }
- }
-
- /**
- * Called when the ok button is clicked.
- */
- abstract void onConfirm();
-
- void initUI(CharSequence title, CharSequence text) {
- TextView titleTextView = findViewById(R.id.bottom_sheet_title);
- TextView contentTextView = findViewById(R.id.bottom_sheet_body);
- ImageView icon = findViewById(R.id.bottom_sheet_icon);
- ImageView secondIcon = findViewById(R.id.bottom_sheet_second_icon);
- Button okButton = findViewById(R.id.bottom_sheet_positive_button);
- Button cancelButton = findViewById(R.id.bottom_sheet_negative_button);
-
- titleTextView.setText(title);
- contentTextView.setText(text);
- icon.setImageResource(com.android.internal.R.drawable.ic_usb_48dp);
- secondIcon.setVisibility(View.GONE);
- okButton.setText(android.R.string.ok);
- okButton.setOnClickListener(this);
-
- cancelButton.setText(android.R.string.cancel);
- cancelButton.setOnClickListener(this);
- cancelButton.requestFocus();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbPermissionActivity.java
deleted file mode 100644
index 7b415b7..0000000
--- a/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbPermissionActivity.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.usb.tv;
-
-import com.android.systemui.R;
-
-/**
- * Dialog shown when a package requests access to a USB device or accessory on TVs.
- */
-public class TvUsbPermissionActivity extends TvUsbDialogActivity {
- private static final String TAG = TvUsbPermissionActivity.class.getSimpleName();
-
- private boolean mPermissionGranted = false;
-
- @Override
- public void onResume() {
- super.onResume();
- final int strId;
- if (mDialogHelper.isUsbDevice()) {
- boolean useRecordWarning = mDialogHelper.deviceHasAudioCapture()
- && !mDialogHelper.packageHasAudioRecordingPermission();
- strId = useRecordWarning
- ? R.string.usb_device_permission_prompt_warn
- : R.string.usb_device_permission_prompt;
- } else {
- // UsbAccessory case
- strId = R.string.usb_accessory_permission_prompt;
- }
- CharSequence text = getString(strId, mDialogHelper.getAppName(),
- mDialogHelper.getDeviceDescription());
- initUI(mDialogHelper.getAppName(), text);
- }
-
- @Override
- protected void onPause() {
- if (isFinishing()) {
- mDialogHelper.sendPermissionDialogResponse(mPermissionGranted);
- }
- super.onPause();
- }
-
- @Override
- void onConfirm() {
- mDialogHelper.grantUidAccessPermission();
- mPermissionGranted = true;
- finish();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt b/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
index 693c270..5582ced 100644
--- a/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
@@ -23,7 +23,6 @@
* changes.
*/
interface UserAwareController {
- @JvmDefault
fun changeUser(newUser: UserHandle) {}
val currentUserId: Int
diff --git a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
index db2aca8..65a0218 100644
--- a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
@@ -16,32 +16,34 @@
package com.android.systemui.util
-import android.app.WallpaperInfo
import android.app.WallpaperManager
import android.util.Log
import android.view.View
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.wallpapers.data.repository.WallpaperRepository
import javax.inject.Inject
import kotlin.math.max
private const val TAG = "WallpaperController"
+/**
+ * Controller for wallpaper-related logic.
+ *
+ * Note: New logic should be added to [WallpaperRepository], not this class.
+ */
@SysUISingleton
-class WallpaperController @Inject constructor(private val wallpaperManager: WallpaperManager) {
+class WallpaperController @Inject constructor(
+ private val wallpaperManager: WallpaperManager,
+ private val wallpaperRepository: WallpaperRepository,
+) {
var rootView: View? = null
private var notificationShadeZoomOut: Float = 0f
private var unfoldTransitionZoomOut: Float = 0f
- private var wallpaperInfo: WallpaperInfo? = null
-
- fun onWallpaperInfoUpdated(wallpaperInfo: WallpaperInfo?) {
- this.wallpaperInfo = wallpaperInfo
- }
-
private val shouldUseDefaultUnfoldTransition: Boolean
- get() = wallpaperInfo?.shouldUseDefaultUnfoldTransition()
+ get() = wallpaperRepository.wallpaperInfo.value?.shouldUseDefaultUnfoldTransition()
?: true
fun setNotificationShadeZoom(zoomOut: Float) {
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
index a640589..b45b8cd 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
@@ -16,9 +16,11 @@
package com.android.systemui.wallpapers.data.repository
+import android.app.WallpaperInfo
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
/**
@@ -29,5 +31,6 @@
*/
@SysUISingleton
class NoopWallpaperRepository @Inject constructor() : WallpaperRepository {
+ override val wallpaperInfo: StateFlow<WallpaperInfo?> = MutableStateFlow(null).asStateFlow()
override val wallpaperSupportsAmbientMode = MutableStateFlow(false).asStateFlow()
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
index 48895ff..b8f9583 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.wallpapers.data.repository
+import android.app.WallpaperInfo
import android.app.WallpaperManager
import android.content.Context
import android.content.Intent
@@ -36,11 +37,15 @@
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
/** A repository storing information about the current wallpaper. */
interface WallpaperRepository {
+ /** Emits the current user's current wallpaper. */
+ val wallpaperInfo: StateFlow<WallpaperInfo?>
+
/** Emits true if the current user's current wallpaper supports ambient mode. */
val wallpaperSupportsAmbientMode: StateFlow<Boolean>
}
@@ -78,28 +83,35 @@
// Only update the wallpaper status once the user selection has finished.
.filter { it.selectionStatus == SelectionStatus.SELECTION_COMPLETE }
- override val wallpaperSupportsAmbientMode: StateFlow<Boolean> =
+ override val wallpaperInfo: StateFlow<WallpaperInfo?> =
if (!wallpaperManager.isWallpaperSupported || !deviceSupportsAodWallpaper) {
- MutableStateFlow(false).asStateFlow()
+ MutableStateFlow(null).asStateFlow()
} else {
combine(wallpaperChanged, selectedUser) { _, selectedUser ->
- doesWallpaperSupportAmbientMode(selectedUser)
+ getWallpaper(selectedUser)
}
.stateIn(
scope,
// Always be listening for wallpaper changes.
SharingStarted.Eagerly,
- initialValue =
- doesWallpaperSupportAmbientMode(userRepository.selectedUser.value),
+ initialValue = getWallpaper(userRepository.selectedUser.value),
)
}
- private fun doesWallpaperSupportAmbientMode(selectedUser: SelectedUserModel): Boolean {
- return wallpaperManager
- .getWallpaperInfoForUser(
- selectedUser.userInfo.id,
+ override val wallpaperSupportsAmbientMode: StateFlow<Boolean> =
+ wallpaperInfo
+ .map {
+ // If WallpaperInfo is null, it's ImageWallpaper which never supports ambient mode.
+ it?.supportsAmbientMode() == true
+ }
+ .stateIn(
+ scope,
+ // Always be listening for wallpaper changes.
+ SharingStarted.Eagerly,
+ initialValue = wallpaperInfo.value?.supportsAmbientMode() == true,
)
- // If WallpaperInfo is null, it's ImageWallpaper which never supports ambient mode.
- ?.supportsAmbientMode() == true
+
+ private fun getWallpaper(selectedUser: SelectedUserModel): WallpaperInfo? {
+ return wallpaperManager.getWallpaperInfoForUser(selectedUser.userInfo.id)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index a5365fb..2acd4b9 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -621,13 +621,9 @@
}
private boolean isDismissableFromBubbles(NotificationEntry e) {
- if (mNotifPipelineFlags.allowDismissOngoing()) {
- // Bubbles are only accessible from the unlocked state,
- // so we can calculate this from the Notification flags only.
- return e.isDismissableForState(/*isLocked=*/ false);
- } else {
- return e.legacyIsDismissableRecursive();
- }
+ // Bubbles are only accessible from the unlocked state,
+ // so we can calculate this from the Notification flags only.
+ return e.isDismissableForState(/*isLocked=*/ false);
}
private boolean shouldBubbleUp(NotificationEntry e) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 5d75428..cb18229 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -76,7 +76,7 @@
private lateinit var mKeyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
- @Mock private lateinit var mPostureController: DevicePostureController
+ @Mock private lateinit var mPostureController: DevicePostureController
private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController
private lateinit var fakeFeatureFlags: FakeFeatureFlags
@@ -119,7 +119,7 @@
mKeyguardPatternViewController.onViewAttached()
- assertThat(getPatternTopGuideline()).isEqualTo(getExpectedTopGuideline())
+ assertThat(getPatternTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
}
@Test
@@ -131,15 +131,20 @@
mKeyguardPatternViewController.onViewAttached()
// Verify view begins in posture state DEVICE_POSTURE_HALF_OPENED
- assertThat(getPatternTopGuideline()).isEqualTo(getExpectedTopGuideline())
+ assertThat(getPatternTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
// Simulate posture change to state DEVICE_POSTURE_OPENED with callback
verify(mPostureController).addCallback(postureCallbackCaptor.capture())
val postureCallback: DevicePostureController.Callback = postureCallbackCaptor.value
postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
- // Verify view is now in posture state DEVICE_POSTURE_OPENED
- assertThat(getPatternTopGuideline()).isNotEqualTo(getExpectedTopGuideline())
+ // Simulate posture change to same state with callback
+ assertThat(getPatternTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())
+
+ postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
+
+ // Verify view is still in posture state DEVICE_POSTURE_OPENED
+ assertThat(getPatternTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())
}
private fun getPatternTopGuideline(): Float {
@@ -150,7 +155,7 @@
return cs.getConstraint(R.id.pattern_top_guideline).layout.guidePercent
}
- private fun getExpectedTopGuideline(): Float {
+ private fun getHalfOpenedBouncerHeightRatio(): Float {
return mContext.resources.getFloat(R.dimen.half_opened_bouncer_height_ratio)
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index d256ee1..4dc7652 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -19,6 +19,8 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
@@ -32,6 +34,8 @@
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -51,7 +55,10 @@
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class KeyguardPinViewControllerTest : SysuiTestCase() {
- @Mock private lateinit var keyguardPinView: KeyguardPINView
+
+ private lateinit var objectKeyguardPINView: KeyguardPINView
+
+ @Mock private lateinit var mockKeyguardPinView: KeyguardPINView
@Mock private lateinit var keyguardMessageArea: BouncerKeyguardMessageArea
@@ -83,64 +90,73 @@
@Mock lateinit var deleteButton: NumPadButton
@Mock lateinit var enterButton: View
- private lateinit var pinViewController: KeyguardPinViewController
-
@Captor lateinit var postureCallbackCaptor: ArgumentCaptor<DevicePostureController.Callback>
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- Mockito.`when`(keyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
+ Mockito.`when`(mockKeyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
.thenReturn(keyguardMessageArea)
Mockito.`when`(
keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java))
)
.thenReturn(keyguardMessageAreaController)
- `when`(keyguardPinView.passwordTextViewId).thenReturn(R.id.pinEntry)
- `when`(keyguardPinView.findViewById<PasswordTextView>(R.id.pinEntry))
+ `when`(mockKeyguardPinView.passwordTextViewId).thenReturn(R.id.pinEntry)
+ `when`(mockKeyguardPinView.findViewById<PasswordTextView>(R.id.pinEntry))
.thenReturn(passwordTextView)
- `when`(keyguardPinView.resources).thenReturn(context.resources)
- `when`(keyguardPinView.findViewById<NumPadButton>(R.id.delete_button))
+ `when`(mockKeyguardPinView.resources).thenReturn(context.resources)
+ `when`(mockKeyguardPinView.findViewById<NumPadButton>(R.id.delete_button))
.thenReturn(deleteButton)
- `when`(keyguardPinView.findViewById<View>(R.id.key_enter)).thenReturn(enterButton)
+ `when`(mockKeyguardPinView.findViewById<View>(R.id.key_enter)).thenReturn(enterButton)
// For posture tests:
- `when`(keyguardPinView.buttons).thenReturn(arrayOf())
+ `when`(mockKeyguardPinView.buttons).thenReturn(arrayOf())
`when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
- pinViewController =
- KeyguardPinViewController(
- keyguardPinView,
- keyguardUpdateMonitor,
- securityMode,
- lockPatternUtils,
- mKeyguardSecurityCallback,
- keyguardMessageAreaControllerFactory,
- mLatencyTracker,
- liftToActivateListener,
- mEmergencyButtonController,
- falsingCollector,
- postureController,
- featureFlags
- )
+ objectKeyguardPINView =
+ View.inflate(mContext, R.layout.keyguard_pin_view, null)
+ .findViewById(R.id.keyguard_pin_view) as KeyguardPINView
+ }
+
+ private fun constructPinViewController(
+ mKeyguardPinView: KeyguardPINView
+ ): KeyguardPinViewController {
+ return KeyguardPinViewController(
+ mKeyguardPinView,
+ keyguardUpdateMonitor,
+ securityMode,
+ lockPatternUtils,
+ mKeyguardSecurityCallback,
+ keyguardMessageAreaControllerFactory,
+ mLatencyTracker,
+ liftToActivateListener,
+ mEmergencyButtonController,
+ falsingCollector,
+ postureController,
+ featureFlags
+ )
}
@Test
- fun onViewAttached_deviceHalfFolded_propagatedToPinView() {
- `when`(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
+ fun onViewAttached_deviceHalfFolded_propagatedToPatternView() {
+ val pinViewController = constructPinViewController(objectKeyguardPINView)
+ overrideResource(R.dimen.half_opened_bouncer_height_ratio, 0.5f)
+ whenever(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
pinViewController.onViewAttached()
- verify(keyguardPinView).onDevicePostureChanged(DEVICE_POSTURE_HALF_OPENED)
+ assertThat(getPinTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
}
@Test
- fun onDevicePostureChanged_deviceHalfFolded_propagatedToPinView() {
- `when`(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
+ fun onDevicePostureChanged_deviceOpened_propagatedToPatternView() {
+ val pinViewController = constructPinViewController(objectKeyguardPINView)
+ overrideResource(R.dimen.half_opened_bouncer_height_ratio, 0.5f)
+
+ whenever(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
+ pinViewController.onViewAttached()
// Verify view begins in posture state DEVICE_POSTURE_HALF_OPENED
- pinViewController.onViewAttached()
-
- verify(keyguardPinView).onDevicePostureChanged(DEVICE_POSTURE_HALF_OPENED)
+ assertThat(getPinTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
// Simulate posture change to state DEVICE_POSTURE_OPENED with callback
verify(postureController).addCallback(postureCallbackCaptor.capture())
@@ -148,31 +164,57 @@
postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
// Verify view is now in posture state DEVICE_POSTURE_OPENED
- verify(keyguardPinView).onDevicePostureChanged(DEVICE_POSTURE_OPENED)
+ assertThat(getPinTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())
+
+ // Simulate posture change to same state with callback
+ postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
+
+ // Verify view is still in posture state DEVICE_POSTURE_OPENED
+ assertThat(getPinTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())
+ }
+
+ private fun getPinTopGuideline(): Float {
+ val cs = ConstraintSet()
+ val container = objectKeyguardPINView.findViewById(R.id.pin_container) as ConstraintLayout
+ cs.clone(container)
+ return cs.getConstraint(R.id.pin_pad_top_guideline).layout.guidePercent
+ }
+
+ private fun getHalfOpenedBouncerHeightRatio(): Float {
+ return mContext.resources.getFloat(R.dimen.half_opened_bouncer_height_ratio)
}
@Test
fun startAppearAnimation() {
+ val pinViewController = constructPinViewController(mockKeyguardPinView)
+
pinViewController.startAppearAnimation()
+
verify(keyguardMessageAreaController)
.setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false)
}
@Test
fun startAppearAnimation_withExistingMessage() {
+ val pinViewController = constructPinViewController(mockKeyguardPinView)
Mockito.`when`(keyguardMessageAreaController.message).thenReturn("Unlock to continue.")
+
pinViewController.startAppearAnimation()
+
verify(keyguardMessageAreaController, Mockito.never()).setMessage(anyString(), anyBoolean())
}
@Test
fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsLessThan5() {
+ val pinViewController = constructPinViewController(mockKeyguardPinView)
`when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
+ `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
`when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
`when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(3)
`when`(passwordTextView.text).thenReturn("")
pinViewController.startAppearAnimation()
+
verify(deleteButton).visibility = View.INVISIBLE
verify(enterButton).visibility = View.INVISIBLE
verify(passwordTextView).setUsePinShapes(true)
@@ -181,12 +223,15 @@
@Test
fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsMoreThan5() {
+ val pinViewController = constructPinViewController(mockKeyguardPinView)
`when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
+ `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
`when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
`when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(6)
`when`(passwordTextView.text).thenReturn("")
pinViewController.startAppearAnimation()
+
verify(deleteButton).visibility = View.VISIBLE
verify(enterButton).visibility = View.VISIBLE
verify(passwordTextView).setUsePinShapes(true)
@@ -195,7 +240,10 @@
@Test
fun handleLockout_readsNumberOfErrorAttempts() {
+ val pinViewController = constructPinViewController(mockKeyguardPinView)
+
pinViewController.handleAttemptLockout(0)
+
verify(lockPatternUtils).getCurrentFailedPasswordAttempts(anyInt())
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 512e5dc..7114c22 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -27,6 +27,7 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.keyguard.logging.KeyguardLogger;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ClockConfig;
import com.android.systemui.plugins.ClockController;
@@ -62,6 +63,8 @@
@Mock private FeatureFlags mFeatureFlags;
@Mock private InteractionJankMonitor mInteractionJankMonitor;
+ @Mock private DumpManager mDumpManager;
+
@Captor
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor;
@@ -82,7 +85,8 @@
mScreenOffAnimationController,
mKeyguardLogger,
mFeatureFlags,
- mInteractionJankMonitor) {
+ mInteractionJankMonitor,
+ mDumpManager) {
@Override
void setProperty(
AnimatableProperty property,
@@ -170,4 +174,12 @@
verify(mKeyguardClockSwitchController, times(1)).setSplitShadeEnabled(false);
verify(mKeyguardClockSwitchController, times(0)).setSplitShadeEnabled(true);
}
+
+ @Test
+ public void correctlyDump() {
+ mController.onInit();
+ verify(mDumpManager).registerDumpable(mController);
+ mController.onDestroy();
+ verify(mDumpManager, times(1)).unregisterDumpable(KeyguardStatusViewController.TAG);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
index 6decb88..5867a40c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
@@ -30,6 +30,8 @@
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -43,12 +45,14 @@
@RunWithLooper
public class ExpandHelperTest extends SysuiTestCase {
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private ExpandableNotificationRow mRow;
private ExpandHelper mExpandHelper;
private ExpandHelper.Callback mCallback;
@Before
public void setUp() throws Exception {
+ mFeatureFlags.setDefault(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION);
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
mDependency.injectMockDependency(NotificationMediaManager.class);
allowTestableLooperAsMainThread();
@@ -56,7 +60,8 @@
NotificationTestHelper helper = new NotificationTestHelper(
mContext,
mDependency,
- TestableLooper.get(this));
+ TestableLooper.get(this),
+ mFeatureFlags);
mRow = helper.createRow();
mCallback = mock(ExpandHelper.Callback.class);
mExpandHelper = new ExpandHelper(context, mCallback, 10, 100);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index eaa31ac..ecc776b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -48,6 +48,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.airbnb.lottie.LottieAnimationView
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
@@ -64,7 +65,6 @@
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.FakeExecutor
@@ -153,8 +153,8 @@
mock(KeyguardStateController::class.java),
keyguardBouncerRepository,
FakeBiometricSettingsRepository(),
- FakeDeviceEntryFingerprintAuthRepository(),
FakeSystemClock(),
+ mock(KeyguardUpdateMonitor::class.java),
)
displayStateInteractor =
DisplayStateInteractorImpl(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 9df06dc..8dfeb3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -34,7 +34,6 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -106,8 +105,8 @@
mock(KeyguardStateController::class.java),
keyguardBouncerRepository,
mock(BiometricSettingsRepository::class.java),
- mock(DeviceEntryFingerprintAuthRepository::class.java),
mock(SystemClock::class.java),
+ mKeyguardUpdateMonitor,
)
return createUdfpsKeyguardViewController(
/* useModernBouncer */ true, /* useExpandedOverlay */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt
new file mode 100644
index 0000000..f9b590f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.shared.model.BiometricModality
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class PromptHistoryImplTest : SysuiTestCase() {
+
+ private lateinit var history: PromptHistoryImpl
+
+ @Before
+ fun setup() {
+ history = PromptHistoryImpl()
+ }
+
+ @Test
+ fun empty() {
+ assertThat(history.faceFailed).isFalse()
+ assertThat(history.fingerprintFailed).isFalse()
+ }
+
+ @Test
+ fun faceFailed() =
+ repeat(2) {
+ history.failure(BiometricModality.None)
+ history.failure(BiometricModality.Face)
+
+ assertThat(history.faceFailed).isTrue()
+ assertThat(history.fingerprintFailed).isFalse()
+ }
+
+ @Test
+ fun fingerprintFailed() =
+ repeat(2) {
+ history.failure(BiometricModality.None)
+ history.failure(BiometricModality.Fingerprint)
+
+ assertThat(history.faceFailed).isFalse()
+ assertThat(history.fingerprintFailed).isTrue()
+ }
+
+ @Test
+ fun coexFailed() =
+ repeat(2) {
+ history.failure(BiometricModality.Face)
+ history.failure(BiometricModality.Fingerprint)
+
+ assertThat(history.faceFailed).isTrue()
+ assertThat(history.fingerprintFailed).isTrue()
+
+ history.failure(BiometricModality.None)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 91140a9..40b1f20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -335,7 +335,7 @@
error,
messageAfterError = "or me",
authenticateAfterError = false,
- suppressIf = { _ -> true },
+ suppressIf = { _, _ -> true },
)
}
}
@@ -364,7 +364,7 @@
error,
messageAfterError = "$error $afterSuffix",
authenticateAfterError = false,
- suppressIf = { currentMessage -> suppress && currentMessage.isError },
+ suppressIf = { currentMessage, _ -> suppress && currentMessage.isError },
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
index 37b9ca4..186df02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
@@ -18,6 +18,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
@@ -54,6 +55,7 @@
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var systemClock: SystemClock
@Mock private lateinit var bouncerLogger: TableLogBuffer
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Before
fun setup() {
@@ -72,8 +74,8 @@
keyguardStateController,
bouncerRepository,
biometricSettingsRepository,
- deviceEntryFingerprintAuthRepository,
systemClock,
+ keyguardUpdateMonitor,
)
}
@@ -118,7 +120,7 @@
@Test
fun canShowAlternateBouncerForFingerprint_fingerprintLockedOut() {
givenCanShowAlternateBouncer()
- deviceEntryFingerprintAuthRepository.setLockedOut(true)
+ whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true)
assertFalse(underTest.canShowAlternateBouncerForFingerprint())
}
@@ -168,7 +170,7 @@
biometricSettingsRepository.setFingerprintEnrolled(true)
biometricSettingsRepository.setStrongBiometricAllowed(true)
biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(true)
- deviceEntryFingerprintAuthRepository.setLockedOut(false)
+ whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
whenever(keyguardStateController.isUnlocked).thenReturn(false)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
index 461ec65..40f0ed3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
@@ -28,10 +28,10 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dreams.DreamLogger;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.log.core.FakeLogBuffer;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -57,8 +57,6 @@
private FakeFeatureFlags mFeatureFlags;
@Mock
private Observer mObserver;
- @Mock
- private DreamLogger mLogger;
@Before
public void setUp() {
@@ -70,7 +68,7 @@
mExecutor,
/* overlayEnabled= */ true,
mFeatureFlags,
- mLogger);
+ FakeLogBuffer.Factory.Companion.create());
mLiveData = new ComplicationCollectionLiveData(mStateController);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
index a00e545..57307fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
@@ -9,6 +9,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.complication.ComplicationHostViewController
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
+import com.android.systemui.log.core.FakeLogBuffer
import com.android.systemui.statusbar.BlurUtils
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.mockito.argumentCaptor
@@ -47,7 +48,7 @@
@Mock private lateinit var stateController: DreamOverlayStateController
@Mock private lateinit var configController: ConfigurationController
@Mock private lateinit var transitionViewModel: DreamingToLockscreenTransitionViewModel
- @Mock private lateinit var logger: DreamLogger
+ private val logBuffer = FakeLogBuffer.Factory.create()
private lateinit var controller: DreamOverlayAnimationsController
@Before
@@ -66,7 +67,7 @@
DREAM_IN_COMPLICATIONS_ANIMATION_DURATION,
DREAM_IN_TRANSLATION_Y_DISTANCE,
DREAM_IN_TRANSLATION_Y_DURATION,
- logger
+ logBuffer
)
val mockView: View = mock()
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 2c1ebe4..44a78ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -34,6 +34,8 @@
import com.android.systemui.complication.Complication;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.core.FakeLogBuffer;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -58,8 +60,7 @@
@Mock
private FeatureFlags mFeatureFlags;
- @Mock
- private DreamLogger mLogger;
+ private final LogBuffer mLogBuffer = FakeLogBuffer.Factory.Companion.create();
final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@@ -408,6 +409,11 @@
}
private DreamOverlayStateController getDreamOverlayStateController(boolean overlayEnabled) {
- return new DreamOverlayStateController(mExecutor, overlayEnabled, mFeatureFlags, mLogger);
+ return new DreamOverlayStateController(
+ mExecutor,
+ overlayEnabled,
+ mFeatureFlags,
+ mLogBuffer
+ );
}
}
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 5dc0e55..4e74f451 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -48,6 +48,8 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.core.FakeLogBuffer;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.statusbar.policy.NextAlarmController;
@@ -113,8 +115,8 @@
DreamOverlayStateController mDreamOverlayStateController;
@Mock
UserTracker mUserTracker;
- @Mock
- DreamLogger mLogger;
+
+ LogBuffer mLogBuffer = FakeLogBuffer.Factory.Companion.create();
@Captor
private ArgumentCaptor<DreamOverlayStateController.Callback> mCallbackCaptor;
@@ -149,7 +151,7 @@
mDreamOverlayStatusBarItemsProvider,
mDreamOverlayStateController,
mUserTracker,
- mLogger);
+ mLogBuffer);
}
@Test
@@ -293,7 +295,7 @@
mDreamOverlayStatusBarItemsProvider,
mDreamOverlayStateController,
mUserTracker,
- mLogger);
+ mLogBuffer);
controller.onViewAttached();
verify(mView, never()).showIcon(
eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelperTest.java
deleted file mode 100644
index eb998cc..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelperTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.hdmi;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.settings.SecureSettings;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Locale;
-import java.util.concurrent.Executor;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class HdmiCecSetMenuLanguageHelperTest extends SysuiTestCase {
-
- private HdmiCecSetMenuLanguageHelper mHdmiCecSetMenuLanguageHelper;
-
- @Mock
- private Executor mExecutor;
-
- @Mock
- private SecureSettings mSecureSettings;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mSecureSettings.getStringForUser(
- Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST,
- UserHandle.USER_CURRENT)).thenReturn(null);
- mHdmiCecSetMenuLanguageHelper =
- new HdmiCecSetMenuLanguageHelper(mExecutor, mSecureSettings);
- }
-
- @Test
- public void testSetGetLocale() {
- mHdmiCecSetMenuLanguageHelper.setLocale("en");
- assertThat(mHdmiCecSetMenuLanguageHelper.getLocale()).isEqualTo(Locale.ENGLISH);
- }
-
- @Test
- public void testIsLocaleDenylisted_EmptyByDefault() {
- mHdmiCecSetMenuLanguageHelper.setLocale("en");
- assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(false);
- }
-
- @Test
- public void testIsLocaleDenylisted_AcceptLanguage() {
- mHdmiCecSetMenuLanguageHelper.setLocale("de");
- mHdmiCecSetMenuLanguageHelper.acceptLocale();
- assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(false);
- verify(mExecutor).execute(any());
- }
-
- @Test
- public void testIsLocaleDenylisted_DeclineLanguage() {
- mHdmiCecSetMenuLanguageHelper.setLocale("de");
- mHdmiCecSetMenuLanguageHelper.declineLocale();
- assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(true);
- verify(mSecureSettings).putStringForUser(
- Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, "de",
- UserHandle.USER_CURRENT);
- }
-
- @Test
- public void testIsLocaleDenylisted_DeclineTwoLanguages() {
- mHdmiCecSetMenuLanguageHelper.setLocale("de");
- mHdmiCecSetMenuLanguageHelper.declineLocale();
- assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(true);
- verify(mSecureSettings).putStringForUser(
- Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, "de",
- UserHandle.USER_CURRENT);
- mHdmiCecSetMenuLanguageHelper.setLocale("pl");
- mHdmiCecSetMenuLanguageHelper.declineLocale();
- assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(true);
- verify(mSecureSettings).putStringForUser(
- Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, "de,pl",
- UserHandle.USER_CURRENT);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index ddcbe79..00f67a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -78,6 +78,7 @@
import com.android.keyguard.KeyguardSecurityView;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.TestScopeProvider;
import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.DejankUtils;
import com.android.systemui.SysuiTestCase;
@@ -115,9 +116,11 @@
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.settings.SystemSettings;
import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.wallpapers.data.repository.FakeWallpaperRepository;
import com.android.wm.shell.keyguard.KeyguardTransitions;
import org.junit.After;
@@ -132,6 +135,7 @@
import kotlinx.coroutines.CoroutineDispatcher;
import kotlinx.coroutines.flow.Flow;
+import kotlinx.coroutines.test.TestScope;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -139,6 +143,9 @@
public class KeyguardViewMediatorTest extends SysuiTestCase {
private KeyguardViewMediator mViewMediator;
+ private final TestScope mTestScope = TestScopeProvider.getTestScope();
+ private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
+
private @Mock UserTracker mUserTracker;
private @Mock DevicePolicyManager mDevicePolicyManager;
private @Mock LockPatternUtils mLockPatternUtils;
@@ -191,6 +198,7 @@
private @Mock SecureSettings mSecureSettings;
private @Mock AlarmManager mAlarmManager;
private FakeSystemClock mSystemClock;
+ private final FakeWallpaperRepository mWallpaperRepository = new FakeWallpaperRepository();
/** Most recent value passed to {@link KeyguardStateController#notifyKeyguardGoingAway}. */
private boolean mKeyguardGoingAway = false;
@@ -928,6 +936,8 @@
mKeyguardTransitions,
mInteractionJankMonitor,
mDreamOverlayStateController,
+ mJavaAdapter,
+ mWallpaperRepository,
() -> mShadeController,
() -> mNotificationShadeWindowController,
() -> mActivityLaunchAnimator,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index f9070b3..c6a2fa5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -162,11 +162,11 @@
@Test
fun convenienceBiometricAllowedChange() =
testScope.runTest {
+ overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, false)
createBiometricSettingsRepository()
val convenienceBiometricAllowed =
collectLastValue(underTest.isNonStrongBiometricAllowed)
runCurrent()
-
onNonStrongAuthChanged(true, PRIMARY_USER_ID)
assertThat(convenienceBiometricAllowed()).isTrue()
@@ -175,6 +175,45 @@
onNonStrongAuthChanged(false, PRIMARY_USER_ID)
assertThat(convenienceBiometricAllowed()).isFalse()
+ mContext.orCreateTestableResources.removeOverride(
+ com.android.internal.R.bool.config_strongAuthRequiredOnBoot
+ )
+ }
+
+ @Test
+ fun whenStrongAuthRequiredAfterBoot_nonStrongBiometricNotAllowed() =
+ testScope.runTest {
+ overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, true)
+ createBiometricSettingsRepository()
+
+ val convenienceBiometricAllowed =
+ collectLastValue(underTest.isNonStrongBiometricAllowed)
+ runCurrent()
+ onNonStrongAuthChanged(true, PRIMARY_USER_ID)
+
+ assertThat(convenienceBiometricAllowed()).isFalse()
+ mContext.orCreateTestableResources.removeOverride(
+ com.android.internal.R.bool.config_strongAuthRequiredOnBoot
+ )
+ }
+
+ @Test
+ fun whenStrongBiometricAuthIsNotAllowed_nonStrongBiometrics_alsoNotAllowed() =
+ testScope.runTest {
+ overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, false)
+ createBiometricSettingsRepository()
+
+ val convenienceBiometricAllowed =
+ collectLastValue(underTest.isNonStrongBiometricAllowed)
+ runCurrent()
+ onNonStrongAuthChanged(true, PRIMARY_USER_ID)
+ assertThat(convenienceBiometricAllowed()).isTrue()
+
+ onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, PRIMARY_USER_ID)
+ assertThat(convenienceBiometricAllowed()).isFalse()
+ mContext.orCreateTestableResources.removeOverride(
+ com.android.internal.R.bool.config_strongAuthRequiredOnBoot
+ )
}
private fun onStrongAuthChanged(flags: Int, userId: Int) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 020c0b2..8127ac6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -36,6 +36,7 @@
import com.android.keyguard.FaceAuthUiEvent
import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
@@ -43,6 +44,7 @@
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.coroutines.FlowValue
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.dump.DumpManager
import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlags
@@ -50,12 +52,12 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
+import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.shared.model.WakeSleepReason
@@ -113,6 +115,7 @@
@Mock private lateinit var sessionTracker: SessionTracker
@Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Captor
private lateinit var authenticationCallback: ArgumentCaptor<FaceManager.AuthenticationCallback>
@@ -131,8 +134,8 @@
private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
private lateinit var testScope: TestScope
private lateinit var fakeUserRepository: FakeUserRepository
- private lateinit var authStatus: FlowValue<AuthenticationStatus?>
- private lateinit var detectStatus: FlowValue<DetectionStatus?>
+ private lateinit var authStatus: FlowValue<FaceAuthenticationStatus?>
+ private lateinit var detectStatus: FlowValue<FaceDetectionStatus?>
private lateinit var authRunning: FlowValue<Boolean?>
private lateinit var bypassEnabled: FlowValue<Boolean?>
private lateinit var lockedOut: FlowValue<Boolean?>
@@ -175,10 +178,10 @@
AlternateBouncerInteractor(
bouncerRepository = bouncerRepository,
biometricSettingsRepository = biometricSettingsRepository,
- deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository,
systemClock = mock(SystemClock::class.java),
keyguardStateController = FakeKeyguardStateController(),
statusBarStateController = mock(StatusBarStateController::class.java),
+ keyguardUpdateMonitor = keyguardUpdateMonitor,
)
bypassStateChangedListener =
@@ -262,7 +265,7 @@
val successResult = successResult()
authenticationCallback.value.onAuthenticationSucceeded(successResult)
- assertThat(authStatus()).isEqualTo(SuccessAuthenticationStatus(successResult))
+ assertThat(authStatus()).isEqualTo(SuccessFaceAuthenticationStatus(successResult))
assertThat(authenticated()).isTrue()
assertThat(authRunning()).isFalse()
assertThat(canFaceAuthRun()).isFalse()
@@ -383,7 +386,10 @@
detectionCallback.value.onFaceDetected(1, 1, true)
- assertThat(detectStatus()).isEqualTo(DetectionStatus(1, 1, true))
+ val status = detectStatus()!!
+ assertThat(status.sensorId).isEqualTo(1)
+ assertThat(status.userId).isEqualTo(1)
+ assertThat(status.isStrongBiometric).isEqualTo(true)
}
@Test
@@ -423,7 +429,7 @@
FACE_ERROR_CANCELED,
"First auth attempt cancellation completed"
)
- val value = authStatus() as ErrorAuthenticationStatus
+ val value = authStatus() as ErrorFaceAuthenticationStatus
assertThat(value.msgId).isEqualTo(FACE_ERROR_CANCELED)
assertThat(value.msg).isEqualTo("First auth attempt cancellation completed")
@@ -449,6 +455,29 @@
}
@Test
+ fun multipleCancelCallsShouldNotCauseMultipleCancellationStatusBeingEmitted() =
+ testScope.runTest {
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+ val emittedValues by collectValues(underTest.authenticationStatus)
+
+ underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ underTest.cancel()
+ advanceTimeBy(100)
+ underTest.cancel()
+
+ advanceTimeBy(DeviceEntryFaceAuthRepositoryImpl.DEFAULT_CANCEL_SIGNAL_TIMEOUT)
+ runCurrent()
+ advanceTimeBy(DeviceEntryFaceAuthRepositoryImpl.DEFAULT_CANCEL_SIGNAL_TIMEOUT)
+ runCurrent()
+
+ assertThat(emittedValues.size).isEqualTo(1)
+ assertThat(emittedValues.first())
+ .isInstanceOf(ErrorFaceAuthenticationStatus::class.java)
+ assertThat((emittedValues.first() as ErrorFaceAuthenticationStatus).msgId).isEqualTo(-1)
+ }
+
+ @Test
fun faceHelpMessagesAreIgnoredBasedOnConfig() =
testScope.runTest {
overrideResource(
@@ -465,7 +494,7 @@
authenticationCallback.value.onAuthenticationHelp(10, "Ignored help msg")
authenticationCallback.value.onAuthenticationHelp(11, "Ignored help msg")
- assertThat(authStatus()).isEqualTo(HelpAuthenticationStatus(9, "help msg"))
+ assertThat(authStatus()).isEqualTo(HelpFaceAuthenticationStatus(9, "help msg"))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
index 264328b..def016a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
@@ -26,7 +26,11 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -49,7 +53,6 @@
@RunWith(AndroidJUnit4::class)
class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- @Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var authController: AuthController
@Captor
private lateinit var updateMonitorCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
@@ -68,7 +71,6 @@
authController,
keyguardUpdateMonitor,
testScope.backgroundScope,
- dumpManager,
)
}
@@ -177,4 +179,129 @@
callback.value.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT)
assertThat(availableFpSensorType()).isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT)
}
+
+ @Test
+ fun onFingerprintSuccess_successAuthenticationStatus() =
+ testScope.runTest {
+ val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+ runCurrent()
+
+ verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+ updateMonitorCallback.value.onBiometricAuthenticated(
+ 0,
+ BiometricSourceType.FINGERPRINT,
+ true,
+ )
+
+ val status = authenticationStatus as SuccessFingerprintAuthenticationStatus
+ assertThat(status.userId).isEqualTo(0)
+ assertThat(status.isStrongBiometric).isEqualTo(true)
+ }
+
+ @Test
+ fun onFingerprintFailed_failedAuthenticationStatus() =
+ testScope.runTest {
+ val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+ runCurrent()
+
+ verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+ updateMonitorCallback.value.onBiometricAuthFailed(
+ BiometricSourceType.FINGERPRINT,
+ )
+
+ assertThat(authenticationStatus)
+ .isInstanceOf(FailFingerprintAuthenticationStatus::class.java)
+ }
+
+ @Test
+ fun onFingerprintError_errorAuthenticationStatus() =
+ testScope.runTest {
+ val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+ runCurrent()
+
+ verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+ updateMonitorCallback.value.onBiometricError(
+ 1,
+ "test_string",
+ BiometricSourceType.FINGERPRINT,
+ )
+
+ val status = authenticationStatus as ErrorFingerprintAuthenticationStatus
+ assertThat(status.msgId).isEqualTo(1)
+ assertThat(status.msg).isEqualTo("test_string")
+ }
+
+ @Test
+ fun onFingerprintHelp_helpAuthenticationStatus() =
+ testScope.runTest {
+ val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+ runCurrent()
+
+ verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+ updateMonitorCallback.value.onBiometricHelp(
+ 1,
+ "test_string",
+ BiometricSourceType.FINGERPRINT,
+ )
+
+ val status = authenticationStatus as HelpFingerprintAuthenticationStatus
+ assertThat(status.msgId).isEqualTo(1)
+ assertThat(status.msg).isEqualTo("test_string")
+ }
+
+ @Test
+ fun onFingerprintAcquired_acquiredAuthenticationStatus() =
+ testScope.runTest {
+ val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+ runCurrent()
+
+ verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+ updateMonitorCallback.value.onBiometricAcquired(
+ BiometricSourceType.FINGERPRINT,
+ 5,
+ )
+
+ val status = authenticationStatus as AcquiredFingerprintAuthenticationStatus
+ assertThat(status.acquiredInfo).isEqualTo(5)
+ }
+
+ @Test
+ fun onFaceCallbacks_fingerprintAuthenticationStatusIsUnchanged() =
+ testScope.runTest {
+ val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+ runCurrent()
+
+ verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+ updateMonitorCallback.value.onBiometricAuthenticated(
+ 0,
+ BiometricSourceType.FACE,
+ true,
+ )
+ assertThat(authenticationStatus).isNull()
+
+ updateMonitorCallback.value.onBiometricAuthFailed(
+ BiometricSourceType.FACE,
+ )
+ assertThat(authenticationStatus).isNull()
+
+ updateMonitorCallback.value.onBiometricHelp(
+ 1,
+ "test_string",
+ BiometricSourceType.FACE,
+ )
+ assertThat(authenticationStatus).isNull()
+
+ updateMonitorCallback.value.onBiometricAcquired(
+ BiometricSourceType.FACE,
+ 5,
+ )
+ assertThat(authenticationStatus).isNull()
+
+ updateMonitorCallback.value.onBiometricError(
+ 1,
+ "test_string",
+ BiometricSourceType.FACE,
+ )
+ assertThat(authenticationStatus).isNull()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index f541815..ba7d349 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -31,11 +31,14 @@
import com.android.systemui.doze.DozeTransitionCallback
import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.dreams.DreamOverlayCallbackController
+import com.android.systemui.keyguard.ScreenLifecycle
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.ScreenModel
+import com.android.systemui.keyguard.shared.model.ScreenState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -71,6 +74,7 @@
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
+ @Mock private lateinit var screenLifecycle: ScreenLifecycle
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Mock private lateinit var dozeTransitionListener: DozeTransitionListener
@Mock private lateinit var authController: AuthController
@@ -92,6 +96,7 @@
KeyguardRepositoryImpl(
statusBarStateController,
wakefulnessLifecycle,
+ screenLifecycle,
biometricUnlockController,
keyguardStateController,
keyguardBypassController,
@@ -160,6 +165,16 @@
}
@Test
+ fun dozeTimeTick() =
+ testScope.runTest {
+ var dozeTimeTickValue = collectLastValue(underTest.dozeTimeTick)
+ underTest.dozeTimeTick()
+ runCurrent()
+
+ assertThat(dozeTimeTickValue()).isNull()
+ }
+
+ @Test
fun isKeyguardShowing() =
testScope.runTest {
whenever(keyguardStateController.isShowing).thenReturn(false)
@@ -371,6 +386,48 @@
}
@Test
+ fun screenModel() =
+ testScope.runTest {
+ val values = mutableListOf<ScreenModel>()
+ val job = underTest.screenModel.onEach(values::add).launchIn(this)
+
+ runCurrent()
+ val captor = argumentCaptor<ScreenLifecycle.Observer>()
+ verify(screenLifecycle).addObserver(captor.capture())
+
+ whenever(screenLifecycle.getScreenState()).thenReturn(ScreenLifecycle.SCREEN_TURNING_ON)
+ captor.value.onScreenTurningOn()
+ runCurrent()
+
+ whenever(screenLifecycle.getScreenState()).thenReturn(ScreenLifecycle.SCREEN_ON)
+ captor.value.onScreenTurnedOn()
+ runCurrent()
+
+ whenever(screenLifecycle.getScreenState())
+ .thenReturn(ScreenLifecycle.SCREEN_TURNING_OFF)
+ captor.value.onScreenTurningOff()
+ runCurrent()
+
+ whenever(screenLifecycle.getScreenState()).thenReturn(ScreenLifecycle.SCREEN_OFF)
+ captor.value.onScreenTurnedOff()
+ runCurrent()
+
+ assertThat(values.map { it.state })
+ .isEqualTo(
+ listOf(
+ // Initial value will be OFF
+ ScreenState.SCREEN_OFF,
+ ScreenState.SCREEN_TURNING_ON,
+ ScreenState.SCREEN_ON,
+ ScreenState.SCREEN_TURNING_OFF,
+ ScreenState.SCREEN_OFF,
+ )
+ )
+
+ job.cancel()
+ }
+
+ @Test
fun isUdfpsSupported() =
testScope.runTest {
whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractorTest.kt
new file mode 100644
index 0000000..3389fa9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractorTest.kt
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.hardware.biometrics.BiometricSourceType.FINGERPRINT
+import android.hardware.fingerprint.FingerprintManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.util.IndicationHelper
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BiometricMessageInteractorTest : SysuiTestCase() {
+
+ private lateinit var underTest: BiometricMessageInteractor
+ private lateinit var testScope: TestScope
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var fingerprintAuthRepository: FakeDeviceEntryFingerprintAuthRepository
+
+ @Mock private lateinit var indicationHelper: IndicationHelper
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ testScope = TestScope()
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
+ underTest =
+ BiometricMessageInteractor(
+ mContext.resources,
+ fingerprintAuthRepository,
+ fingerprintPropertyRepository,
+ indicationHelper,
+ keyguardUpdateMonitor,
+ )
+ }
+
+ @Test
+ fun fingerprintErrorMessage() =
+ testScope.runTest {
+ val fingerprintErrorMessage by collectLastValue(underTest.fingerprintErrorMessage)
+
+ // GIVEN FINGERPRINT_ERROR_HW_UNAVAILABLE should NOT be suppressed
+ whenever(
+ indicationHelper.shouldSuppressErrorMsg(
+ FINGERPRINT,
+ FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE
+ )
+ )
+ .thenReturn(false)
+
+ // WHEN authentication status error is FINGERPRINT_ERROR_HW_UNAVAILABLE
+ fingerprintAuthRepository.setAuthenticationStatus(
+ ErrorFingerprintAuthenticationStatus(
+ msgId = FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ msg = "test"
+ )
+ )
+
+ // THEN fingerprintErrorMessage is updated
+ assertThat(fingerprintErrorMessage?.source).isEqualTo(FINGERPRINT)
+ assertThat(fingerprintErrorMessage?.type).isEqualTo(BiometricMessageType.ERROR)
+ assertThat(fingerprintErrorMessage?.id)
+ .isEqualTo(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE)
+ assertThat(fingerprintErrorMessage?.message).isEqualTo("test")
+ }
+
+ @Test
+ fun fingerprintErrorMessage_suppressedError() =
+ testScope.runTest {
+ val fingerprintErrorMessage by collectLastValue(underTest.fingerprintErrorMessage)
+
+ // GIVEN FINGERPRINT_ERROR_HW_UNAVAILABLE should be suppressed
+ whenever(
+ indicationHelper.shouldSuppressErrorMsg(
+ FINGERPRINT,
+ FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE
+ )
+ )
+ .thenReturn(true)
+
+ // WHEN authentication status error is FINGERPRINT_ERROR_HW_UNAVAILABLE
+ fingerprintAuthRepository.setAuthenticationStatus(
+ ErrorFingerprintAuthenticationStatus(
+ msgId = FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ msg = "test"
+ )
+ )
+
+ // THEN fingerprintErrorMessage isn't update - it's still null
+ assertThat(fingerprintErrorMessage).isNull()
+ }
+
+ @Test
+ fun fingerprintHelpMessage() =
+ testScope.runTest {
+ val fingerprintHelpMessage by collectLastValue(underTest.fingerprintHelpMessage)
+
+ // GIVEN primary auth is NOT required
+ whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+ .thenReturn(true)
+
+ // WHEN authentication status help is FINGERPRINT_ACQUIRED_IMAGER_DIRTY
+ fingerprintAuthRepository.setAuthenticationStatus(
+ HelpFingerprintAuthenticationStatus(
+ msgId = FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY,
+ msg = "test"
+ )
+ )
+
+ // THEN fingerprintHelpMessage is updated
+ assertThat(fingerprintHelpMessage?.source).isEqualTo(FINGERPRINT)
+ assertThat(fingerprintHelpMessage?.type).isEqualTo(BiometricMessageType.HELP)
+ assertThat(fingerprintHelpMessage?.id)
+ .isEqualTo(FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY)
+ assertThat(fingerprintHelpMessage?.message).isEqualTo("test")
+ }
+
+ @Test
+ fun fingerprintHelpMessage_primaryAuthRequired() =
+ testScope.runTest {
+ val fingerprintHelpMessage by collectLastValue(underTest.fingerprintHelpMessage)
+
+ // GIVEN primary auth is required
+ whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+ .thenReturn(false)
+
+ // WHEN authentication status help is FINGERPRINT_ACQUIRED_IMAGER_DIRTY
+ fingerprintAuthRepository.setAuthenticationStatus(
+ HelpFingerprintAuthenticationStatus(
+ msgId = FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY,
+ msg = "test"
+ )
+ )
+
+ // THEN fingerprintHelpMessage isn't update - it's still null
+ assertThat(fingerprintHelpMessage).isNull()
+ }
+
+ @Test
+ fun fingerprintFailMessage_nonUdfps() =
+ testScope.runTest {
+ val fingerprintFailMessage by collectLastValue(underTest.fingerprintFailMessage)
+
+ // GIVEN primary auth is NOT required
+ whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+ .thenReturn(true)
+
+ // GIVEN rear fingerprint (not UDFPS)
+ fingerprintPropertyRepository.setProperties(
+ 0,
+ SensorStrength.STRONG,
+ FingerprintSensorType.REAR,
+ mapOf()
+ )
+
+ // WHEN authentication status fail
+ fingerprintAuthRepository.setAuthenticationStatus(FailFingerprintAuthenticationStatus)
+
+ // THEN fingerprintFailMessage is updated
+ assertThat(fingerprintFailMessage?.source).isEqualTo(FINGERPRINT)
+ assertThat(fingerprintFailMessage?.type).isEqualTo(BiometricMessageType.FAIL)
+ assertThat(fingerprintFailMessage?.id)
+ .isEqualTo(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED)
+ assertThat(fingerprintFailMessage?.message)
+ .isEqualTo(
+ mContext.resources.getString(
+ com.android.internal.R.string.fingerprint_error_not_match
+ )
+ )
+ }
+
+ @Test
+ fun fingerprintFailMessage_udfps() =
+ testScope.runTest {
+ val fingerprintFailMessage by collectLastValue(underTest.fingerprintFailMessage)
+
+ // GIVEN primary auth is NOT required
+ whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+ .thenReturn(true)
+
+ // GIVEN UDFPS
+ fingerprintPropertyRepository.setProperties(
+ 0,
+ SensorStrength.STRONG,
+ FingerprintSensorType.UDFPS_OPTICAL,
+ mapOf()
+ )
+
+ // WHEN authentication status fail
+ fingerprintAuthRepository.setAuthenticationStatus(FailFingerprintAuthenticationStatus)
+
+ // THEN fingerprintFailMessage is updated to udfps message
+ assertThat(fingerprintFailMessage?.source).isEqualTo(FINGERPRINT)
+ assertThat(fingerprintFailMessage?.type).isEqualTo(BiometricMessageType.FAIL)
+ assertThat(fingerprintFailMessage?.id)
+ .isEqualTo(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED)
+ assertThat(fingerprintFailMessage?.message)
+ .isEqualTo(
+ mContext.resources.getString(
+ com.android.internal.R.string.fingerprint_udfps_error_not_match
+ )
+ )
+ }
+
+ @Test
+ fun fingerprintFailedMessage_primaryAuthRequired() =
+ testScope.runTest {
+ val fingerprintFailedMessage by collectLastValue(underTest.fingerprintFailMessage)
+
+ // GIVEN primary auth is required
+ whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+ .thenReturn(false)
+
+ // WHEN authentication status fail
+ fingerprintAuthRepository.setAuthenticationStatus(FailFingerprintAuthenticationStatus)
+
+ // THEN fingerprintFailedMessage isn't update - it's still null
+ assertThat(fingerprintFailedMessage).isNull()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index 3e81cd3..ced0a21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -38,10 +38,9 @@
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -121,8 +120,8 @@
mock(KeyguardStateController::class.java),
bouncerRepository,
mock(BiometricSettingsRepository::class.java),
- FakeDeviceEntryFingerprintAuthRepository(),
FakeSystemClock(),
+ keyguardUpdateMonitor,
),
keyguardTransitionInteractor,
featureFlags,
@@ -160,7 +159,7 @@
underTest.onDeviceLifted()
- val outputValue = authenticationStatus()!! as ErrorAuthenticationStatus
+ val outputValue = authenticationStatus()!! as ErrorFaceAuthenticationStatus
assertThat(outputValue.msgId)
.isEqualTo(BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT)
assertThat(outputValue.msg).isEqualTo("Face Unlock unavailable")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
new file mode 100644
index 0000000..6e52d1a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.content.Context
+import android.content.Intent
+import android.hardware.biometrics.BiometricSourceType
+import android.hardware.fingerprint.FingerprintManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
+import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.util.IndicationHelper
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.ArgumentMatchers.isNull
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class OccludingAppDeviceEntryInteractorTest : SysuiTestCase() {
+
+ private lateinit var underTest: OccludingAppDeviceEntryInteractor
+ private lateinit var testScope: TestScope
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+ private lateinit var fingerprintAuthRepository: FakeDeviceEntryFingerprintAuthRepository
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
+ private lateinit var configurationRepository: FakeConfigurationRepository
+ private lateinit var featureFlags: FakeFeatureFlags
+ private lateinit var trustRepository: FakeTrustRepository
+
+ @Mock private lateinit var indicationHelper: IndicationHelper
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var mockedContext: Context
+ @Mock private lateinit var activityStarter: ActivityStarter
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ testScope = TestScope()
+ biometricSettingsRepository = FakeBiometricSettingsRepository()
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
+ keyguardRepository = FakeKeyguardRepository()
+ bouncerRepository = FakeKeyguardBouncerRepository()
+ configurationRepository = FakeConfigurationRepository()
+ featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.FACE_AUTH_REFACTOR, false)
+ set(Flags.DELAY_BOUNCER, false)
+ }
+ trustRepository = FakeTrustRepository()
+ underTest =
+ OccludingAppDeviceEntryInteractor(
+ BiometricMessageInteractor(
+ mContext.resources,
+ fingerprintAuthRepository,
+ fingerprintPropertyRepository,
+ indicationHelper,
+ keyguardUpdateMonitor,
+ ),
+ fingerprintAuthRepository,
+ KeyguardInteractor(
+ keyguardRepository,
+ commandQueue = mock(),
+ featureFlags,
+ bouncerRepository,
+ configurationRepository,
+ ),
+ PrimaryBouncerInteractor(
+ bouncerRepository,
+ primaryBouncerView = mock(),
+ mainHandler = mock(),
+ keyguardStateController = mock(),
+ keyguardSecurityModel = mock(),
+ primaryBouncerCallbackInteractor = mock(),
+ falsingCollector = mock(),
+ dismissCallbackRegistry = mock(),
+ context,
+ keyguardUpdateMonitor,
+ trustRepository,
+ featureFlags,
+ testScope.backgroundScope,
+ ),
+ AlternateBouncerInteractor(
+ statusBarStateController = mock(),
+ keyguardStateController = mock(),
+ bouncerRepository,
+ biometricSettingsRepository,
+ FakeSystemClock(),
+ keyguardUpdateMonitor,
+ ),
+ testScope.backgroundScope,
+ mockedContext,
+ activityStarter,
+ )
+ }
+
+ @Test
+ fun fingerprintSuccess_goToHomeScreen() =
+ testScope.runTest {
+ givenOnOccludingApp(true)
+ fingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+ runCurrent()
+ verifyGoToHomeScreen()
+ }
+
+ @Test
+ fun fingerprintSuccess_notOnOccludingApp_doesNotGoToHomeScreen() =
+ testScope.runTest {
+ givenOnOccludingApp(false)
+ fingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+ runCurrent()
+ verifyNeverGoToHomeScreen()
+ }
+
+ @Test
+ fun lockout_goToHomeScreenOnDismissAction() =
+ testScope.runTest {
+ givenOnOccludingApp(true)
+ fingerprintAuthRepository.setAuthenticationStatus(
+ ErrorFingerprintAuthenticationStatus(
+ FingerprintManager.FINGERPRINT_ERROR_LOCKOUT,
+ "lockoutTest"
+ )
+ )
+ runCurrent()
+ verifyGoToHomeScreenOnDismiss()
+ }
+
+ @Test
+ fun lockout_notOnOccludingApp_neverGoToHomeScreen() =
+ testScope.runTest {
+ givenOnOccludingApp(false)
+ fingerprintAuthRepository.setAuthenticationStatus(
+ ErrorFingerprintAuthenticationStatus(
+ FingerprintManager.FINGERPRINT_ERROR_LOCKOUT,
+ "lockoutTest"
+ )
+ )
+ runCurrent()
+ verifyNeverGoToHomeScreen()
+ }
+
+ @Test
+ fun message_fpFailOnOccludingApp_thenNotOnOccludingApp() =
+ testScope.runTest {
+ val message by collectLastValue(underTest.message)
+
+ givenOnOccludingApp(true)
+ givenPrimaryAuthRequired(false)
+ runCurrent()
+ // WHEN a fp failure come in
+ fingerprintAuthRepository.setAuthenticationStatus(FailFingerprintAuthenticationStatus)
+ // THEN message set to failure
+ assertThat(message?.type).isEqualTo(BiometricMessageType.FAIL)
+
+ // GIVEN fingerprint shouldn't run
+ givenOnOccludingApp(false)
+ runCurrent()
+ // WHEN another fp failure arrives
+ fingerprintAuthRepository.setAuthenticationStatus(FailFingerprintAuthenticationStatus)
+
+ // THEN message set to null
+ assertThat(message).isNull()
+ }
+
+ @Test
+ fun message_fpErrorHelpFailOnOccludingApp() =
+ testScope.runTest {
+ val message by collectLastValue(underTest.message)
+
+ givenOnOccludingApp(true)
+ givenPrimaryAuthRequired(false)
+ runCurrent()
+
+ // ERROR message
+ fingerprintAuthRepository.setAuthenticationStatus(
+ ErrorFingerprintAuthenticationStatus(
+ FingerprintManager.FINGERPRINT_ERROR_LOCKOUT,
+ "testError",
+ )
+ )
+ assertThat(message?.source).isEqualTo(BiometricSourceType.FINGERPRINT)
+ assertThat(message?.id).isEqualTo(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)
+ assertThat(message?.message).isEqualTo("testError")
+ assertThat(message?.type).isEqualTo(BiometricMessageType.ERROR)
+
+ // HELP message
+ fingerprintAuthRepository.setAuthenticationStatus(
+ HelpFingerprintAuthenticationStatus(
+ FingerprintManager.FINGERPRINT_ACQUIRED_PARTIAL,
+ "testHelp",
+ )
+ )
+ assertThat(message?.source).isEqualTo(BiometricSourceType.FINGERPRINT)
+ assertThat(message?.id).isEqualTo(FingerprintManager.FINGERPRINT_ACQUIRED_PARTIAL)
+ assertThat(message?.message).isEqualTo("testHelp")
+ assertThat(message?.type).isEqualTo(BiometricMessageType.HELP)
+
+ // FAIL message
+ fingerprintAuthRepository.setAuthenticationStatus(FailFingerprintAuthenticationStatus)
+ assertThat(message?.source).isEqualTo(BiometricSourceType.FINGERPRINT)
+ assertThat(message?.id)
+ .isEqualTo(KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED)
+ assertThat(message?.type).isEqualTo(BiometricMessageType.FAIL)
+ }
+
+ private fun givenOnOccludingApp(isOnOccludingApp: Boolean) {
+ keyguardRepository.setKeyguardOccluded(isOnOccludingApp)
+ keyguardRepository.setKeyguardShowing(isOnOccludingApp)
+ bouncerRepository.setPrimaryShow(!isOnOccludingApp)
+ bouncerRepository.setAlternateVisible(!isOnOccludingApp)
+ }
+
+ private fun givenPrimaryAuthRequired(required: Boolean) {
+ whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+ .thenReturn(!required)
+ }
+
+ private fun verifyGoToHomeScreen() {
+ val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
+ verify(mockedContext).startActivity(intentCaptor.capture())
+
+ assertThat(intentCaptor.value.hasCategory(Intent.CATEGORY_HOME)).isTrue()
+ assertThat(intentCaptor.value.action).isEqualTo(Intent.ACTION_MAIN)
+ }
+
+ private fun verifyNeverGoToHomeScreen() {
+ verify(mockedContext, never()).startActivity(any())
+ verify(activityStarter, never())
+ .dismissKeyguardThenExecute(any(OnDismissAction::class.java), isNull(), eq(false))
+ }
+
+ private fun verifyGoToHomeScreenOnDismiss() {
+ val onDimissActionCaptor = ArgumentCaptor.forClass(OnDismissAction::class.java)
+ verify(activityStarter)
+ .dismissKeyguardThenExecute(onDimissActionCaptor.capture(), isNull(), eq(false))
+ onDimissActionCaptor.value.onDismiss()
+
+ verifyGoToHomeScreen()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
index 1baca21..b019a21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
@@ -33,6 +33,9 @@
import com.android.systemui.keyguard.shared.model.WakeSleepReason
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessState
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
+import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
@@ -46,6 +49,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@ExperimentalCoroutinesApi
@@ -63,19 +67,21 @@
private lateinit var fakeCommandQueue: FakeCommandQueue
private lateinit var featureFlags: FakeFeatureFlags
private lateinit var burnInInteractor: BurnInInteractor
+ private lateinit var shadeRepository: FakeShadeRepository
@Mock private lateinit var burnInHelper: BurnInHelperWrapper
+ @Mock private lateinit var dialogManager: SystemUIDialogManager
private lateinit var underTest: UdfpsKeyguardInteractor
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
-
testScope = TestScope()
configRepository = FakeConfigurationRepository()
keyguardRepository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository()
+ shadeRepository = FakeShadeRepository()
fakeCommandQueue = FakeCommandQueue()
featureFlags =
FakeFeatureFlags().apply {
@@ -102,6 +108,8 @@
bouncerRepository,
configRepository,
),
+ shadeRepository,
+ dialogManager,
)
}
@@ -142,6 +150,61 @@
assertThat(burnInOffsets?.burnInXOffset).isEqualTo(burnInXOffset)
}
+ @Test
+ fun dialogHideAffordances() =
+ testScope.runTest {
+ val dialogHideAffordancesRequest by
+ collectLastValue(underTest.dialogHideAffordancesRequest)
+ runCurrent()
+ val captor = argumentCaptor<SystemUIDialogManager.Listener>()
+ verify(dialogManager).registerListener(captor.capture())
+
+ captor.value.shouldHideAffordances(false)
+ assertThat(dialogHideAffordancesRequest).isEqualTo(false)
+
+ captor.value.shouldHideAffordances(true)
+ assertThat(dialogHideAffordancesRequest).isEqualTo(true)
+
+ captor.value.shouldHideAffordances(false)
+ assertThat(dialogHideAffordancesRequest).isEqualTo(false)
+ }
+
+ @Test
+ fun shadeExpansion_updates() =
+ testScope.runTest {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ val shadeExpansion by collectLastValue(underTest.shadeExpansion)
+ assertThat(shadeExpansion).isEqualTo(0f)
+
+ shadeRepository.setUdfpsTransitionToFullShadeProgress(.5f)
+ assertThat(shadeExpansion).isEqualTo(.5f)
+
+ shadeRepository.setUdfpsTransitionToFullShadeProgress(.7f)
+ assertThat(shadeExpansion).isEqualTo(.7f)
+
+ shadeRepository.setUdfpsTransitionToFullShadeProgress(.22f)
+ assertThat(shadeExpansion).isEqualTo(.22f)
+
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+ assertThat(shadeExpansion).isEqualTo(1f)
+ }
+
+ @Test
+ fun qsProgress_updates() =
+ testScope.runTest {
+ val qsProgress by collectLastValue(underTest.qsProgress)
+ assertThat(qsProgress).isEqualTo(0f)
+
+ shadeRepository.setQsExpansion(.22f)
+ assertThat(qsProgress).isEqualTo(.44f)
+
+ shadeRepository.setQsExpansion(.5f)
+ assertThat(qsProgress).isEqualTo(1f)
+
+ shadeRepository.setQsExpansion(.7f)
+ assertThat(qsProgress).isEqualTo(1f)
+ }
+
private fun initializeBurnInOffsets() {
whenever(burnInHelper.burnInProgressOffset()).thenReturn(burnInProgress)
whenever(burnInHelper.burnInOffset(anyInt(), /* xAxis */ eq(true)))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
index 436c09c..b985b3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
@@ -32,6 +32,8 @@
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -58,7 +60,9 @@
private lateinit var keyguardRepository: FakeKeyguardRepository
private lateinit var fakeCommandQueue: FakeCommandQueue
private lateinit var featureFlags: FakeFeatureFlags
+ private lateinit var shadeRepository: FakeShadeRepository
+ @Mock private lateinit var dialogManager: SystemUIDialogManager
@Mock private lateinit var burnInHelper: BurnInHelperWrapper
@Before
@@ -70,6 +74,7 @@
keyguardRepository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository()
fakeCommandQueue = FakeCommandQueue()
+ shadeRepository = FakeShadeRepository()
featureFlags =
FakeFeatureFlags().apply {
set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
@@ -93,6 +98,8 @@
bouncerRepository,
configRepository,
),
+ shadeRepository,
+ dialogManager,
)
underTest =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
index a30e2a6..0fbcec2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
@@ -33,6 +33,8 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -60,8 +62,10 @@
private lateinit var fakeCommandQueue: FakeCommandQueue
private lateinit var featureFlags: FakeFeatureFlags
private lateinit var transitionRepository: FakeKeyguardTransitionRepository
+ private lateinit var shadeRepository: FakeShadeRepository
@Mock private lateinit var burnInHelper: BurnInHelperWrapper
+ @Mock private lateinit var dialogManager: SystemUIDialogManager
@Before
fun setUp() {
@@ -79,35 +83,39 @@
}
bouncerRepository = FakeKeyguardBouncerRepository()
transitionRepository = FakeKeyguardTransitionRepository()
+ shadeRepository = FakeShadeRepository()
val transitionInteractor =
KeyguardTransitionInteractor(
transitionRepository,
testScope.backgroundScope,
)
- val udfpsKeyguardInteractor =
- UdfpsKeyguardInteractor(
+ val keyguardInteractor =
+ KeyguardInteractor(
+ keyguardRepository,
+ fakeCommandQueue,
+ featureFlags,
+ bouncerRepository,
configRepository,
- BurnInInteractor(
- context,
- burnInHelper,
- testScope.backgroundScope,
- configRepository,
- FakeSystemClock(),
- ),
- KeyguardInteractor(
- keyguardRepository,
- fakeCommandQueue,
- featureFlags,
- bouncerRepository,
- configRepository,
- ),
)
underTest =
FingerprintViewModel(
context,
transitionInteractor,
- udfpsKeyguardInteractor,
+ UdfpsKeyguardInteractor(
+ configRepository,
+ BurnInInteractor(
+ context,
+ burnInHelper,
+ testScope.backgroundScope,
+ configRepository,
+ FakeSystemClock(),
+ ),
+ keyguardInteractor,
+ shadeRepository,
+ dialogManager,
+ ),
+ keyguardInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
index d58ceee..41ae931 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
@@ -20,12 +20,27 @@
import androidx.test.filters.SmallTest
import com.android.settingslib.Utils
import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.FakeSystemClock
+import com.android.wm.shell.animation.Interpolators
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -35,6 +50,8 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.MockitoAnnotations
/** Tests UDFPS lockscreen view model transitions. */
@@ -48,26 +65,63 @@
private val alternateBouncerColor =
Utils.getColorAttrDefaultColor(context, alternateBouncerResId)
+ @Mock private lateinit var dialogManager: SystemUIDialogManager
+
private lateinit var underTest: UdfpsLockscreenViewModel
private lateinit var testScope: TestScope
private lateinit var transitionRepository: FakeKeyguardTransitionRepository
+ private lateinit var configRepository: FakeConfigurationRepository
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
+ private lateinit var shadeRepository: FakeShadeRepository
+ private lateinit var featureFlags: FakeFeatureFlags
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
testScope = TestScope()
transitionRepository = FakeKeyguardTransitionRepository()
- val transitionInteractor =
- KeyguardTransitionInteractor(
- transitionRepository,
- testScope.backgroundScope,
+ configRepository = FakeConfigurationRepository()
+ keyguardRepository = FakeKeyguardRepository()
+ bouncerRepository = FakeKeyguardBouncerRepository()
+ shadeRepository = FakeShadeRepository()
+ featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
+ set(Flags.FACE_AUTH_REFACTOR, false)
+ }
+ val keyguardInteractor =
+ KeyguardInteractor(
+ keyguardRepository,
+ commandQueue = mock(),
+ featureFlags,
+ bouncerRepository,
+ configRepository,
)
+
underTest =
UdfpsLockscreenViewModel(
context,
lockscreenColorResId,
alternateBouncerResId,
- transitionInteractor,
+ KeyguardTransitionInteractor(
+ transitionRepository,
+ testScope.backgroundScope,
+ ),
+ UdfpsKeyguardInteractor(
+ configRepository,
+ BurnInInteractor(
+ context,
+ burnInHelperWrapper = mock(),
+ testScope.backgroundScope,
+ configRepository,
+ FakeSystemClock(),
+ ),
+ keyguardInteractor,
+ shadeRepository,
+ dialogManager,
+ ),
+ keyguardInteractor,
)
}
@@ -125,6 +179,7 @@
testScope.runTest {
val transition by collectLastValue(underTest.transition)
val visible by collectLastValue(underTest.visible)
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
// TransitionState.STARTED: lockscreen -> AOD
transitionRepository.sendTransitionStep(
@@ -176,6 +231,56 @@
}
@Test
+ fun lockscreenShadeLockedToAod() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+
+ // TransitionState.STARTED: lockscreen -> AOD
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "lockscreenToAod",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(visible).isFalse()
+
+ // TransitionState.RUNNING: lockscreen -> AOD
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "lockscreenToAod",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(visible).isFalse()
+
+ // TransitionState.FINISHED: lockscreen -> AOD
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "lockscreenToAod",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(visible).isFalse()
+ }
+
+ @Test
fun aodToLockscreen() =
testScope.runTest {
val transition by collectLastValue(underTest.transition)
@@ -235,6 +340,7 @@
testScope.runTest {
val transition by collectLastValue(underTest.transition)
val visible by collectLastValue(underTest.visible)
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
// TransitionState.STARTED: lockscreen -> alternate bouncer
transitionRepository.sendTransitionStep(
@@ -398,6 +504,7 @@
testScope.runTest {
val transition by collectLastValue(underTest.transition)
val visible by collectLastValue(underTest.visible)
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
// TransitionState.STARTED: lockscreen -> occluded
transitionRepository.sendTransitionStep(
@@ -502,4 +609,152 @@
assertThat(transition?.color).isEqualTo(lockscreenColor)
assertThat(visible).isTrue()
}
+
+ @Test
+ fun qsProgressChange() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+ givenTransitionToLockscreenFinished()
+
+ // qsExpansion = 0f
+ shadeRepository.setQsExpansion(0f)
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(visible).isEqualTo(true)
+
+ // qsExpansion = .25
+ shadeRepository.setQsExpansion(.2f)
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(.6f)
+ assertThat(visible).isEqualTo(true)
+
+ // qsExpansion = .5
+ shadeRepository.setQsExpansion(.5f)
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(visible).isEqualTo(false)
+
+ // qsExpansion = 1
+ shadeRepository.setQsExpansion(1f)
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(visible).isEqualTo(false)
+ }
+
+ @Test
+ fun shadeExpansionChanged() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+ givenTransitionToLockscreenFinished()
+
+ // shadeExpansion = 0f
+ shadeRepository.setUdfpsTransitionToFullShadeProgress(0f)
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(visible).isEqualTo(true)
+
+ // shadeExpansion = .2
+ shadeRepository.setUdfpsTransitionToFullShadeProgress(.2f)
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(.8f)
+ assertThat(visible).isEqualTo(true)
+
+ // shadeExpansion = .5
+ shadeRepository.setUdfpsTransitionToFullShadeProgress(.5f)
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(.5f)
+ assertThat(visible).isEqualTo(true)
+
+ // shadeExpansion = 1
+ shadeRepository.setUdfpsTransitionToFullShadeProgress(1f)
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(visible).isEqualTo(false)
+ }
+
+ @Test
+ fun dialogHideAffordancesRequestChanged() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ givenTransitionToLockscreenFinished()
+ runCurrent()
+ val captor = argumentCaptor<SystemUIDialogManager.Listener>()
+ Mockito.verify(dialogManager).registerListener(captor.capture())
+
+ captor.value.shouldHideAffordances(true)
+ assertThat(transition?.alpha).isEqualTo(0f)
+
+ captor.value.shouldHideAffordances(false)
+ assertThat(transition?.alpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun occludedToAlternateBouncer() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+
+ // TransitionState.STARTED: occluded -> alternate bouncer
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "occludedToAlternateBouncer",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(0f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.RUNNING: occluded -> alternate bouncer
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "occludedToAlternateBouncer",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale)
+ .isEqualTo(Interpolators.FAST_OUT_SLOW_IN.getInterpolation(.6f))
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.FINISHED: occluded -> alternate bouncer
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "occludedToAlternateBouncer",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isTrue()
+ }
+
+ private suspend fun givenTransitionToLockscreenFinished() {
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "givenTransitionToLockscreenFinished",
+ )
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/core/FakeLogBuffer.kt b/packages/SystemUI/tests/src/com/android/systemui/log/core/FakeLogBuffer.kt
new file mode 100644
index 0000000..272d686
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/core/FakeLogBuffer.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.core
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogMessageImpl
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import org.mockito.Mockito.anyString
+
+/**
+ * A fake [LogBuffer] used for testing that obtains a real [LogMessage] to prevent a
+ * [NullPointerException].
+ */
+class FakeLogBuffer private constructor() {
+ class Factory private constructor() {
+ companion object {
+ fun create(): LogBuffer {
+ val logBuffer = mock<LogBuffer>()
+ whenever(
+ logBuffer.obtain(
+ tag = anyString(),
+ level = any(),
+ messagePrinter = any(),
+ exception = nullable(),
+ )
+ )
+ .thenReturn(LogMessageImpl.Factory.create())
+ return logBuffer
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index f79c53d..ab24c46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -63,7 +63,6 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -124,7 +123,7 @@
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
mMediaSessionManager, mLocalBluetoothManager, mStarter,
mNotifCollection, mDialogLaunchAnimator,
- Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+ mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext, mBroadcastSender,
mMediaOutputController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
index f8971fd..45e8e27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
@@ -70,7 +70,6 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -126,7 +125,7 @@
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
mMediaSessionManager, mLocalBluetoothManager, mStarter,
mNotifCollection, mDialogLaunchAnimator,
- Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+ mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputBroadcastDialog = new MediaOutputBroadcastDialog(mContext, false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 9f06b5f..708bb55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -93,7 +93,6 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -197,7 +196,7 @@
mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME,
mMediaSessionManager, mLocalBluetoothManager, mStarter,
mNotifCollection, mDialogLaunchAnimator,
- Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+ mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(false);
@@ -279,7 +278,7 @@
mMediaOutputController = new MediaOutputController(mSpyContext, null,
mMediaSessionManager, mLocalBluetoothManager, mStarter,
mNotifCollection, mDialogLaunchAnimator,
- Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+ mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
mMediaOutputController.start(mCb);
@@ -309,7 +308,7 @@
mMediaOutputController = new MediaOutputController(mSpyContext, null,
mMediaSessionManager, mLocalBluetoothManager, mStarter,
mNotifCollection, mDialogLaunchAnimator,
- Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+ mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
mMediaOutputController.start(mCb);
@@ -530,7 +529,7 @@
"",
mMediaSessionManager, mLocalBluetoothManager, mStarter,
mNotifCollection, mDialogLaunchAnimator,
- Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+ mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
testMediaOutputController.start(mCb);
reset(mCb);
@@ -553,7 +552,7 @@
"",
mMediaSessionManager, mLocalBluetoothManager, mStarter,
mNotifCollection, mDialogLaunchAnimator,
- Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+ mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
testMediaOutputController.start(mCb);
reset(mCb);
@@ -589,7 +588,7 @@
null,
mMediaSessionManager, mLocalBluetoothManager, mStarter,
mNotifCollection, mDialogLaunchAnimator,
- Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+ mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
LocalMediaManager testLocalMediaManager = spy(testMediaOutputController.mLocalMediaManager);
@@ -606,7 +605,7 @@
null,
mMediaSessionManager, mLocalBluetoothManager, mStarter,
mNotifCollection, mDialogLaunchAnimator,
- Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+ mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
LocalMediaManager testLocalMediaManager = spy(testMediaOutputController.mLocalMediaManager);
@@ -783,33 +782,20 @@
}
@Test
- public void getActiveRemoteMediaDevice_isSystemSession_returnSession() {
+ public void getActiveRemoteMediaDevices() {
when(mRemoteSessionInfo.getId()).thenReturn(TEST_SESSION_ID);
when(mRemoteSessionInfo.getName()).thenReturn(TEST_SESSION_NAME);
when(mRemoteSessionInfo.getVolumeMax()).thenReturn(100);
when(mRemoteSessionInfo.getVolume()).thenReturn(10);
when(mRemoteSessionInfo.isSystemSession()).thenReturn(false);
mRoutingSessionInfos.add(mRemoteSessionInfo);
- when(mLocalMediaManager.getActiveMediaSession()).thenReturn(mRoutingSessionInfos);
+ when(mLocalMediaManager.getRemoteRoutingSessions()).thenReturn(mRoutingSessionInfos);
assertThat(mMediaOutputController.getActiveRemoteMediaDevices()).containsExactly(
mRemoteSessionInfo);
}
@Test
- public void getActiveRemoteMediaDevice_notSystemSession_returnEmpty() {
- when(mRemoteSessionInfo.getId()).thenReturn(TEST_SESSION_ID);
- when(mRemoteSessionInfo.getName()).thenReturn(TEST_SESSION_NAME);
- when(mRemoteSessionInfo.getVolumeMax()).thenReturn(100);
- when(mRemoteSessionInfo.getVolume()).thenReturn(10);
- when(mRemoteSessionInfo.isSystemSession()).thenReturn(true);
- mRoutingSessionInfos.add(mRemoteSessionInfo);
- when(mLocalMediaManager.getActiveMediaSession()).thenReturn(mRoutingSessionInfos);
-
- assertThat(mMediaOutputController.getActiveRemoteMediaDevices()).isEmpty();
- }
-
- @Test
public void getGroupMediaDevices_differentDeviceOrder_showingSameOrder() {
final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class);
final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class);
@@ -888,7 +874,7 @@
mMediaOutputController = new MediaOutputController(mSpyContext, null,
mMediaSessionManager, mLocalBluetoothManager, mStarter,
mNotifCollection, mDialogLaunchAnimator,
- Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+ mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
assertThat(mMediaOutputController.getNotificationIcon()).isNull();
@@ -1080,7 +1066,7 @@
null,
mMediaSessionManager, mLocalBluetoothManager, mStarter,
mNotifCollection, mDialogLaunchAnimator,
- Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+ mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
testMediaOutputController.setTemporaryAllowListExceptionIfNeeded(mMediaDevice2);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index a14ff2f..3e69a29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -67,7 +67,6 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import java.util.function.Consumer;
@MediumTest
@@ -132,7 +131,7 @@
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
mMediaSessionManager, mLocalBluetoothManager, mStarter,
mNotifCollection, mDialogLaunchAnimator,
- Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+ mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputDialog = makeTestDialog(mMediaOutputController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
index 01ffdcd..ee3b80a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
@@ -26,6 +26,7 @@
import android.view.WindowManager
import android.view.WindowMetrics
import androidx.core.view.WindowInsetsCompat.Type
+import androidx.lifecycle.LifecycleOwner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener
@@ -41,9 +42,10 @@
@SmallTest
class TaskPreviewSizeProviderTest : SysuiTestCase() {
- private val mockContext: Context = mock()
- private val resources: Resources = mock()
- private val windowManager: WindowManager = mock()
+ private val lifecycleOwner = mock<LifecycleOwner>()
+ private val mockContext = mock<Context>()
+ private val resources = mock<Resources>()
+ private val windowManager = mock<WindowManager>()
private val sizeUpdates = arrayListOf<Rect>()
private val testConfigurationController = FakeConfigurationController()
@@ -76,7 +78,7 @@
@Test
fun size_phoneDisplayAndRotate_emitsSizeUpdate() {
givenDisplay(width = 400, height = 600, isTablet = false)
- createSizeProvider()
+ createSizeProvider().also { it.onCreate(lifecycleOwner) }
givenDisplay(width = 600, height = 400, isTablet = false)
testConfigurationController.onConfigurationChanged(Configuration())
@@ -87,7 +89,7 @@
@Test
fun size_phoneDisplayAndRotateConfigurationChange_returnsUpdatedSize() {
givenDisplay(width = 400, height = 600, isTablet = false)
- val sizeProvider = createSizeProvider()
+ val sizeProvider = createSizeProvider().also { it.onCreate(lifecycleOwner) }
givenDisplay(width = 600, height = 400, isTablet = false)
testConfigurationController.onConfigurationChanged(Configuration())
@@ -95,6 +97,20 @@
assertThat(sizeProvider.size).isEqualTo(Rect(0, 0, 150, 100))
}
+ @Test
+ fun size_phoneDisplayAndRotateConfigurationChange_afterChooserDestroyed_doesNotUpdate() {
+ givenDisplay(width = 400, height = 600, isTablet = false)
+ val sizeProvider = createSizeProvider()
+ val previousSize = Rect(sizeProvider.size)
+
+ sizeProvider.onCreate(lifecycleOwner)
+ sizeProvider.onDestroy(lifecycleOwner)
+ givenDisplay(width = 600, height = 400, isTablet = false)
+ testConfigurationController.onConfigurationChanged(Configuration())
+
+ assertThat(sizeProvider.size).isEqualTo(previousSize)
+ }
+
private fun givenTaskbarSize(size: Int) {
val windowInsets =
WindowInsets.Builder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 9188293..ecd3308 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -370,7 +370,8 @@
mScreenOffAnimationController,
mKeyguardLogger,
mFeatureFlags,
- mInteractionJankMonitor));
+ mInteractionJankMonitor,
+ mDumpManager));
when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
when(mHeadsUpCallback.getContext()).thenReturn(mContext);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt
index ed24947..f91e5a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt
@@ -21,7 +21,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
@@ -47,14 +46,11 @@
private lateinit var onBeforeRenderListListener: OnBeforeRenderListListener
private val keyguardStateController: KeyguardStateController = mock()
private val pipeline: NotifPipeline = mock()
- private val flags: NotifPipelineFlags = mock()
private val dumpManager: DumpManager = mock()
@Before
fun setUp() {
- whenever(flags.allowDismissOngoing()).thenReturn(true)
-
- dismissibilityProvider = NotificationDismissibilityProviderImpl(flags, dumpManager)
+ dismissibilityProvider = NotificationDismissibilityProviderImpl(dumpManager)
coordinator = DismissibilityCoordinator(keyguardStateController, dismissibilityProvider)
coordinator.attach(pipeline)
onBeforeRenderListListener = withArgCaptor {
@@ -309,57 +305,4 @@
dismissibilityProvider.isDismissable(summary)
)
}
-
- @Test
- fun testFeatureToggleOffNonDismissibleEntry() {
- whenever(flags.allowDismissOngoing()).thenReturn(false)
- val entry =
- NotificationEntryBuilder()
- .setTag("entry")
- .setFlag(mContext, Notification.FLAG_NO_DISMISS, true)
- .build()
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- assertTrue(
- "FLAG_NO_DISMISS should be ignored, if the feature is off",
- dismissibilityProvider.isDismissable(entry)
- )
- }
-
- @Test
- fun testFeatureToggleOffOngoingNotifWhenPhoneIsLocked() {
- whenever(flags.allowDismissOngoing()).thenReturn(false)
- whenever(keyguardStateController.isUnlocked).thenReturn(false)
- val entry =
- NotificationEntryBuilder()
- .setTag("entry")
- .setFlag(mContext, Notification.FLAG_ONGOING_EVENT, true)
- .build()
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- assertFalse(
- "Ongoing Notifs should NOT be dismissible, if the feature is off",
- dismissibilityProvider.isDismissable(entry)
- )
- }
-
- @Test
- fun testFeatureToggleOffOngoingNotifWhenPhoneIsUnLocked() {
- whenever(flags.allowDismissOngoing()).thenReturn(false)
- whenever(keyguardStateController.isUnlocked).thenReturn(true)
- val entry =
- NotificationEntryBuilder()
- .setTag("entry")
- .setFlag(mContext, Notification.FLAG_ONGOING_EVENT, true)
- .build()
-
- onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
- assertFalse(
- "Ongoing Notifs should NOT be dismissible, if the feature is off",
- dismissibilityProvider.isDismissable(entry)
- )
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 608778e..1dc8453 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -27,6 +27,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -87,6 +88,7 @@
@RunWithLooper
public class ExpandableNotificationRowTest extends SysuiTestCase {
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private NotificationTestHelper mNotificationTestHelper;
@Rule public MockitoRule mockito = MockitoJUnit.rule();
@@ -96,12 +98,10 @@
mNotificationTestHelper = new NotificationTestHelper(
mContext,
mDependency,
- TestableLooper.get(this));
+ TestableLooper.get(this),
+ mFeatureFlags);
mNotificationTestHelper.setDefaultInflationFlags(FLAG_CONTENT_VIEW_ALL);
-
- FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags();
- fakeFeatureFlags.set(Flags.SENSITIVE_REVEAL_ANIM, false);
- mNotificationTestHelper.setFeatureFlags(fakeFeatureFlags);
+ mFeatureFlags.setDefault(Flags.SENSITIVE_REVEAL_ANIM);
}
@Test
@@ -183,6 +183,14 @@
}
@Test
+ public void testSetSensitiveOnNotifRowNotifiesOfHeightChange_withOtherFlagValue()
+ throws Exception {
+ FakeFeatureFlags flags = mFeatureFlags;
+ flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
+ testSetSensitiveOnNotifRowNotifiesOfHeightChange();
+ }
+
+ @Test
public void testSetSensitiveOnNotifRowNotifiesOfHeightChange() throws Exception {
// GIVEN a sensitive notification row that's currently redacted
ExpandableNotificationRow row = mNotificationTestHelper.createRow();
@@ -199,10 +207,19 @@
// WHEN the row is set to no longer be sensitive
row.setSensitive(false, true);
+ boolean expectAnimation = mFeatureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
// VERIFY that the height change listener is invoked
assertThat(row.getShowingLayout()).isSameInstanceAs(row.getPrivateLayout());
assertThat(row.getIntrinsicHeight()).isGreaterThan(0);
- verify(listener).onHeightChanged(eq(row), eq(false));
+ verify(listener).onHeightChanged(eq(row), eq(expectAnimation));
+ }
+
+ @Test
+ public void testSetSensitiveOnGroupRowNotifiesOfHeightChange_withOtherFlagValue()
+ throws Exception {
+ FakeFeatureFlags flags = mFeatureFlags;
+ flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
+ testSetSensitiveOnGroupRowNotifiesOfHeightChange();
}
@Test
@@ -222,10 +239,19 @@
// WHEN the row is set to no longer be sensitive
group.setSensitive(false, true);
+ boolean expectAnimation = mFeatureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
// VERIFY that the height change listener is invoked
assertThat(group.getShowingLayout()).isSameInstanceAs(group.getPrivateLayout());
assertThat(group.getIntrinsicHeight()).isGreaterThan(0);
- verify(listener).onHeightChanged(eq(group), eq(false));
+ verify(listener).onHeightChanged(eq(group), eq(expectAnimation));
+ }
+
+ @Test
+ public void testSetSensitiveOnPublicRowDoesNotNotifyOfHeightChange_withOtherFlagValue()
+ throws Exception {
+ FakeFeatureFlags flags = mFeatureFlags;
+ flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
+ testSetSensitiveOnPublicRowDoesNotNotifyOfHeightChange();
}
@Test
@@ -254,7 +280,7 @@
assertThat(publicRow.getIntrinsicHeight()).isGreaterThan(0);
assertThat(publicRow.getPrivateLayout().getMinHeight())
.isEqualTo(publicRow.getPublicLayout().getMinHeight());
- verify(listener, never()).onHeightChanged(eq(publicRow), eq(false));
+ verify(listener, never()).onHeightChanged(eq(publicRow), anyBoolean());
}
private void measureAndLayout(ExpandableNotificationRow row) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 1a644d3..d21029d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -48,12 +48,16 @@
import android.view.LayoutInflater;
import android.widget.RemoteViews;
+import androidx.annotation.NonNull;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.TestableDependency;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -90,6 +94,7 @@
import org.mockito.ArgumentCaptor;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@@ -130,14 +135,24 @@
private final NotificationDismissibilityProvider mDismissibilityProvider;
public final Runnable mFutureDismissalRunnable;
private @InflationFlag int mDefaultInflationFlags;
- private FeatureFlags mFeatureFlags;
+ private final FakeFeatureFlags mFeatureFlags;
public NotificationTestHelper(
Context context,
TestableDependency dependency,
TestableLooper testLooper) {
+ this(context, dependency, testLooper, new FakeFeatureFlags());
+ }
+
+ public NotificationTestHelper(
+ Context context,
+ TestableDependency dependency,
+ TestableLooper testLooper,
+ @NonNull FakeFeatureFlags featureFlags) {
mContext = context;
mTestLooper = testLooper;
+ mFeatureFlags = Objects.requireNonNull(featureFlags);
+ dependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
dependency.injectMockDependency(NotificationMediaManager.class);
dependency.injectMockDependency(NotificationShadeWindowController.class);
dependency.injectMockDependency(MediaOutputDialogFactory.class);
@@ -183,17 +198,12 @@
mFutureDismissalRunnable = mock(Runnable.class);
when(mOnUserInteractionCallback.registerFutureDismissal(any(), anyInt()))
.thenReturn(mFutureDismissalRunnable);
- mFeatureFlags = mock(FeatureFlags.class);
}
public void setDefaultInflationFlags(@InflationFlag int defaultInflationFlags) {
mDefaultInflationFlags = defaultInflationFlags;
}
- public void setFeatureFlags(FeatureFlags featureFlags) {
- mFeatureFlags = featureFlags;
- }
-
public ExpandableNotificationRowLogger getMockLogger() {
return mMockLogger;
}
@@ -527,6 +537,10 @@
@InflationFlag int extraInflationFlags,
int importance)
throws Exception {
+ // NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be
+ // set, but we do not want to override an existing value that is needed by a specific test.
+ mFeatureFlags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS);
+
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
mContext.LAYOUT_INFLATER_SERVICE);
mRow = (ExpandableNotificationRow) inflater.inflate(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
index 09382ec..3d75288 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
@@ -20,7 +20,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
@@ -42,7 +41,6 @@
private val bypassController = StackScrollAlgorithm.BypassController { false }
private val statusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>()
private val largeScreenShadeInterpolator = mock<LargeScreenShadeInterpolator>()
- private val featureFlags = mock<FeatureFlags>()
private lateinit var sut: AmbientState
@@ -55,8 +53,7 @@
sectionProvider,
bypassController,
statusBarKeyguardViewManager,
- largeScreenShadeInterpolator,
- featureFlags
+ largeScreenShadeInterpolator
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index f38881c..4b145d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -30,7 +30,6 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.controls.ui.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
@@ -65,7 +64,6 @@
@Mock private SectionHeaderController mPeopleHeaderController;
@Mock private SectionHeaderController mAlertingHeaderController;
@Mock private SectionHeaderController mSilentHeaderController;
- @Mock private FeatureFlags mFeatureFlag;
private NotificationSectionsManager mSectionsManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 8d751e3..1dc0ab0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -9,7 +9,9 @@
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
+import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.StatusBarIconView
@@ -20,6 +22,7 @@
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
+import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,22 +37,32 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
-class NotificationShelfTest : SysuiTestCase() {
+open class NotificationShelfTest : SysuiTestCase() {
+
+ open val useShelfRefactor: Boolean = false
+ open val useSensitiveReveal: Boolean = false
+ private val flags = FakeFeatureFlags()
@Mock
private lateinit var largeScreenShadeInterpolator: LargeScreenShadeInterpolator
@Mock
- private lateinit var flags: FeatureFlags
- @Mock
private lateinit var ambientState: AmbientState
@Mock
private lateinit var hostLayoutController: NotificationStackScrollLayoutController
+ @Mock
+ private lateinit var hostLayout: NotificationStackScrollLayout
+ @Mock
+ private lateinit var roundnessManager: NotificationRoundnessManager
private lateinit var shelf: NotificationShelf
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ mDependency.injectTestDependency(FeatureFlags::class.java, flags)
+ flags.set(Flags.NOTIFICATION_SHELF_REFACTOR, useShelfRefactor)
+ flags.set(Flags.SENSITIVE_REVEAL_ANIM, useSensitiveReveal)
+ flags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS)
val root = FrameLayout(context)
shelf = LayoutInflater.from(root.context)
.inflate(/* resource = */ R.layout.status_bar_notification_shelf,
@@ -57,10 +70,13 @@
/* attachToRoot = */false) as NotificationShelf
whenever(ambientState.largeScreenShadeInterpolator).thenReturn(largeScreenShadeInterpolator)
- whenever(ambientState.featureFlags).thenReturn(flags)
whenever(ambientState.isSmallScreen).thenReturn(true)
- shelf.bind(ambientState, /* hostLayoutController */ hostLayoutController)
+ if (useShelfRefactor) {
+ shelf.bind(ambientState, hostLayout, roundnessManager)
+ } else {
+ shelf.bind(ambientState, hostLayoutController)
+ }
shelf.layout(/* left */ 0, /* top */ 0, /* right */ 30, /* bottom */5)
}
@@ -345,7 +361,7 @@
@Test
fun updateState_withNullLastVisibleBackgroundChild_hideShelf() {
// GIVEN
- shelf.setSensitiveRevealAnimEnabled(true)
+ assumeTrue(useSensitiveReveal)
whenever(ambientState.stackY).thenReturn(100f)
whenever(ambientState.stackHeight).thenReturn(100f)
val paddingBetweenElements =
@@ -372,7 +388,7 @@
@Test
fun updateState_withNullFirstViewInShelf_hideShelf() {
// GIVEN
- shelf.setSensitiveRevealAnimEnabled(true)
+ assumeTrue(useSensitiveReveal)
whenever(ambientState.stackY).thenReturn(100f)
whenever(ambientState.stackHeight).thenReturn(100f)
val paddingBetweenElements =
@@ -399,7 +415,7 @@
@Test
fun updateState_withCollapsedShade_hideShelf() {
// GIVEN
- shelf.setSensitiveRevealAnimEnabled(true)
+ assumeTrue(useSensitiveReveal)
whenever(ambientState.stackY).thenReturn(100f)
whenever(ambientState.stackHeight).thenReturn(100f)
val paddingBetweenElements =
@@ -426,7 +442,7 @@
@Test
fun updateState_withHiddenSectionBeforeShelf_hideShelf() {
// GIVEN
- shelf.setSensitiveRevealAnimEnabled(true)
+ assumeTrue(useSensitiveReveal)
whenever(ambientState.stackY).thenReturn(100f)
whenever(ambientState.stackHeight).thenReturn(100f)
val paddingBetweenElements =
@@ -486,3 +502,25 @@
assertEquals(expectedAlpha, shelf.viewState.alpha)
}
}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationShelfWithRefactorTest : NotificationShelfTest() {
+ override val useShelfRefactor: Boolean = true
+}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationShelfWithSensitiveRevealTest : NotificationShelfTest() {
+ override val useSensitiveReveal: Boolean = true
+}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationShelfWithBothFlagsTest : NotificationShelfTest() {
+ override val useShelfRefactor: Boolean = true
+ override val useSensitiveReveal: Boolean = true
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index ee8325e..07eadf7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -54,7 +54,6 @@
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
@@ -119,6 +118,7 @@
@RunWith(AndroidTestingRunner.class)
public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock private NotificationGutsManager mNotificationGutsManager;
@Mock private NotificationsController mNotificationsController;
@Mock private NotificationVisibilityProvider mVisibilityProvider;
@@ -157,7 +157,6 @@
@Mock private StackStateLogger mStackLogger;
@Mock private NotificationStackScrollLogger mLogger;
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
- private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock private NotificationTargetsHelper mNotificationTargetsHelper;
@Mock private SecureSettings mSecureSettings;
@Mock private NotificationIconAreaController mIconAreaController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 8ad271b..72fcdec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -68,7 +68,9 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.EmptyShadeView;
@@ -106,6 +108,7 @@
@TestableLooper.RunWithLooper
public class NotificationStackScrollLayoutTest extends SysuiTestCase {
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private NotificationStackScrollLayout mStackScroller; // Normally test this
private NotificationStackScrollLayout mStackScrollerInternal; // See explanation below
private AmbientState mAmbientState;
@@ -129,7 +132,6 @@
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
- @Mock private FeatureFlags mFeatureFlags;
@Before
public void setUp() throws Exception {
@@ -143,11 +145,25 @@
mNotificationSectionsManager,
mBypassController,
mStatusBarKeyguardViewManager,
- mLargeScreenShadeInterpolator,
- mFeatureFlags
+ mLargeScreenShadeInterpolator
));
+ // Register the debug flags we use
+ assertFalse(Flags.NSSL_DEBUG_LINES.getDefault());
+ assertFalse(Flags.NSSL_DEBUG_REMOVE_ANIMATION.getDefault());
+ mFeatureFlags.set(Flags.NSSL_DEBUG_LINES, false);
+ mFeatureFlags.set(Flags.NSSL_DEBUG_REMOVE_ANIMATION, false);
+
+ // Register the feature flags we use
+ // TODO: Ideally we wouldn't need to set these unless a test actually reads them,
+ // and then we would test both configurations, but currently they are all read
+ // in the constructor.
+ mFeatureFlags.setDefault(Flags.SENSITIVE_REVEAL_ANIM);
+ mFeatureFlags.setDefault(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
+ mFeatureFlags.setDefault(Flags.NOTIFICATION_SHELF_REFACTOR);
+
// Inject dependencies before initializing the layout
+ mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
mDependency.injectTestDependency(SysuiStatusBarStateController.class, mBarState);
mDependency.injectMockDependency(ShadeController.class);
mDependency.injectTestDependency(
@@ -176,13 +192,18 @@
mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper,
mNotificationStackSizeCalculator);
mStackScroller = spy(mStackScrollerInternal);
- mStackScroller.setShelfController(notificationShelfController);
+ if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ mStackScroller.setShelfController(notificationShelfController);
+ }
mStackScroller.setNotificationsController(mNotificationsController);
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(true);
when(mStackScrollLayoutController.getNotificationRoundnessManager())
.thenReturn(mNotificationRoundnessManager);
mStackScroller.setController(mStackScrollLayoutController);
+ if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ mStackScroller.setShelf(mNotificationShelf);
+ }
doNothing().when(mGroupExpansionManager).collapseGroups();
doNothing().when(mExpandHelper).cancelImmediately();
@@ -899,7 +920,6 @@
@Test
public void testWindowInsetAnimationProgress_updatesBottomInset() {
int bottomImeInset = 100;
- mStackScrollerInternal.setAnimatedInsetsEnabled(true);
WindowInsets windowInsets = new WindowInsets.Builder()
.setInsets(ime(), Insets.of(0, 0, 0, bottomImeInset)).build();
ArrayList<WindowInsetsAnimation> windowInsetsAnimations = new ArrayList<>();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index df65c09..85a2bdd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -47,6 +47,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
@@ -83,7 +84,7 @@
private Handler mHandler;
private ExpandableNotificationRow mNotificationRow;
private Runnable mFalsingCheck;
- private FeatureFlags mFeatureFlags;
+ private final FeatureFlags mFeatureFlags = new FakeFeatureFlags();
private static final int FAKE_ROW_WIDTH = 20;
private static final int FAKE_ROW_HEIGHT = 20;
@@ -96,7 +97,6 @@
mCallback = mock(NotificationSwipeHelper.NotificationCallback.class);
mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class);
mNotificationRoundnessManager = mock(NotificationRoundnessManager.class);
- mFeatureFlags = mock(FeatureFlags.class);
mSwipeHelper = spy(new NotificationSwipeHelper(
mContext.getResources(),
ViewConfiguration.get(mContext),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
index 45725ce..e30947c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
@@ -18,6 +18,7 @@
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class NotificationTargetsHelperTest : SysuiTestCase() {
+ private val featureFlags = FakeFeatureFlags()
lateinit var notificationTestHelper: NotificationTestHelper
private val sectionsManager: NotificationSectionsManager = mock()
private val stackScrollLayout: NotificationStackScrollLayout = mock()
@@ -26,10 +27,10 @@
fun setUp() {
allowTestableLooperAsMainThread()
notificationTestHelper =
- NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
+ NotificationTestHelper(mContext, mDependency, TestableLooper.get(this), featureFlags)
}
- private fun notificationTargetsHelper() = NotificationTargetsHelper(FakeFeatureFlags())
+ private fun notificationTargetsHelper() = NotificationTargetsHelper(featureFlags)
@Test
fun targetsForFirstNotificationInGroup() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 4c97d20..987861d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -8,7 +8,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation.getContentAlpha
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.EmptyShadeView
import com.android.systemui.statusbar.NotificationShelf
@@ -45,7 +44,6 @@
private val dumpManager = mock<DumpManager>()
private val mStatusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>()
private val notificationShelf = mock<NotificationShelf>()
- private val featureFlags = mock<FeatureFlags>()
private val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply {
layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
}
@@ -56,7 +54,6 @@
/* bypassController */ { false },
mStatusBarKeyguardViewManager,
largeScreenShadeInterpolator,
- featureFlags,
)
private val testableResources = mContext.getOrCreateTestableResources()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
index 85fbef0..9795b9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
@@ -18,6 +18,7 @@
import android.app.AlarmManager
import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyResourcesManager
import android.content.SharedPreferences
import android.os.UserManager
import android.telecom.TelecomManager
@@ -49,6 +50,7 @@
import com.android.systemui.util.RingerModeTracker
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.kotlin.JavaAdapter
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.time.DateFormatUtil
import com.android.systemui.util.time.FakeSystemClock
@@ -67,6 +69,7 @@
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyString
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.never
@@ -83,6 +86,7 @@
companion object {
private const val ALARM_SLOT = "alarm"
private const val CONNECTED_DISPLAY_SLOT = "connected_display"
+ private const val MANAGED_PROFILE_SLOT = "managed_profile"
}
@Mock private lateinit var iconController: StatusBarIconController
@@ -104,6 +108,7 @@
@Mock private lateinit var userManager: UserManager
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
+ @Mock private lateinit var devicePolicyManagerResources: DevicePolicyResourcesManager
@Mock private lateinit var recordingController: RecordingController
@Mock private lateinit var telecomManager: TelecomManager
@Mock private lateinit var sharedPreferences: SharedPreferences
@@ -132,6 +137,12 @@
com.android.internal.R.string.status_bar_alarm_clock,
ALARM_SLOT
)
+ context.orCreateTestableResources.addOverride(
+ com.android.internal.R.string.status_bar_managed_profile,
+ MANAGED_PROFILE_SLOT
+ )
+ whenever(devicePolicyManager.resources).thenReturn(devicePolicyManagerResources)
+ whenever(devicePolicyManagerResources.getString(anyString(), any())).thenReturn("")
statusBarPolicy = createStatusBarPolicy()
}
@@ -182,6 +193,32 @@
}
@Test
+ fun testAppTransitionFinished_doesNotShowManagedProfileIcon() {
+ whenever(userManager.getUserStatusBarIconResId(anyInt())).thenReturn(0 /* ID_NULL */)
+ whenever(keyguardStateController.isShowing).thenReturn(false)
+ statusBarPolicy.appTransitionFinished(0)
+ // The above call posts to bgExecutor and then back to mainExecutor
+ executor.advanceClockToLast()
+ executor.runAllReady()
+ executor.advanceClockToLast()
+ executor.runAllReady()
+ verify(iconController, never()).setIconVisibility(MANAGED_PROFILE_SLOT, true)
+ }
+
+ @Test
+ fun testAppTransitionFinished_showsManagedProfileIcon() {
+ whenever(userManager.getUserStatusBarIconResId(anyInt())).thenReturn(100)
+ whenever(keyguardStateController.isShowing).thenReturn(false)
+ statusBarPolicy.appTransitionFinished(0)
+ // The above call posts to bgExecutor and then back to mainExecutor
+ executor.advanceClockToLast()
+ executor.runAllReady()
+ executor.advanceClockToLast()
+ executor.runAllReady()
+ verify(iconController).setIconVisibility(MANAGED_PROFILE_SLOT, true)
+ }
+
+ @Test
fun connectedDisplay_connected_iconShown() =
testScope.runTest {
statusBarPolicy.init()
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 9157cd9..085ec27 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
@@ -15,7 +15,6 @@
package com.android.systemui.statusbar.phone;
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON;
-import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE;
import static junit.framework.Assert.assertTrue;
@@ -39,14 +38,13 @@
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
@@ -57,23 +55,27 @@
import org.junit.runner.RunWith;
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
@SmallTest
public class StatusBarIconControllerTest extends LeakCheckedTest {
private MobileContextProvider mMobileContextProvider = mock(MobileContextProvider.class);
+ private MobileUiAdapter mMobileUiAdapter = mock(MobileUiAdapter.class);
+ private MobileIconsViewModel mMobileIconsViewModel = mock(MobileIconsViewModel.class);
@Before
public void setup() {
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
// For testing, ignore context overrides
when(mMobileContextProvider.getMobileContextForSub(anyInt(), any())).thenReturn(mContext);
+ when(mMobileUiAdapter.getMobileIconsViewModel()).thenReturn(mMobileIconsViewModel);
}
@Test
public void testSetCalledOnAdd_IconManager() {
LinearLayout layout = new LinearLayout(mContext);
- TestIconManager manager = new TestIconManager(layout, mMobileContextProvider);
+ TestIconManager manager =
+ new TestIconManager(layout, mMobileUiAdapter, mMobileContextProvider);
testCallOnAdd_forManager(manager);
}
@@ -83,9 +85,8 @@
TestDarkIconManager manager = new TestDarkIconManager(
layout,
StatusBarLocation.HOME,
- mock(StatusBarPipelineFlags.class),
mock(WifiUiAdapter.class),
- mock(MobileUiAdapter.class),
+ mMobileUiAdapter,
mMobileContextProvider,
mock(DarkIconDispatcher.class));
testCallOnAdd_forManager(manager);
@@ -153,15 +154,10 @@
assertTrue("Expected StatusBarIconView",
(manager.getViewAt(0) instanceof StatusBarIconView));
- holder = holderForType(TYPE_MOBILE);
- manager.onIconAdded(1, "test_mobile", false, holder);
- assertTrue(manager.getViewAt(1) instanceof StatusBarMobileView);
}
private StatusBarIconHolder holderForType(int type) {
switch (type) {
- case TYPE_MOBILE:
- return StatusBarIconHolder.fromMobileIconState(mock(MobileIconState.class));
case TYPE_ICON:
default:
@@ -175,14 +171,12 @@
TestDarkIconManager(
LinearLayout group,
StatusBarLocation location,
- StatusBarPipelineFlags statusBarPipelineFlags,
WifiUiAdapter wifiUiAdapter,
MobileUiAdapter mobileUiAdapter,
MobileContextProvider contextProvider,
DarkIconDispatcher darkIconDispatcher) {
super(group,
location,
- statusBarPipelineFlags,
wifiUiAdapter,
mobileUiAdapter,
contextProvider,
@@ -202,23 +196,18 @@
return mock;
}
-
- @Override
- protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) {
- StatusBarMobileView mock = mock(StatusBarMobileView.class);
- mGroup.addView(mock, index);
-
- return mock;
- }
}
private static class TestIconManager extends IconManager implements TestableIconManager {
- TestIconManager(ViewGroup group, MobileContextProvider contextProvider) {
+ TestIconManager(
+ ViewGroup group,
+ MobileUiAdapter adapter,
+ MobileContextProvider contextProvider
+ ) {
super(group,
StatusBarLocation.HOME,
- mock(StatusBarPipelineFlags.class),
mock(WifiUiAdapter.class),
- mock(MobileUiAdapter.class),
+ adapter,
contextProvider);
}
@@ -235,14 +224,6 @@
return mock;
}
-
- @Override
- protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) {
- StatusBarMobileView mock = mock(StatusBarMobileView.class);
- mGroup.addView(mock, index);
-
- return mock;
- }
}
private interface TestableIconManager {
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 9c7f619..33144f2 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
@@ -64,7 +64,7 @@
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.classifier.FalsingCollectorFake;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
@@ -117,6 +117,7 @@
public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
private static final int DISPLAY_ID = 0;
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock
private AssistManager mAssistManager;
@@ -256,7 +257,7 @@
notificationAnimationProvider,
mock(LaunchFullScreenIntentProvider.class),
mPowerInteractor,
- mock(FeatureFlags.class),
+ mFeatureFlags,
mUserTracker
);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index cd8aaa2..9c52788 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -79,7 +79,6 @@
private CommandQueue mCommandQueue;
private FakeMetricsLogger mMetricsLogger;
private final ShadeController mShadeController = mock(ShadeController.class);
- private final CentralSurfaces mCentralSurfaces = mock(CentralSurfaces.class);
private final NotificationsInteractor mNotificationsInteractor =
mock(NotificationsInteractor.class);
private final KeyguardStateController mKeyguardStateController =
@@ -118,7 +117,6 @@
mock(NotificationShadeWindowController.class),
mock(DynamicPrivacyController.class),
mKeyguardStateController,
- mCentralSurfaces,
mNotificationsInteractor,
mock(LockscreenShadeTransitionController.class),
mock(PowerInteractor.class),
@@ -202,7 +200,6 @@
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isOccluded()).thenReturn(false);
- when(mCentralSurfaces.isOccluded()).thenReturn(false);
assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt
index 4aa48d6..755aaa6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt
@@ -54,7 +54,7 @@
@Test
fun collectionStarted_dumpHasInfo() {
val view = TextView(context)
- val viewModel = QsMobileIconViewModel(commonViewModel, flags)
+ val viewModel = QsMobileIconViewModel(commonViewModel)
underTest.logCollectionStarted(view, viewModel)
@@ -66,8 +66,8 @@
fun collectionStarted_multipleViews_dumpHasInfo() {
val view = TextView(context)
val view2 = TextView(context)
- val viewModel = QsMobileIconViewModel(commonViewModel, flags)
- val viewModel2 = KeyguardMobileIconViewModel(commonViewModel, flags)
+ val viewModel = QsMobileIconViewModel(commonViewModel)
+ val viewModel2 = KeyguardMobileIconViewModel(commonViewModel)
underTest.logCollectionStarted(view, viewModel)
underTest.logCollectionStarted(view2, viewModel2)
@@ -81,8 +81,8 @@
fun collectionStopped_dumpHasInfo() {
val view = TextView(context)
val view2 = TextView(context)
- val viewModel = QsMobileIconViewModel(commonViewModel, flags)
- val viewModel2 = KeyguardMobileIconViewModel(commonViewModel, flags)
+ val viewModel = QsMobileIconViewModel(commonViewModel)
+ val viewModel2 = KeyguardMobileIconViewModel(commonViewModel)
underTest.logCollectionStarted(view, viewModel)
underTest.logCollectionStarted(view2, viewModel2)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
index 7420db2..59fc0ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
@@ -235,7 +235,6 @@
@Test
fun onDarkChanged_iconHasNewColor() {
- whenever(statusBarPipelineFlags.useDebugColoring()).thenReturn(false)
val view =
ModernStatusBarMobileView.constructAndBind(
context,
@@ -257,7 +256,6 @@
@Test
fun setStaticDrawableColor_iconHasNewColor() {
- whenever(statusBarPipelineFlags.useDebugColoring()).thenReturn(false)
val view =
ModernStatusBarMobileView.constructAndBind(
context,
@@ -298,7 +296,7 @@
constants,
testScope.backgroundScope,
)
- viewModel = QsMobileIconViewModel(viewModelCommon, statusBarPipelineFlags)
+ viewModel = QsMobileIconViewModel(viewModelCommon)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
index d5fb577..e59d90f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
@@ -86,9 +86,9 @@
testScope.backgroundScope,
)
- homeIcon = HomeMobileIconViewModel(commonImpl, statusBarPipelineFlags, mock())
- qsIcon = QsMobileIconViewModel(commonImpl, statusBarPipelineFlags)
- keyguardIcon = KeyguardMobileIconViewModel(commonImpl, statusBarPipelineFlags)
+ homeIcon = HomeMobileIconViewModel(commonImpl, mock())
+ qsIcon = QsMobileIconViewModel(commonImpl)
+ keyguardIcon = KeyguardMobileIconViewModel(commonImpl)
}
@Test
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 0d51af2..3f49935 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
@@ -31,7 +31,6 @@
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
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
@@ -46,7 +45,6 @@
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel.Companion.viewModelForLocation
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -64,7 +62,6 @@
private lateinit var testableLooper: TestableLooper
- @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock private lateinit var tableLogBuffer: TableLogBuffer
@Mock private lateinit var connectivityConstants: ConnectivityConstants
@Mock private lateinit var wifiConstants: WifiConstants
@@ -110,7 +107,6 @@
viewModel =
viewModelForLocation(
viewModelCommon,
- statusBarPipelineFlags,
StatusBarLocation.HOME,
)
}
@@ -199,7 +195,6 @@
@Test
fun onDarkChanged_iconHasNewColor() {
- whenever(statusBarPipelineFlags.useDebugColoring()).thenReturn(false)
val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
ViewUtils.attachView(view)
testableLooper.processAllMessages()
@@ -215,7 +210,6 @@
@Test
fun setStaticDrawableColor_iconHasNewColor() {
- whenever(statusBarPipelineFlags.useDebugColoring()).thenReturn(false)
val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
ViewUtils.attachView(view)
testableLooper.processAllMessages()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 0e303b2..cb469ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -20,7 +20,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableLogBuffer
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
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
@@ -58,7 +57,6 @@
private lateinit var underTest: WifiViewModel
- @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock private lateinit var tableLogBuffer: TableLogBuffer
@Mock private lateinit var connectivityConstants: ConnectivityConstants
@Mock private lateinit var wifiConstants: WifiConstants
@@ -107,11 +105,9 @@
@Test
fun wifiIcon_allLocationViewModelsReceiveSameData() =
runBlocking(IMMEDIATE) {
- val home =
- viewModelForLocation(underTest, statusBarPipelineFlags, StatusBarLocation.HOME)
- val keyguard =
- viewModelForLocation(underTest, statusBarPipelineFlags, StatusBarLocation.KEYGUARD)
- val qs = viewModelForLocation(underTest, statusBarPipelineFlags, StatusBarLocation.QS)
+ val home = viewModelForLocation(underTest, StatusBarLocation.HOME)
+ val keyguard = viewModelForLocation(underTest, StatusBarLocation.KEYGUARD)
+ val qs = viewModelForLocation(underTest, StatusBarLocation.QS)
var latestHome: WifiIcon? = null
val jobHome = home.wifiIcon.onEach { latestHome = it }.launchIn(this)
@@ -249,11 +245,9 @@
createAndSetViewModel()
wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
- val home =
- viewModelForLocation(underTest, statusBarPipelineFlags, StatusBarLocation.HOME)
- val keyguard =
- viewModelForLocation(underTest, statusBarPipelineFlags, StatusBarLocation.KEYGUARD)
- val qs = viewModelForLocation(underTest, statusBarPipelineFlags, StatusBarLocation.QS)
+ val home = viewModelForLocation(underTest, StatusBarLocation.HOME)
+ val keyguard = viewModelForLocation(underTest, StatusBarLocation.KEYGUARD)
+ val qs = viewModelForLocation(underTest, StatusBarLocation.QS)
var latestHome: Boolean? = null
val jobHome = home.isActivityInViewVisible.onEach { latestHome = it }.launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 391c8ca..7c285b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -102,6 +102,8 @@
"com.android.sysuitest.dummynotificationsender";
private static final int DUMMY_MESSAGE_APP_ID = Process.LAST_APPLICATION_UID - 1;
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
+
@Mock private RemoteInputController mController;
@Mock private ShortcutManager mShortcutManager;
@Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
@@ -453,8 +455,7 @@
private RemoteInputViewController bindController(
RemoteInputView view,
NotificationEntry entry) {
- FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags();
- fakeFeatureFlags.set(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION, true);
+ mFeatureFlags.set(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION, true);
RemoteInputViewControllerImpl viewController = new RemoteInputViewControllerImpl(
view,
entry,
@@ -462,7 +463,7 @@
mController,
mShortcutManager,
mUiEventLoggerFake,
- fakeFeatureFlags
+ mFeatureFlags
);
viewController.bind();
return viewController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
index d8e418a..b13cb72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
@@ -26,6 +26,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.eq
+import com.android.systemui.wallpapers.data.repository.FakeWallpaperRepository
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -56,6 +57,7 @@
private lateinit var viewRootImpl: ViewRootImpl
@Mock
private lateinit var windowToken: IBinder
+ private val wallpaperRepository = FakeWallpaperRepository()
@JvmField
@Rule
@@ -69,7 +71,7 @@
`when`(root.windowToken).thenReturn(windowToken)
`when`(root.isAttachedToWindow).thenReturn(true)
- wallaperController = WallpaperController(wallpaperManager)
+ wallaperController = WallpaperController(wallpaperManager, wallpaperRepository)
wallaperController.rootView = root
}
@@ -90,9 +92,9 @@
@Test
fun setUnfoldTransitionZoom_defaultUnfoldTransitionIsDisabled_doesNotUpdateWallpaperZoom() {
- wallaperController.onWallpaperInfoUpdated(createWallpaperInfo(
+ wallpaperRepository.wallpaperInfo.value = createWallpaperInfo(
useDefaultTransition = false
- ))
+ )
wallaperController.setUnfoldTransitionZoom(0.5f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
index 6fc36b0..fe5024f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
@@ -16,9 +16,11 @@
package com.android.systemui.wallpapers.data.repository
+import android.app.WallpaperInfo
import kotlinx.coroutines.flow.MutableStateFlow
/** Fake implementation of the wallpaper repository. */
class FakeWallpaperRepository : WallpaperRepository {
+ override val wallpaperInfo = MutableStateFlow<WallpaperInfo?>(null)
override val wallpaperSupportsAmbientMode = MutableStateFlow(false)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
index 132b9b4..f8b096a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
@@ -65,6 +65,171 @@
}
@Test
+ fun wallpaperInfo_nullInfo() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wallpaperInfo)
+
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(null)
+
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_WALLPAPER_CHANGED),
+ )
+
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun wallpaperInfo_hasInfoFromManager() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wallpaperInfo)
+
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(UNSUPPORTED_WP)
+
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_WALLPAPER_CHANGED),
+ )
+
+ assertThat(latest).isEqualTo(UNSUPPORTED_WP)
+ }
+
+ @Test
+ fun wallpaperInfo_initialValueIsFetched() =
+ testScope.runTest {
+ whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_SUPPORTED_WP.id))
+ .thenReturn(SUPPORTED_WP)
+ userRepository.setUserInfos(listOf(USER_WITH_SUPPORTED_WP))
+ userRepository.setSelectedUserInfo(USER_WITH_SUPPORTED_WP)
+
+ // WHEN the repo initially starts up (underTest is lazy), then it fetches the current
+ // value for the wallpaper
+ assertThat(underTest.wallpaperInfo.value).isEqualTo(SUPPORTED_WP)
+ }
+
+ @Test
+ fun wallpaperInfo_updatesOnUserChanged() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wallpaperInfo)
+
+ val user3 = UserInfo(/* id= */ 3, /* name= */ "user3", /* flags= */ 0)
+ val user3Wp = mock<WallpaperInfo>()
+ whenever(wallpaperManager.getWallpaperInfoForUser(user3.id)).thenReturn(user3Wp)
+
+ val user4 = UserInfo(/* id= */ 4, /* name= */ "user4", /* flags= */ 0)
+ val user4Wp = mock<WallpaperInfo>()
+ whenever(wallpaperManager.getWallpaperInfoForUser(user4.id)).thenReturn(user4Wp)
+
+ userRepository.setUserInfos(listOf(user3, user4))
+
+ // WHEN user3 is selected
+ userRepository.setSelectedUserInfo(user3)
+
+ // THEN user3's wallpaper is used
+ assertThat(latest).isEqualTo(user3Wp)
+
+ // WHEN the user is switched to user4
+ userRepository.setSelectedUserInfo(user4)
+
+ // THEN user4's wallpaper is used
+ assertThat(latest).isEqualTo(user4Wp)
+ }
+
+ @Test
+ fun wallpaperInfo_doesNotUpdateOnUserChanging() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wallpaperInfo)
+
+ val user3 = UserInfo(/* id= */ 3, /* name= */ "user3", /* flags= */ 0)
+ val user3Wp = mock<WallpaperInfo>()
+ whenever(wallpaperManager.getWallpaperInfoForUser(user3.id)).thenReturn(user3Wp)
+
+ val user4 = UserInfo(/* id= */ 4, /* name= */ "user4", /* flags= */ 0)
+ val user4Wp = mock<WallpaperInfo>()
+ whenever(wallpaperManager.getWallpaperInfoForUser(user4.id)).thenReturn(user4Wp)
+
+ userRepository.setUserInfos(listOf(user3, user4))
+
+ // WHEN user3 is selected
+ userRepository.setSelectedUserInfo(user3)
+
+ // THEN user3's wallpaper is used
+ assertThat(latest).isEqualTo(user3Wp)
+
+ // WHEN the user has started switching to user4 but hasn't finished yet
+ userRepository.selectedUser.value =
+ SelectedUserModel(user4, SelectionStatus.SELECTION_IN_PROGRESS)
+
+ // THEN the wallpaper still matches user3
+ assertThat(latest).isEqualTo(user3Wp)
+ }
+
+ @Test
+ fun wallpaperInfo_updatesOnIntent() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wallpaperInfo)
+
+ val wp1 = mock<WallpaperInfo>()
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(wp1)
+
+ assertThat(latest).isEqualTo(wp1)
+
+ // WHEN the info is new and a broadcast is sent
+ val wp2 = mock<WallpaperInfo>()
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(wp2)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_WALLPAPER_CHANGED),
+ )
+
+ // THEN the flow updates
+ assertThat(latest).isEqualTo(wp2)
+ }
+
+ @Test
+ fun wallpaperInfo_wallpaperNotSupported_alwaysNull() =
+ testScope.runTest {
+ whenever(wallpaperManager.isWallpaperSupported).thenReturn(false)
+
+ val latest by collectLastValue(underTest.wallpaperInfo)
+ assertThat(latest).isNull()
+
+ // Even WHEN there *is* current wallpaper
+ val wp1 = mock<WallpaperInfo>()
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(wp1)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_WALLPAPER_CHANGED),
+ )
+
+ // THEN the value is still null because wallpaper isn't supported
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun wallpaperInfo_deviceDoesNotSupportAmbientWallpaper_alwaysFalse() =
+ testScope.runTest {
+ context.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_dozeSupportsAodWallpaper,
+ false
+ )
+
+ val latest by collectLastValue(underTest.wallpaperInfo)
+ assertThat(latest).isNull()
+
+ // Even WHEN there *is* current wallpaper
+ val wp1 = mock<WallpaperInfo>()
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(wp1)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_WALLPAPER_CHANGED),
+ )
+
+ // THEN the value is still null because wallpaper isn't supported
+ assertThat(latest).isNull()
+ }
+
+ @Test
fun wallpaperSupportsAmbientMode_nullInfo_false() =
testScope.runTest {
val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
@@ -190,14 +355,12 @@
testScope.runTest {
val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
- val info: WallpaperInfo = mock()
- whenever(info.supportsAmbientMode()).thenReturn(false)
- whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(info)
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(UNSUPPORTED_WP)
assertThat(latest).isFalse()
// WHEN the info now supports ambient mode and a broadcast is sent
- whenever(info.supportsAmbientMode()).thenReturn(true)
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(SUPPORTED_WP)
fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
context,
Intent(Intent.ACTION_WALLPAPER_CHANGED),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 4839eeb..820e2a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -92,7 +92,7 @@
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -305,6 +305,7 @@
private TestableLooper mTestableLooper;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private UserHandle mUser0;
@@ -423,7 +424,7 @@
mCommonNotifCollection,
mNotifPipeline,
mSysUiState,
- mock(FeatureFlags.class),
+ mFeatureFlags,
mNotifPipelineFlags,
syncExecutor);
mBubblesManager.addNotifCallback(mNotifCallback);
@@ -432,7 +433,8 @@
mNotificationTestHelper = new NotificationTestHelper(
mContext,
mDependency,
- TestableLooper.get(this));
+ TestableLooper.get(this),
+ mFeatureFlags);
mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
mNonBubbleNotifRow = mNotificationTestHelper.createRow();
@@ -1844,8 +1846,7 @@
}
@Test
- public void testCreateBubbleFromOngoingNotification_OngoingDismissalEnabled() {
- when(mNotifPipelineFlags.allowDismissOngoing()).thenReturn(true);
+ public void testCreateBubbleFromOngoingNotification() {
NotificationEntry notif = new NotificationEntryBuilder()
.setFlag(mContext, Notification.FLAG_ONGOING_EVENT, true)
.setCanBubble(true)
@@ -1858,8 +1859,7 @@
@Test
- public void testCreateBubbleFromNoDismissNotification_OngoingDismissalEnabled() {
- when(mNotifPipelineFlags.allowDismissOngoing()).thenReturn(true);
+ public void testCreateBubbleFromNoDismissNotification() {
NotificationEntry notif = new NotificationEntryBuilder()
.setFlag(mContext, Notification.FLAG_NO_DISMISS, true)
.setCanBubble(true)
@@ -1871,37 +1871,6 @@
}
@Test
- public void testCreateBubbleFromOngoingNotification_OngoingDismissalDisabled() {
- NotificationEntry notif = new NotificationEntryBuilder()
- .setFlag(mContext, Notification.FLAG_ONGOING_EVENT, true)
- .setCanBubble(true)
- .build();
-
- BubbleEntry bubble = mBubblesManager.notifToBubbleEntry(notif);
-
- assertFalse(
- "Ongoing Notifis should be dismissable, if the feature is off",
- bubble.isDismissable()
- );
- }
-
-
- @Test
- public void testCreateBubbleFromNoDismissNotification_OngoingDismissalDisabled() {
- NotificationEntry notif = new NotificationEntryBuilder()
- .setFlag(mContext, Notification.FLAG_NO_DISMISS, true)
- .setCanBubble(true)
- .build();
-
- BubbleEntry bubble = mBubblesManager.notifToBubbleEntry(notif);
-
- assertTrue(
- "FLAG_NO_DISMISS should be ignored, if the feature is off",
- bubble.isDismissable()
- );
- }
-
- @Test
public void registerBubbleBarListener_barDisabled_largeScreen_shouldBeIgnored() {
mBubbleProperties.mIsBubbleBarEnabled = false;
mPositioner.setIsLargeScreen(true);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
index b94f816e..36fa7e6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -62,6 +62,32 @@
}
}
+ /**
+ * Set the given flag's default value if no other value has been set.
+ *
+ * REMINDER: You should always test your code with your flag in both configurations, so
+ * generally you should be setting a particular value. This method should be reserved for
+ * situations where the flag needs to be read (e.g. in the class constructor), but its
+ * value shouldn't affect the actual test cases. In those cases, it's mildly safer to use
+ * this method than to hard-code `false` or `true` because then at least if you're wrong,
+ * and the flag value *does* matter, you'll notice when the flag is flipped and tests
+ * start failing.
+ */
+ fun setDefault(flag: BooleanFlag) = booleanFlags.putIfAbsent(flag.id, flag.default)
+
+ /**
+ * Set the given flag's default value if no other value has been set.
+ *
+ * REMINDER: You should always test your code with your flag in both configurations, so
+ * generally you should be setting a particular value. This method should be reserved for
+ * situations where the flag needs to be read (e.g. in the class constructor), but its
+ * value shouldn't affect the actual test cases. In those cases, it's mildly safer to use
+ * this method than to hard-code `false` or `true` because then at least if you're wrong,
+ * and the flag value *does* matter, you'll notice when the flag is flipped and tests
+ * start failing.
+ */
+ fun setDefault(flag: SysPropBooleanFlag) = booleanFlags.putIfAbsent(flag.id, flag.default)
+
private fun notifyFlagChanged(flag: Flag<*>) {
flagListeners[flag.id]?.let { listeners ->
listeners.forEach { listener ->
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
index 2715aaa..548169e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
@@ -17,8 +17,8 @@
package com.android.systemui.keyguard.data.repository
import com.android.keyguard.FaceAuthUiEvent
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -29,16 +29,16 @@
override val isAuthenticated = MutableStateFlow(false)
override val canRunFaceAuth = MutableStateFlow(false)
- private val _authenticationStatus = MutableStateFlow<AuthenticationStatus?>(null)
- override val authenticationStatus: Flow<AuthenticationStatus> =
+ private val _authenticationStatus = MutableStateFlow<FaceAuthenticationStatus?>(null)
+ override val authenticationStatus: Flow<FaceAuthenticationStatus> =
_authenticationStatus.filterNotNull()
- fun setAuthenticationStatus(status: AuthenticationStatus) {
+ fun setAuthenticationStatus(status: FaceAuthenticationStatus) {
_authenticationStatus.value = status
}
- private val _detectionStatus = MutableStateFlow<DetectionStatus?>(null)
- override val detectionStatus: Flow<DetectionStatus>
+ private val _detectionStatus = MutableStateFlow<FaceDetectionStatus?>(null)
+ override val detectionStatus: Flow<FaceDetectionStatus>
get() = _detectionStatus.filterNotNull()
- fun setDetectionStatus(status: DetectionStatus) {
+ fun setDetectionStatus(status: FaceDetectionStatus) {
_detectionStatus.value = status
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
index 4bfd3d6..38791ca 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
@@ -17,32 +17,38 @@
package com.android.systemui.keyguard.data.repository
+import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.filterNotNull
class FakeDeviceEntryFingerprintAuthRepository : DeviceEntryFingerprintAuthRepository {
private val _isLockedOut = MutableStateFlow(false)
override val isLockedOut: StateFlow<Boolean> = _isLockedOut.asStateFlow()
-
- private val _isRunning = MutableStateFlow(false)
- override val isRunning: Flow<Boolean>
- get() = _isRunning
-
- private var fpSensorType = MutableStateFlow<BiometricType?>(null)
- override val availableFpSensorType: Flow<BiometricType?>
- get() = fpSensorType
-
fun setLockedOut(lockedOut: Boolean) {
_isLockedOut.value = lockedOut
}
+ private val _isRunning = MutableStateFlow(false)
+ override val isRunning: Flow<Boolean>
+ get() = _isRunning
fun setIsRunning(value: Boolean) {
_isRunning.value = value
}
+ private var fpSensorType = MutableStateFlow<BiometricType?>(null)
+ override val availableFpSensorType: Flow<BiometricType?>
+ get() = fpSensorType
fun setAvailableFpSensorType(value: BiometricType?) {
fpSensorType.value = value
}
+
+ private var _authenticationStatus = MutableStateFlow<FingerprintAuthenticationStatus?>(null)
+ override val authenticationStatus: Flow<FingerprintAuthenticationStatus>
+ get() = _authenticationStatus.filterNotNull()
+ fun setAuthenticationStatus(status: FingerprintAuthenticationStatus) {
+ _authenticationStatus.value = status
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index b9d098f..8428566 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -22,11 +22,14 @@
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.ScreenModel
+import com.android.systemui.keyguard.shared.model.ScreenState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakeSleepReason
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessState
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -56,6 +59,9 @@
private val _isDozing = MutableStateFlow(false)
override val isDozing: StateFlow<Boolean> = _isDozing
+ private val _dozeTimeTick = MutableSharedFlow<Unit>()
+ override val dozeTimeTick = _dozeTimeTick
+
private val _lastDozeTapToWakePosition = MutableStateFlow<Point?>(null)
override val lastDozeTapToWakePosition = _lastDozeTapToWakePosition.asStateFlow()
@@ -86,6 +92,9 @@
)
override val wakefulness = _wakefulnessModel
+ private val _screenModel = MutableStateFlow(ScreenModel(ScreenState.SCREEN_OFF))
+ override val screenModel = _screenModel
+
private val _isUdfpsSupported = MutableStateFlow(false)
private val _isKeyguardGoingAway = MutableStateFlow(false)
@@ -147,6 +156,10 @@
_isDozing.value = isDozing
}
+ override fun dozeTimeTick() {
+ _dozeTimeTick.tryEmit(Unit)
+ }
+
override fun setLastDozeTapToWakePosition(position: Point) {
_lastDozeTapToWakePosition.value = position
}
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 56837e8..03e3423 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
@@ -20,7 +20,6 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import java.util.List;
@@ -65,10 +64,6 @@
}
@Override
- public void setMobileIcons(String slot, List<MobileIconState> states) {
- }
-
- @Override
public void setNewMobileIconSubIds(List<Integer> subIds) {
}
diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp
index 2e0a946..1f0181f 100644
--- a/packages/SystemUI/unfold/Android.bp
+++ b/packages/SystemUI/unfold/Android.bp
@@ -33,7 +33,7 @@
"dagger2",
"jsr330",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
java_version: "1.8",
sdk_version: "current",
min_sdk_version: "current",
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
index fee485d..896444d 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
@@ -35,14 +35,12 @@
interface TransitionProgressListener {
/** Called when transition is started */
- @JvmDefault
fun onTransitionStarted() {}
/**
* Called whenever transition progress is updated, [progress] is a value of the animation
* where 0 is fully folded, 1 is fully unfolded
*/
- @JvmDefault
fun onTransitionProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float) {}
/**
@@ -51,11 +49,9 @@
* For example, in [PhysicsBasedUnfoldTransitionProgressProvider] this could happen when the
* animation is not tied to the hinge angle anymore and it is about to run fixed animation.
*/
- @JvmDefault
fun onTransitionFinishing() {}
/** Called when transition is completely finished */
- @JvmDefault
fun onTransitionFinished() {}
}
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
index 0af372f..bce7e88 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
@@ -31,9 +31,9 @@
val isFinishedOpening: Boolean
interface FoldUpdatesListener {
- @JvmDefault fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float) {}
- @JvmDefault fun onFoldUpdate(@FoldUpdate update: Int) {}
- @JvmDefault fun onUnfoldedScreenAvailable() {}
+ fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float) {}
+ fun onFoldUpdate(@FoldUpdate update: Int) {}
+ fun onUnfoldedScreenAvailable() {}
}
@IntDef(
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 1482d07..3f3fa34 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -246,6 +246,31 @@
}
@GuardedBy("mLock")
+ boolean isAtEdge() {
+ return isAtLeftEdge() || isAtRightEdge() || isAtTopEdge() || isAtBottomEdge();
+ }
+
+ @GuardedBy("mLock")
+ boolean isAtLeftEdge() {
+ return getOffsetX() == getMaxOffsetXLocked();
+ }
+
+ @GuardedBy("mLock")
+ boolean isAtRightEdge() {
+ return getOffsetX() == getMinOffsetXLocked();
+ }
+
+ @GuardedBy("mLock")
+ boolean isAtTopEdge() {
+ return getOffsetY() == getMaxOffsetYLocked();
+ }
+
+ @GuardedBy("mLock")
+ boolean isAtBottomEdge() {
+ return getOffsetY() == getMinOffsetYLocked();
+ }
+
+ @GuardedBy("mLock")
float getCenterX() {
return (mMagnificationBounds.width() / 2.0f
+ mMagnificationBounds.left - getOffsetX()) / getScale();
@@ -1086,6 +1111,87 @@
}
/**
+ * Returns whether the user is at one of the edges (left, right, top, bottom)
+ * of the magnification viewport
+ *
+ * @param displayId
+ * @return if user is at the edge of the view
+ */
+ public boolean isAtEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtEdge();
+ }
+ }
+
+ /**
+ * Returns whether the user is at the left edge of the viewport
+ *
+ * @param displayId
+ * @return if user is at left edge of view
+ */
+ public boolean isAtLeftEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtLeftEdge();
+ }
+ }
+
+ /**
+ * Returns whether the user is at the right edge of the viewport
+ *
+ * @param displayId
+ * @return if user is at right edge of view
+ */
+ public boolean isAtRightEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtRightEdge();
+ }
+ }
+
+ /**
+ * Returns whether the user is at the top edge of the viewport
+ *
+ * @param displayId
+ * @return if user is at top edge of view
+ */
+ public boolean isAtTopEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtTopEdge();
+ }
+ }
+
+ /**
+ * Returns whether the user is at the bottom edge of the viewport
+ *
+ * @param displayId
+ * @return if user is at bottom edge of view
+ */
+ public boolean isAtBottomEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtBottomEdge();
+ }
+ }
+
+ /**
* Returns the Y offset of the magnification viewport. If an animation
* is in progress, this reflects the end state of the animation.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 038847e..4aebbf1 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -137,6 +137,7 @@
@VisibleForTesting final DetectingState mDetectingState;
@VisibleForTesting final PanningScalingState mPanningScalingState;
@VisibleForTesting final ViewportDraggingState mViewportDraggingState;
+ @VisibleForTesting final SinglePanningState mSinglePanningState;
private final ScreenStateReceiver mScreenStateReceiver;
private final WindowMagnificationPromptController mPromptController;
@@ -146,7 +147,7 @@
private PointerCoords[] mTempPointerCoords;
private PointerProperties[] mTempPointerProperties;
-
+ @VisibleForTesting boolean mIsSinglePanningEnabled;
public FullScreenMagnificationGestureHandler(@UiContext Context context,
FullScreenMagnificationController fullScreenMagnificationController,
AccessibilityTraceManager trace,
@@ -202,6 +203,8 @@
mDetectingState = new DetectingState(context);
mViewportDraggingState = new ViewportDraggingState();
mPanningScalingState = new PanningScalingState(context);
+ mSinglePanningState = new SinglePanningState(context);
+ setSinglePanningEnabled(false);
if (mDetectShortcutTrigger) {
mScreenStateReceiver = new ScreenStateReceiver(context, this);
@@ -213,6 +216,11 @@
transitionTo(mDetectingState);
}
+ @VisibleForTesting
+ void setSinglePanningEnabled(boolean isEnabled) {
+ mIsSinglePanningEnabled = isEnabled;
+ }
+
@Override
void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
handleEventWith(mCurrentState, event, rawEvent, policyFlags);
@@ -223,6 +231,7 @@
// To keep InputEventConsistencyVerifiers within GestureDetectors happy
mPanningScalingState.mScrollGestureDetector.onTouchEvent(event);
mPanningScalingState.mScaleGestureDetector.onTouchEvent(event);
+ mSinglePanningState.mScrollGestureDetector.onTouchEvent(event);
try {
stateHandler.onMotionEvent(event, rawEvent, policyFlags);
@@ -669,7 +678,6 @@
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-
// Ensures that the state at the end of delegation is consistent with the last delegated
// UP/DOWN event in queue: still delegating if pointer is down, detecting otherwise
switch (event.getActionMasked()) {
@@ -726,6 +734,8 @@
@VisibleForTesting Handler mHandler = new Handler(Looper.getMainLooper(), this);
+ private PointF mFirstPointerDownLocation = new PointF(Float.NaN, Float.NaN);
+
DetectingState(Context context) {
mLongTapMinDelay = ViewConfiguration.getLongPressTimeout();
mMultiTapMaxDelay = ViewConfiguration.getDoubleTapTimeout()
@@ -765,10 +775,11 @@
cacheDelayedMotionEvent(event, rawEvent, policyFlags);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
-
mLastDetectingDownEventTime = event.getDownTime();
mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+ mFirstPointerDownLocation.set(event.getX(), event.getY());
+
if (!mFullScreenMagnificationController.magnificationRegionContains(
mDisplayId, event.getX(), event.getY())) {
@@ -800,7 +811,7 @@
break;
case ACTION_POINTER_DOWN: {
if (isActivated() && event.getPointerCount() == 2) {
- storeSecondPointerDownLocation(event);
+ storePointerDownLocation(mSecondPointerDownLocation, event);
mHandler.sendEmptyMessageDelayed(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
ViewConfiguration.getTapTimeout());
} else {
@@ -815,7 +826,6 @@
case ACTION_MOVE: {
if (isFingerDown()
&& distance(mLastDown, /* move */ event) > mSwipeMinDistance) {
-
// Swipe detected - transition immediately
// For convenience, viewport dragging takes precedence
@@ -826,10 +836,15 @@
} else if (isActivated() && event.getPointerCount() == 2) {
//Primary pointer is swiping, so transit to PanningScalingState
transitToPanningScalingStateAndClear();
+ } else if (mIsSinglePanningEnabled
+ && isActivated()
+ && event.getPointerCount() == 1
+ && !isOverscroll(event)) {
+ transitToSinglePanningStateAndClear();
} else {
transitionToDelegatingStateAndClear();
}
- } else if (isActivated() && secondPointerDownValid()
+ } else if (isActivated() && pointerDownValid(mSecondPointerDownLocation)
&& distanceClosestPointerToPoint(
mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance) {
//Second pointer is swiping, so transit to PanningScalingState
@@ -843,11 +858,9 @@
if (!mFullScreenMagnificationController.magnificationRegionContains(
mDisplayId, event.getX(), event.getY())) {
-
transitionToDelegatingStateAndClear();
} else if (isMultiTapTriggered(3 /* taps */)) {
-
onTripleTap(/* up */ event);
} else if (
@@ -856,7 +869,6 @@
//TODO long tap should never happen here
&& ((timeBetween(mLastDown, mLastUp) >= mLongTapMinDelay)
|| (distance(mLastDown, mLastUp) >= mSwipeMinDistance))) {
-
transitionToDelegatingStateAndClear();
}
@@ -865,14 +877,28 @@
}
}
- private void storeSecondPointerDownLocation(MotionEvent event) {
- final int index = event.getActionIndex();
- mSecondPointerDownLocation.set(event.getX(index), event.getY(index));
+ private boolean isOverscroll(MotionEvent event) {
+ if (!pointerDownValid(mFirstPointerDownLocation)) {
+ return false;
+ }
+ float dX = event.getX() - mFirstPointerDownLocation.x;
+ float dY = event.getY() - mFirstPointerDownLocation.y;
+ boolean didOverscroll =
+ mFullScreenMagnificationController.isAtLeftEdge(mDisplayId) && dX > 0
+ || mFullScreenMagnificationController.isAtRightEdge(mDisplayId) && dX < 0
+ || mFullScreenMagnificationController.isAtTopEdge(mDisplayId) && dY > 0
+ || mFullScreenMagnificationController.isAtBottomEdge(mDisplayId) && dY < 0;
+ return didOverscroll;
}
- private boolean secondPointerDownValid() {
- return !(Float.isNaN(mSecondPointerDownLocation.x) && Float.isNaN(
- mSecondPointerDownLocation.y));
+ private void storePointerDownLocation(PointF pointerDownLocation, MotionEvent event) {
+ final int index = event.getActionIndex();
+ pointerDownLocation.set(event.getX(index), event.getY(index));
+ }
+
+ private boolean pointerDownValid(PointF pointerDownLocation) {
+ return !(Float.isNaN(pointerDownLocation.x) && Float.isNaN(
+ pointerDownLocation.y));
}
private void transitToPanningScalingStateAndClear() {
@@ -880,6 +906,11 @@
clear();
}
+ private void transitToSinglePanningStateAndClear() {
+ transitionTo(mSinglePanningState);
+ clear();
+ }
+
public boolean isMultiTapTriggered(int numTaps) {
// Shortcut acts as the 2 initial taps
@@ -947,6 +978,7 @@
setShortcutTriggered(false);
removePendingDelayedMessages();
clearDelayedMotionEvents();
+ mFirstPointerDownLocation.set(Float.NaN, Float.NaN);
mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
}
@@ -1165,12 +1197,14 @@
+ ", mDelegatingState=" + mDelegatingState
+ ", mMagnifiedInteractionState=" + mPanningScalingState
+ ", mViewportDraggingState=" + mViewportDraggingState
+ + ", mSinglePanningState=" + mSinglePanningState
+ ", mDetectTripleTap=" + mDetectTripleTap
+ ", mDetectShortcutTrigger=" + mDetectShortcutTrigger
+ ", mCurrentState=" + State.nameOf(mCurrentState)
+ ", mPreviousState=" + State.nameOf(mPreviousState)
+ ", mMagnificationController=" + mFullScreenMagnificationController
+ ", mDisplayId=" + mDisplayId
+ + ", mIsSinglePanningEnabled=" + mIsSinglePanningEnabled
+ '}';
}
@@ -1285,8 +1319,67 @@
* Indicates an error with a gesture handler or state.
*/
private static class GestureException extends Exception {
+
GestureException(String message) {
super(message);
}
}
+
+ final class SinglePanningState extends SimpleOnGestureListener implements State {
+ private final GestureDetector mScrollGestureDetector;
+ private MotionEventInfo mEvent;
+
+ SinglePanningState(Context context) {
+ mScrollGestureDetector = new GestureDetector(context, this, Handler.getMain());
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ int action = event.getActionMasked();
+ switch (action) {
+ case ACTION_UP:
+ case ACTION_CANCEL:
+ clear();
+ transitionTo(mDetectingState);
+ break;
+ }
+ }
+
+ @Override
+ public boolean onScroll(
+ MotionEvent first, MotionEvent second, float distanceX, float distanceY) {
+ if (mCurrentState != mSinglePanningState) {
+ return true;
+ }
+ mFullScreenMagnificationController.offsetMagnifiedRegion(
+ mDisplayId,
+ distanceX,
+ distanceY,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ if (DEBUG_PANNING_SCALING) {
+ Slog.i(
+ mLogTag,
+ "SinglePanningState Panned content by scrollX: "
+ + distanceX
+ + " scrollY: "
+ + distanceY
+ + " isAtEdge: "
+ + mFullScreenMagnificationController.isAtEdge(mDisplayId));
+ }
+ // TODO: b/280812104 Dispatch events before Delegation
+ if (mFullScreenMagnificationController.isAtEdge(mDisplayId)) {
+ clear();
+ transitionTo(mDelegatingState);
+ }
+ return /* event consumed: */ true;
+ }
+
+ @Override
+ public String toString() {
+ return "SinglePanningState{"
+ + "isEdgeOfView="
+ + mFullScreenMagnificationController.isAtEdge(mDisplayId)
+ + "}";
+ }
+ }
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index ca1ab9b..315972c 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -42,6 +42,7 @@
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
+import android.app.admin.DevicePolicyManagerInternal;
import android.app.assist.ActivityId;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
@@ -94,10 +95,12 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.infra.GlobalWhitelistState;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.DumpUtils;
import com.android.server.LocalServices;
import com.android.server.contentprotection.ContentProtectionBlocklistManager;
+import com.android.server.contentprotection.ContentProtectionConsentManager;
import com.android.server.contentprotection.ContentProtectionPackageManager;
import com.android.server.contentprotection.RemoteContentProtectionService;
import com.android.server.infra.AbstractMasterSystemService;
@@ -216,6 +219,8 @@
@Nullable private final ContentProtectionBlocklistManager mContentProtectionBlocklistManager;
+ @Nullable private final ContentProtectionConsentManager mContentProtectionConsentManager;
+
public ContentCaptureManagerService(@NonNull Context context) {
super(context, new FrameworkResourcesServiceNameResolver(context,
com.android.internal.R.string.config_defaultContentCaptureService),
@@ -260,12 +265,15 @@
mContentProtectionBlocklistManager = createContentProtectionBlocklistManager();
mContentProtectionBlocklistManager.updateBlocklist(
mDevCfgContentProtectionAppsBlocklistSize);
+ mContentProtectionConsentManager = createContentProtectionConsentManager();
} else {
mContentProtectionBlocklistManager = null;
+ mContentProtectionConsentManager = null;
}
} else {
mContentProtectionServiceComponentName = null;
mContentProtectionBlocklistManager = null;
+ mContentProtectionConsentManager = null;
}
}
@@ -802,6 +810,17 @@
new ContentProtectionPackageManager(getContext()));
}
+ /** @hide */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ @NonNull
+ protected ContentProtectionConsentManager createContentProtectionConsentManager() {
+ // Same handler as used by AbstractMasterSystemService
+ return new ContentProtectionConsentManager(
+ BackgroundThread.getHandler(),
+ getContext().getContentResolver(),
+ LocalServices.getService(DevicePolicyManagerInternal.class));
+ }
+
@Nullable
private ComponentName getContentProtectionServiceComponentName() {
String flatComponentName = getContentProtectionServiceFlatComponentName();
@@ -1213,7 +1232,7 @@
isContentCaptureReceiverEnabled =
isContentCaptureReceiverEnabled(userId, packageName);
isContentProtectionReceiverEnabled =
- isContentProtectionReceiverEnabled(packageName);
+ isContentProtectionReceiverEnabled(userId, packageName);
if (!isContentCaptureReceiverEnabled) {
// Full package is not allowlisted: check individual components next
@@ -1284,13 +1303,13 @@
@Override // from GlobalWhitelistState
public boolean isWhitelisted(@UserIdInt int userId, @NonNull String packageName) {
return isContentCaptureReceiverEnabled(userId, packageName)
- || isContentProtectionReceiverEnabled(packageName);
+ || isContentProtectionReceiverEnabled(userId, packageName);
}
@Override // from GlobalWhitelistState
public boolean isWhitelisted(@UserIdInt int userId, @NonNull ComponentName componentName) {
return super.isWhitelisted(userId, componentName)
- || isContentProtectionReceiverEnabled(componentName.getPackageName());
+ || isContentProtectionReceiverEnabled(userId, componentName.getPackageName());
}
private boolean isContentCaptureReceiverEnabled(
@@ -1298,9 +1317,11 @@
return super.isWhitelisted(userId, packageName);
}
- private boolean isContentProtectionReceiverEnabled(@NonNull String packageName) {
+ private boolean isContentProtectionReceiverEnabled(
+ @UserIdInt int userId, @NonNull String packageName) {
if (mContentProtectionServiceComponentName == null
- || mContentProtectionBlocklistManager == null) {
+ || mContentProtectionBlocklistManager == null
+ || mContentProtectionConsentManager == null) {
return false;
}
synchronized (mLock) {
@@ -1308,7 +1329,8 @@
return false;
}
}
- return mContentProtectionBlocklistManager.isAllowed(packageName);
+ return mContentProtectionConsentManager.isConsentGranted(userId)
+ && mContentProtectionBlocklistManager.isAllowed(packageName);
}
}
diff --git a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java
new file mode 100644
index 0000000..2eb758c
--- /dev/null
+++ b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.contentprotection;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Manages consent for content protection.
+ *
+ * @hide
+ */
+public class ContentProtectionConsentManager {
+
+ private static final String TAG = "ContentProtectionConsentManager";
+
+ private static final String KEY_PACKAGE_VERIFIER_USER_CONSENT = "package_verifier_user_consent";
+
+ @NonNull private final ContentResolver mContentResolver;
+
+ @NonNull private final DevicePolicyManagerInternal mDevicePolicyManagerInternal;
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ @NonNull
+ public final ContentObserver mContentObserver;
+
+ private volatile boolean mCachedPackageVerifierConsent;
+
+ public ContentProtectionConsentManager(
+ @NonNull Handler handler,
+ @NonNull ContentResolver contentResolver,
+ @NonNull DevicePolicyManagerInternal devicePolicyManagerInternal) {
+ mContentResolver = contentResolver;
+ mDevicePolicyManagerInternal = devicePolicyManagerInternal;
+ mContentObserver = new SettingsObserver(handler);
+
+ contentResolver.registerContentObserver(
+ Settings.Global.getUriFor(KEY_PACKAGE_VERIFIER_USER_CONSENT),
+ /* notifyForDescendants= */ false,
+ mContentObserver,
+ UserHandle.USER_ALL);
+ mCachedPackageVerifierConsent = isPackageVerifierConsentGranted();
+ }
+
+ /**
+ * Returns true if all the consents are granted
+ */
+ public boolean isConsentGranted(@UserIdInt int userId) {
+ return mCachedPackageVerifierConsent && !isUserOrganizationManaged(userId);
+ }
+
+ private boolean isPackageVerifierConsentGranted() {
+ // Not always cached internally
+ return Settings.Global.getInt(
+ mContentResolver, KEY_PACKAGE_VERIFIER_USER_CONSENT, /* def= */ 0)
+ >= 1;
+ }
+
+ private boolean isUserOrganizationManaged(@UserIdInt int userId) {
+ // Cached internally
+ return mDevicePolicyManagerInternal.isUserOrganizationManaged(userId);
+ }
+
+ private final class SettingsObserver extends ContentObserver {
+
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
+ final String property = uri.getLastPathSegment();
+ if (property == null) {
+ return;
+ }
+ switch (property) {
+ case KEY_PACKAGE_VERIFIER_USER_CONSENT:
+ mCachedPackageVerifierConsent = isPackageVerifierConsentGranted();
+ return;
+ default:
+ Slog.w(TAG, "Ignoring unexpected property: " + property);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 7c3a75f..5a15f17 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -49,6 +49,7 @@
private static final String TAG = "MasterClear";
private boolean mWipeExternalStorage;
private boolean mWipeEsims;
+ private boolean mShowWipeProgress = true;
@Override
public void onReceive(final Context context, final Intent intent) {
@@ -77,8 +78,12 @@
return;
}
- final boolean shutdown = intent.getBooleanExtra("shutdown", false);
final String reason = intent.getStringExtra(Intent.EXTRA_REASON);
+
+ // Factory reset dialog has its own UI for the reset process, so suppress ours if indicated.
+ mShowWipeProgress = intent.getBooleanExtra(Intent.EXTRA_SHOW_WIPE_PROGRESS, true);
+
+ final boolean shutdown = intent.getBooleanExtra("shutdown", false);
mWipeExternalStorage = intent.getBooleanExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false);
mWipeEsims = intent.getBooleanExtra(Intent.EXTRA_WIPE_ESIMS, false);
final boolean forceWipe = intent.getBooleanExtra(Intent.EXTRA_FORCE_MASTER_CLEAR, false)
@@ -190,15 +195,19 @@
public WipeDataTask(Context context, Thread chainedTask) {
mContext = context;
mChainedTask = chainedTask;
- mProgressDialog = new ProgressDialog(context, R.style.Theme_DeviceDefault_System);
+ mProgressDialog = mShowWipeProgress
+ ? new ProgressDialog(context, R.style.Theme_DeviceDefault_System)
+ : null;
}
@Override
protected void onPreExecute() {
- mProgressDialog.setIndeterminate(true);
- mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- mProgressDialog.setMessage(mContext.getText(R.string.progress_erasing));
- mProgressDialog.show();
+ if (mProgressDialog != null) {
+ mProgressDialog.setIndeterminate(true);
+ mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ mProgressDialog.setMessage(mContext.getText(R.string.progress_erasing));
+ mProgressDialog.show();
+ }
}
@Override
@@ -214,7 +223,9 @@
@Override
protected void onPostExecute(Void result) {
- mProgressDialog.dismiss();
+ if (mProgressDialog != null && mProgressDialog.isShowing()) {
+ mProgressDialog.dismiss();
+ }
mChainedTask.start();
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8012c26..26bcb98 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5083,6 +5083,10 @@
// Tell anyone interested that we are done booting!
SystemProperties.set("sys.boot_completed", "1");
SystemProperties.set("dev.bootcomplete", "1");
+
+ // Start PSI monitoring in LMKD if it was skipped earlier.
+ ProcessList.startPsiMonitoringAfterBoot();
+
mUserController.onBootComplete(
new IIntentReceiver.Stub() {
@Override
@@ -19915,7 +19919,8 @@
return superImpl.apply(code, new AttributionSource(shellUid,
Process.INVALID_PID, "com.android.shell",
attributionSource.getAttributionTag(), attributionSource.getToken(),
- /*renouncedPermissions*/ null, attributionSource.getNext()),
+ /*renouncedPermissions*/ null, attributionSource.getDeviceId(),
+ attributionSource.getNext()),
shouldCollectAsyncNotedOp, message, shouldCollectMessage,
skiProxyOperation);
} finally {
@@ -19968,7 +19973,8 @@
return superImpl.apply(clientId, code, new AttributionSource(shellUid,
Process.INVALID_PID, "com.android.shell",
attributionSource.getAttributionTag(), attributionSource.getToken(),
- /*renouncedPermissions*/ null, attributionSource.getNext()),
+ /*renouncedPermissions*/ null, attributionSource.getDeviceId(),
+ attributionSource.getNext()),
startIfModeDefault, shouldCollectAsyncNotedOp, message,
shouldCollectMessage, skipProxyOperation, proxyAttributionFlags,
proxiedAttributionFlags, attributionChainId);
@@ -19994,7 +20000,8 @@
superImpl.apply(clientId, code, new AttributionSource(shellUid,
Process.INVALID_PID, "com.android.shell",
attributionSource.getAttributionTag(), attributionSource.getToken(),
- /*renouncedPermissions*/ null, attributionSource.getNext()),
+ /*renouncedPermissions*/ null, attributionSource.getDeviceId(),
+ attributionSource.getNext()),
skipProxyOperation);
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index acd9771..e484a6c 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -357,6 +357,7 @@
static final byte LMK_UPDATE_PROPS = 7;
static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event
static final byte LMK_STATE_CHANGED = 9; // Msg to subscribed clients on state changed
+ static final byte LMK_START_MONITORING = 9; // Start monitoring if delayed earlier
// Low Memory Killer Daemon command codes.
// These must be kept in sync with async_event_type definitions in lmkd.h
@@ -1568,6 +1569,15 @@
return true;
}
+ /**
+ * {@hide}
+ */
+ public static void startPsiMonitoringAfterBoot() {
+ ByteBuffer buf = ByteBuffer.allocate(4);
+ buf.putInt(LMK_START_MONITORING);
+ writeLmkd(buf, null);
+ }
+
private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) {
if (!sLmkdConnection.isConnected()) {
// try to connect immediately and then keep retrying
diff --git a/services/core/java/com/android/server/appop/OWNERS b/services/core/java/com/android/server/appop/OWNERS
index 999ea0e..2fe78c5 100644
--- a/services/core/java/com/android/server/appop/OWNERS
+++ b/services/core/java/com/android/server/appop/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
include /core/java/android/permission/OWNERS
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 7404b19..9f958be 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -72,6 +72,8 @@
import android.database.ContentObserver;
import android.hardware.SensorPrivacyManager;
import android.hardware.SensorPrivacyManagerInternal;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.hdmi.HdmiAudioSystemClient;
import android.hardware.hdmi.HdmiClient;
import android.hardware.hdmi.HdmiControlManager;
@@ -180,6 +182,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.view.Display;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
@@ -977,6 +980,36 @@
private AtomicBoolean mMasterMute = new AtomicBoolean(false);
+ private DisplayManager mDisplayManager;
+
+ private DisplayListener mDisplayListener =
+ new DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {}
+
+ @Override
+ public void onDisplayRemoved(int displayId) {}
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ return;
+ }
+ int displayState = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getState();
+ if (displayState == Display.STATE_ON) {
+ if (mMonitorRotation) {
+ RotationHelper.enable();
+ }
+ AudioSystem.setParameters("screen_state=on");
+ } else {
+ if (mMonitorRotation) {
+ //reduce wakeups (save current) by only listening when display is on
+ RotationHelper.disable();
+ }
+ AudioSystem.setParameters("screen_state=off");
+ }
+ }
+ };
///////////////////////////////////////////////////////////////////////////
// Construction
@@ -1255,6 +1288,8 @@
0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_SPATIALIZER,
0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
+
+ mDisplayManager = context.getSystemService(DisplayManager.class);
}
private void initVolumeStreamStates() {
@@ -1351,8 +1386,6 @@
new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
intentFilter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
- intentFilter.addAction(Intent.ACTION_SCREEN_ON);
- intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
intentFilter.addAction(Intent.ACTION_USER_BACKGROUND);
intentFilter.addAction(Intent.ACTION_USER_FOREGROUND);
@@ -1382,6 +1415,8 @@
} else {
subscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionChangedListener);
}
+
+ mDisplayManager.registerDisplayListener(mDisplayListener, mAudioHandler);
}
public void systemReady() {
@@ -9552,17 +9587,6 @@
} else if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)
|| action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
mDeviceBroker.receiveBtEvent(intent);
- } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
- if (mMonitorRotation) {
- RotationHelper.enable();
- }
- AudioSystem.setParameters("screen_state=on");
- } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
- if (mMonitorRotation) {
- //reduce wakeups (save current) by only listening when display is on
- RotationHelper.disable();
- }
- AudioSystem.setParameters("screen_state=off");
} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
sendMsg(mAudioHandler,
MSG_CONFIGURATION_CHANGED,
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 78c3808..8a54ae5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -580,7 +580,7 @@
}
final BiometricSchedulerOperation operation = mCurrentOperation;
mHandler.postDelayed(() -> {
- if (operation == mCurrentOperation) {
+ if (operation == mCurrentOperation && !operation.isFinished()) {
Counter.logIncrement("biometric.value_scheduler_watchdog_triggered_count");
clearScheduler();
}
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 5b11cfe..4bfc090 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -36,16 +36,18 @@
BrightnessRangeController(HighBrightnessModeController hbmController,
- Runnable modeChangeCallback) {
- this(hbmController, modeChangeCallback,
+ Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig) {
+ this(hbmController, modeChangeCallback, displayDeviceConfig,
new DeviceConfigParameterProvider(DeviceConfigInterface.REAL));
}
BrightnessRangeController(HighBrightnessModeController hbmController,
- Runnable modeChangeCallback, DeviceConfigParameterProvider configParameterProvider) {
+ Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig,
+ DeviceConfigParameterProvider configParameterProvider) {
mHbmController = hbmController;
mModeChangeCallback = modeChangeCallback;
mUseNbmController = configParameterProvider.isNormalBrightnessControllerFeatureEnabled();
+ mNormalBrightnessModeController.resetNbmData(displayDeviceConfig.getLuxThrottlingData());
}
void dump(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java
index dd5afa2..da51569 100644
--- a/services/core/java/com/android/server/display/DisplayBrightnessState.java
+++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java
@@ -33,12 +33,15 @@
private final String mDisplayBrightnessStrategyName;
private final boolean mShouldUseAutoBrightness;
+ private final boolean mIsSlowChange;
+
private DisplayBrightnessState(Builder builder) {
mBrightness = builder.getBrightness();
mSdrBrightness = builder.getSdrBrightness();
mBrightnessReason = builder.getBrightnessReason();
mDisplayBrightnessStrategyName = builder.getDisplayBrightnessStrategyName();
mShouldUseAutoBrightness = builder.getShouldUseAutoBrightness();
+ mIsSlowChange = builder.isSlowChange();
}
/**
@@ -77,6 +80,13 @@
return mShouldUseAutoBrightness;
}
+ /**
+ * @return {@code true} if the should transit to new state slowly
+ */
+ public boolean isSlowChange() {
+ return mIsSlowChange;
+ }
+
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder("DisplayBrightnessState:");
@@ -88,6 +98,8 @@
stringBuilder.append(getBrightnessReason());
stringBuilder.append("\n shouldUseAutoBrightness:");
stringBuilder.append(getShouldUseAutoBrightness());
+ stringBuilder.append("\n isSlowChange:");
+ stringBuilder.append(mIsSlowChange);
return stringBuilder.toString();
}
@@ -111,13 +123,14 @@
&& mBrightnessReason.equals(otherState.getBrightnessReason())
&& TextUtils.equals(mDisplayBrightnessStrategyName,
otherState.getDisplayBrightnessStrategyName())
- && mShouldUseAutoBrightness == otherState.getShouldUseAutoBrightness();
+ && mShouldUseAutoBrightness == otherState.getShouldUseAutoBrightness()
+ && mIsSlowChange == otherState.isSlowChange();
}
@Override
public int hashCode() {
- return Objects.hash(
- mBrightness, mSdrBrightness, mBrightnessReason, mShouldUseAutoBrightness);
+ return Objects.hash(mBrightness, mSdrBrightness, mBrightnessReason,
+ mShouldUseAutoBrightness, mIsSlowChange);
}
/**
@@ -129,6 +142,7 @@
private BrightnessReason mBrightnessReason = new BrightnessReason();
private String mDisplayBrightnessStrategyName;
private boolean mShouldUseAutoBrightness;
+ private boolean mIsSlowChange;
/**
* Create a builder starting with the values from the specified {@link
@@ -143,6 +157,7 @@
builder.setBrightnessReason(state.getBrightnessReason());
builder.setDisplayBrightnessStrategyName(state.getDisplayBrightnessStrategyName());
builder.setShouldUseAutoBrightness(state.getShouldUseAutoBrightness());
+ builder.setIsSlowChange(state.isSlowChange());
return builder;
}
@@ -237,6 +252,21 @@
}
/**
+ * See {@link DisplayBrightnessState#isSlowChange()}.
+ */
+ public Builder setIsSlowChange(boolean shouldUseAutoBrightness) {
+ this.mIsSlowChange = shouldUseAutoBrightness;
+ return this;
+ }
+
+ /**
+ * See {@link DisplayBrightnessState#isSlowChange()}.
+ */
+ public boolean isSlowChange() {
+ return mIsSlowChange;
+ }
+
+ /**
* This is used to construct an immutable DisplayBrightnessState object from its builder
*/
public DisplayBrightnessState build() {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index dee49e8..75c15eb 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -673,7 +673,7 @@
HighBrightnessModeController hbmController = createHbmControllerLocked(modeChangeCallback);
mBrightnessRangeController = new BrightnessRangeController(hbmController,
- modeChangeCallback);
+ modeChangeCallback, mDisplayDeviceConfig);
mBrightnessThrottler = createBrightnessThrottlerLocked();
@@ -2288,8 +2288,17 @@
if (!reportOnly && mPowerState.getScreenState() != state
&& readyToUpdateDisplayState()) {
Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
- // TODO(b/153319140) remove when we can get this from the above trace invocation
- SystemProperties.set("debug.tracing.screen_state", String.valueOf(state));
+
+ String propertyKey = "debug.tracing.screen_state";
+ String propertyValue = String.valueOf(state);
+ try {
+ // TODO(b/153319140) remove when we can get this from the above trace invocation
+ SystemProperties.set(propertyKey, propertyValue);
+ } catch (RuntimeException e) {
+ Slog.e(mTag, "Failed to set a system property: key=" + propertyKey
+ + " value=" + propertyValue + " " + e.getMessage());
+ }
+
mPowerState.setScreenState(state);
// Tell battery stats about the transition.
noteScreenState(state);
@@ -2382,8 +2391,17 @@
}
if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate)) {
Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target);
- // TODO(b/153319140) remove when we can get this from the above trace invocation
- SystemProperties.set("debug.tracing.screen_brightness", String.valueOf(target));
+
+ String propertyKey = "debug.tracing.screen_brightness";
+ String propertyValue = String.valueOf(target);
+ try {
+ // TODO(b/153319140) remove when we can get this from the above trace invocation
+ SystemProperties.set(propertyKey, propertyValue);
+ } catch (RuntimeException e) {
+ Slog.e(mTag, "Failed to set a system property: key=" + propertyKey
+ + " value=" + propertyValue + " " + e.getMessage());
+ }
+
noteScreenBrightness(target);
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 7e53d9e..42683d8 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -444,9 +444,6 @@
@Nullable
private BrightnessMappingStrategy mIdleModeBrightnessMapper;
- // Indicates whether we should ramp slowly to the brightness value to follow.
- private boolean mBrightnessToFollowSlowChange;
-
private boolean mIsRbcActive;
// Animators.
@@ -555,7 +552,7 @@
mBrightnessThrottler = createBrightnessThrottlerLocked();
mBrightnessRangeController = new BrightnessRangeController(hbmController,
- modeChangeCallback);
+ modeChangeCallback, mDisplayDeviceConfig);
mDisplayBrightnessController =
new DisplayBrightnessController(context, null,
@@ -1291,7 +1288,7 @@
// actual state instead of the desired one.
animateScreenStateChange(state, mDisplayStateController.shouldPerformScreenOffTransition());
state = mPowerState.getScreenState();
- boolean slowChange = false;
+
final boolean userSetBrightnessChanged = mDisplayBrightnessController
.updateUserSetScreenBrightness();
@@ -1300,11 +1297,7 @@
float brightnessState = displayBrightnessState.getBrightness();
float rawBrightnessState = displayBrightnessState.getBrightness();
mBrightnessReasonTemp.set(displayBrightnessState.getBrightnessReason());
-
- if (displayBrightnessState.getBrightnessReason().getReason()
- == BrightnessReason.REASON_FOLLOWER) {
- slowChange = mBrightnessToFollowSlowChange;
- }
+ boolean slowChange = displayBrightnessState.isSlowChange();
// Set up the ScreenOff controller used when coming out of SCREEN_OFF and the ALS sensor
// doesn't yet have a valid lux value to use with auto-brightness.
@@ -1354,6 +1347,7 @@
.getRawAutomaticScreenBrightness();
brightnessState = clampScreenBrightness(brightnessState);
// slowly adapt to auto-brightness
+ // TODO(b/253226419): slowChange should be decided by strategy.updateBrightness
slowChange = mAutomaticBrightnessStrategy.hasAppliedAutoBrightness()
&& !mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged();
brightnessAdjustmentFlags =
@@ -1955,8 +1949,17 @@
if (!reportOnly && mPowerState.getScreenState() != state
&& readyToUpdateDisplayState()) {
Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
- // TODO(b/153319140) remove when we can get this from the above trace invocation
- SystemProperties.set("debug.tracing.screen_state", String.valueOf(state));
+
+ String propertyKey = "debug.tracing.screen_state";
+ String propertyValue = String.valueOf(state);
+ try {
+ // TODO(b/153319140) remove when we can get this from the above trace invocation
+ SystemProperties.set(propertyKey, propertyValue);
+ } catch (RuntimeException e) {
+ Slog.e(mTag, "Failed to set a system property: key=" + propertyKey
+ + " value=" + propertyValue + " " + e.getMessage());
+ }
+
mPowerState.setScreenState(state);
// Tell battery stats about the transition.
noteScreenState(state);
@@ -2031,8 +2034,17 @@
}
if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate)) {
Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target);
- // TODO(b/153319140) remove when we can get this from the above trace invocation
- SystemProperties.set("debug.tracing.screen_brightness", String.valueOf(target));
+
+ String propertyKey = "debug.tracing.screen_brightness";
+ String propertyValue = String.valueOf(target);
+ try {
+ // TODO(b/153319140) remove when we can get this from the above trace invocation
+ SystemProperties.set(propertyKey, propertyValue);
+ } catch (RuntimeException e) {
+ Slog.e(mTag, "Failed to set a system property: key=" + propertyKey
+ + " value=" + propertyValue + " " + e.getMessage());
+ }
+
noteScreenBrightness(target);
}
}
@@ -2259,17 +2271,17 @@
boolean slowChange) {
mBrightnessRangeController.onAmbientLuxChange(ambientLux);
if (nits < 0) {
- mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness);
+ mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, slowChange);
} else {
float brightness = mDisplayBrightnessController.convertToFloatScale(nits);
if (BrightnessUtils.isValidBrightnessValue(brightness)) {
- mDisplayBrightnessController.setBrightnessToFollow(brightness);
+ mDisplayBrightnessController.setBrightnessToFollow(brightness, slowChange);
} else {
// The device does not support nits
- mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness);
+ mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness,
+ slowChange);
}
}
- mBrightnessToFollowSlowChange = slowChange;
sendUpdatePowerState();
}
@@ -2409,7 +2421,6 @@
pw.println(" mReportedToPolicy="
+ reportedToPolicyToString(mReportedScreenStateToPolicy));
pw.println(" mIsRbcActive=" + mIsRbcActive);
- pw.println(" mBrightnessToFollowSlowChange=" + mBrightnessToFollowSlowChange);
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
mAutomaticBrightnessStrategy.dump(ipw);
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessUtils.java b/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
index 3fae224..8bf675c 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
@@ -54,6 +54,16 @@
public static DisplayBrightnessState constructDisplayBrightnessState(
int brightnessChangeReason, float brightness, float sdrBrightness,
String displayBrightnessStrategyName) {
+ return constructDisplayBrightnessState(brightnessChangeReason, brightness, sdrBrightness,
+ displayBrightnessStrategyName, /* slowChange= */ false);
+ }
+
+ /**
+ * A utility to construct the DisplayBrightnessState
+ */
+ public static DisplayBrightnessState constructDisplayBrightnessState(
+ int brightnessChangeReason, float brightness, float sdrBrightness,
+ String displayBrightnessStrategyName, boolean slowChange) {
BrightnessReason brightnessReason = new BrightnessReason();
brightnessReason.setReason(brightnessChangeReason);
return new DisplayBrightnessState.Builder()
@@ -61,6 +71,7 @@
.setSdrBrightness(sdrBrightness)
.setBrightnessReason(brightnessReason)
.setDisplayBrightnessStrategyName(displayBrightnessStrategyName)
+ .setIsSlowChange(slowChange)
.build();
}
}
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index ffd62a3..d6f0098 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -164,10 +164,10 @@
/**
* Sets the brightness to follow
*/
- public void setBrightnessToFollow(Float brightnessToFollow) {
+ public void setBrightnessToFollow(float brightnessToFollow, boolean slowChange) {
synchronized (mLock) {
mDisplayBrightnessStrategySelector.getFollowerDisplayBrightnessStrategy()
- .setBrightnessToFollow(brightnessToFollow);
+ .setBrightnessToFollow(brightnessToFollow, slowChange);
}
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/FollowerBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/FollowerBrightnessStrategy.java
index 090ec13..585f576 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/FollowerBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/FollowerBrightnessStrategy.java
@@ -37,9 +37,13 @@
// Set to PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no brightness to follow set.
private float mBrightnessToFollow;
+ // Indicates whether we should ramp slowly to the brightness value to follow.
+ private boolean mBrightnessToFollowSlowChange;
+
public FollowerBrightnessStrategy(int displayId) {
mDisplayId = displayId;
mBrightnessToFollow = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mBrightnessToFollowSlowChange = false;
}
@Override
@@ -48,7 +52,7 @@
// Todo(b/241308599): Introduce a validator class and add validations before setting
// the brightness
return BrightnessUtils.constructDisplayBrightnessState(BrightnessReason.REASON_FOLLOWER,
- mBrightnessToFollow, mBrightnessToFollow, getName());
+ mBrightnessToFollow, mBrightnessToFollow, getName(), mBrightnessToFollowSlowChange);
}
@Override
@@ -60,8 +64,12 @@
return mBrightnessToFollow;
}
- public void setBrightnessToFollow(float brightnessToFollow) {
+ /**
+ * Updates brightness value and brightness slowChange flag
+ **/
+ public void setBrightnessToFollow(float brightnessToFollow, boolean slowChange) {
mBrightnessToFollow = brightnessToFollow;
+ mBrightnessToFollowSlowChange = slowChange;
}
/**
@@ -71,5 +79,6 @@
writer.println("FollowerBrightnessStrategy:");
writer.println(" mDisplayId=" + mDisplayId);
writer.println(" mBrightnessToFollow:" + mBrightnessToFollow);
+ writer.println(" mBrightnessToFollowSlowChange:" + mBrightnessToFollowSlowChange);
}
}
diff --git a/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
index feebdf1..dfb5f62 100644
--- a/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
+++ b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
@@ -60,6 +60,11 @@
DisplayManager.DeviceConfig.KEY_USE_NORMAL_BRIGHTNESS_MODE_CONTROLLER, false);
}
+ public boolean isDisableScreenWakeLocksWhileCachedFeatureEnabled() {
+ return mDeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_DISABLE_SCREEN_WAKE_LOCKS_WHILE_CACHED, true);
+ }
+
// feature: smooth_display_feature
// parameter: peak_refresh_rate_default
public float getPeakRefreshRateDefault() {
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 633bf731..0e8a5fb 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -17,6 +17,8 @@
package com.android.server.dreams;
import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
+import static android.os.PowerManager.USER_ACTIVITY_EVENT_OTHER;
+import static android.os.PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS;
import android.app.ActivityTaskManager;
import android.app.BroadcastOptions;
@@ -72,6 +74,7 @@
private final Handler mHandler;
private final Listener mListener;
private final ActivityTaskManager mActivityTaskManager;
+ private final PowerManager mPowerManager;
private final Intent mDreamingStartedIntent = new Intent(Intent.ACTION_DREAMING_STARTED)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | FLAG_RECEIVER_FOREGROUND);
@@ -84,6 +87,15 @@
private final Intent mCloseNotificationShadeIntent;
private final Bundle mCloseNotificationShadeOptions;
+ /**
+ * If this flag is on, we report user activity to {@link PowerManager} so that the screen
+ * doesn't shut off immediately when a dream quits unexpectedly. The device will instead go to
+ * keyguard and time out back to dreaming shortly.
+ *
+ * This allows the dream a second chance to relaunch in case of an app update or other crash.
+ */
+ private final boolean mResetScreenTimeoutOnUnexpectedDreamExit;
+
private DreamRecord mCurrentDream;
// Whether a dreaming started intent has been broadcast.
@@ -101,6 +113,7 @@
mHandler = handler;
mListener = listener;
mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
+ mPowerManager = mContext.getSystemService(PowerManager.class);
mCloseNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mCloseNotificationShadeIntent.putExtra(EXTRA_REASON_KEY, EXTRA_REASON_VALUE);
mCloseNotificationShadeIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -110,6 +123,8 @@
EXTRA_REASON_VALUE)
.setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
.toBundle();
+ mResetScreenTimeoutOnUnexpectedDreamExit = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_resetScreenTimeoutOnUnexpectedDreamExit);
}
/**
@@ -235,6 +250,17 @@
}
/**
+ * Sends a user activity signal to PowerManager to stop the screen from turning off immediately
+ * if there hasn't been any user interaction in a while.
+ */
+ private void resetScreenTimeout() {
+ Slog.i(TAG, "Resetting screen timeout");
+ long time = SystemClock.uptimeMillis();
+ mPowerManager.userActivity(time, USER_ACTIVITY_EVENT_OTHER,
+ USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS);
+ }
+
+ /**
* Stops dreaming.
*
* The current dream, if any, and any unstopped previous dreams are stopped. The device stops
@@ -448,6 +474,9 @@
mHandler.post(() -> {
mService = null;
if (mCurrentDream == DreamRecord.this) {
+ if (mResetScreenTimeoutOnUnexpectedDreamExit) {
+ resetScreenTimeout();
+ }
stopDream(true /*immediate*/, "binder died");
}
});
@@ -473,6 +502,9 @@
mHandler.post(() -> {
mService = null;
if (mCurrentDream == DreamRecord.this) {
+ if (mResetScreenTimeoutOnUnexpectedDreamExit) {
+ resetScreenTimeout();
+ }
stopDream(true /*immediate*/, "service disconnected");
}
});
diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
index eb2da34..4b30ae5 100644
--- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
+++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
@@ -21,12 +21,16 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
import android.hardware.input.KeyboardLayout;
import android.icu.util.ULocale;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.InputDevice;
+import android.view.KeyEvent;
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.annotations.VisibleForTesting;
@@ -36,8 +40,12 @@
import java.lang.annotation.Retention;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.Set;
/**
* Collect Keyboard metrics
@@ -50,13 +58,14 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@Retention(SOURCE)
- @IntDef(prefix = { "LAYOUT_SELECTION_CRITERIA_" }, value = {
+ @IntDef(prefix = {"LAYOUT_SELECTION_CRITERIA_"}, value = {
LAYOUT_SELECTION_CRITERIA_USER,
LAYOUT_SELECTION_CRITERIA_DEVICE,
LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
LAYOUT_SELECTION_CRITERIA_DEFAULT
})
- public @interface LayoutSelectionCriteria {}
+ public @interface LayoutSelectionCriteria {
+ }
/** Manual selection by user */
public static final int LAYOUT_SELECTION_CRITERIA_USER = 0;
@@ -76,17 +85,301 @@
@VisibleForTesting
static final String DEFAULT_LANGUAGE_TAG = "None";
+ public enum KeyboardLogEvent {
+ UNSPECIFIED(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED,
+ "INVALID_KEYBOARD_EVENT"),
+ HOME(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME,
+ "HOME"),
+ RECENT_APPS(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RECENT_APPS,
+ "RECENT_APPS"),
+ BACK(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BACK,
+ "BACK"),
+ APP_SWITCH(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__APP_SWITCH,
+ "APP_SWITCH"),
+ LAUNCH_ASSISTANT(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_ASSISTANT,
+ "LAUNCH_ASSISTANT"),
+ LAUNCH_VOICE_ASSISTANT(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_VOICE_ASSISTANT,
+ "LAUNCH_VOICE_ASSISTANT"),
+ LAUNCH_SYSTEM_SETTINGS(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SYSTEM_SETTINGS,
+ "LAUNCH_SYSTEM_SETTINGS"),
+ TOGGLE_NOTIFICATION_PANEL(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_NOTIFICATION_PANEL,
+ "TOGGLE_NOTIFICATION_PANEL"),
+ TOGGLE_TASKBAR(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_TASKBAR,
+ "TOGGLE_TASKBAR"),
+ TAKE_SCREENSHOT(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TAKE_SCREENSHOT,
+ "TAKE_SCREENSHOT"),
+ OPEN_SHORTCUT_HELPER(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_SHORTCUT_HELPER,
+ "OPEN_SHORTCUT_HELPER"),
+ BRIGHTNESS_UP(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_UP,
+ "BRIGHTNESS_UP"),
+ BRIGHTNESS_DOWN(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_DOWN,
+ "BRIGHTNESS_DOWN"),
+ KEYBOARD_BACKLIGHT_UP(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_UP,
+ "KEYBOARD_BACKLIGHT_UP"),
+ KEYBOARD_BACKLIGHT_DOWN(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_DOWN,
+ "KEYBOARD_BACKLIGHT_DOWN"),
+ KEYBOARD_BACKLIGHT_TOGGLE(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_TOGGLE,
+ "KEYBOARD_BACKLIGHT_TOGGLE"),
+ VOLUME_UP(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_UP,
+ "VOLUME_UP"),
+ VOLUME_DOWN(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_DOWN,
+ "VOLUME_DOWN"),
+ VOLUME_MUTE(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_MUTE,
+ "VOLUME_MUTE"),
+ ALL_APPS(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ALL_APPS,
+ "ALL_APPS"),
+ LAUNCH_SEARCH(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SEARCH,
+ "LAUNCH_SEARCH"),
+ LANGUAGE_SWITCH(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LANGUAGE_SWITCH,
+ "LANGUAGE_SWITCH"),
+ ACCESSIBILITY_ALL_APPS(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ACCESSIBILITY_ALL_APPS,
+ "ACCESSIBILITY_ALL_APPS"),
+ TOGGLE_CAPS_LOCK(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_CAPS_LOCK,
+ "TOGGLE_CAPS_LOCK"),
+ SYSTEM_MUTE(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_MUTE,
+ "SYSTEM_MUTE"),
+ SPLIT_SCREEN_NAVIGATION(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SPLIT_SCREEN_NAVIGATION,
+ "SPLIT_SCREEN_NAVIGATION"),
+ TRIGGER_BUG_REPORT(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TRIGGER_BUG_REPORT,
+ "TRIGGER_BUG_REPORT"),
+ LOCK_SCREEN(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LOCK_SCREEN,
+ "LOCK_SCREEN"),
+ OPEN_NOTES(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_NOTES,
+ "OPEN_NOTES"),
+ TOGGLE_POWER(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_POWER,
+ "TOGGLE_POWER"),
+ SYSTEM_NAVIGATION(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_NAVIGATION,
+ "SYSTEM_NAVIGATION"),
+ SLEEP(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SLEEP,
+ "SLEEP"),
+ WAKEUP(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__WAKEUP,
+ "WAKEUP"),
+ MEDIA_KEY(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MEDIA_KEY,
+ "MEDIA_KEY"),
+ LAUNCH_DEFAULT_BROWSER(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_BROWSER,
+ "LAUNCH_DEFAULT_BROWSER"),
+ LAUNCH_DEFAULT_EMAIL(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_EMAIL,
+ "LAUNCH_DEFAULT_EMAIL"),
+ LAUNCH_DEFAULT_CONTACTS(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CONTACTS,
+ "LAUNCH_DEFAULT_CONTACTS"),
+ LAUNCH_DEFAULT_CALENDAR(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALENDAR,
+ "LAUNCH_DEFAULT_CALENDAR"),
+ LAUNCH_DEFAULT_CALCULATOR(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALCULATOR,
+ "LAUNCH_DEFAULT_CALCULATOR"),
+ LAUNCH_DEFAULT_MUSIC(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MUSIC,
+ "LAUNCH_DEFAULT_MUSIC"),
+ LAUNCH_DEFAULT_MAPS(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MAPS,
+ "LAUNCH_DEFAULT_MAPS"),
+ LAUNCH_DEFAULT_MESSAGING(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MESSAGING,
+ "LAUNCH_DEFAULT_MESSAGING"),
+ LAUNCH_DEFAULT_GALLERY(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_GALLERY,
+ "LAUNCH_DEFAULT_GALLERY"),
+ LAUNCH_DEFAULT_FILES(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FILES,
+ "LAUNCH_DEFAULT_FILES"),
+ LAUNCH_DEFAULT_WEATHER(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_WEATHER,
+ "LAUNCH_DEFAULT_WEATHER"),
+ LAUNCH_DEFAULT_FITNESS(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FITNESS,
+ "LAUNCH_DEFAULT_FITNESS"),
+ LAUNCH_APPLICATION_BY_PACKAGE_NAME(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME,
+ "LAUNCH_APPLICATION_BY_PACKAGE_NAME");
+
+ private final int mValue;
+ private final String mName;
+
+ private static final SparseArray<KeyboardLogEvent> VALUE_TO_ENUM_MAP = new SparseArray<>();
+
+ static {
+ for (KeyboardLogEvent type : KeyboardLogEvent.values()) {
+ VALUE_TO_ENUM_MAP.put(type.mValue, type);
+ }
+ }
+
+ KeyboardLogEvent(int enumValue, String enumName) {
+ mValue = enumValue;
+ mName = enumName;
+ }
+
+ public int getIntValue() {
+ return mValue;
+ }
+
+ /**
+ * Convert int value to corresponding KeyboardLogEvent enum. If can't find any matching
+ * value will return {@code null}
+ */
+ @Nullable
+ public static KeyboardLogEvent from(int value) {
+ return VALUE_TO_ENUM_MAP.get(value);
+ }
+
+ /**
+ * Find KeyboardLogEvent corresponding to volume up/down/mute key events.
+ */
+ @Nullable
+ public static KeyboardLogEvent getVolumeEvent(int keycode) {
+ switch (keycode) {
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
+ return VOLUME_DOWN;
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ return VOLUME_UP;
+ case KeyEvent.KEYCODE_VOLUME_MUTE:
+ return VOLUME_MUTE;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Find KeyboardLogEvent corresponding to brightness up/down key events.
+ */
+ @Nullable
+ public static KeyboardLogEvent getBrightnessEvent(int keycode) {
+ switch (keycode) {
+ case KeyEvent.KEYCODE_BRIGHTNESS_DOWN:
+ return BRIGHTNESS_DOWN;
+ case KeyEvent.KEYCODE_BRIGHTNESS_UP:
+ return BRIGHTNESS_UP;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Find KeyboardLogEvent corresponding to intent filter category. Returns
+ * {@code null if no matching event found}
+ */
+ @Nullable
+ public static KeyboardLogEvent getLogEventFromIntent(Intent intent) {
+ Intent selectorIntent = intent.getSelector();
+ if (selectorIntent != null) {
+ Set<String> selectorCategories = selectorIntent.getCategories();
+ if (selectorCategories != null && !selectorCategories.isEmpty()) {
+ for (String intentCategory : selectorCategories) {
+ KeyboardLogEvent logEvent = getEventFromSelectorCategory(intentCategory);
+ if (logEvent == null) {
+ continue;
+ }
+ return logEvent;
+ }
+ }
+ }
+
+ Set<String> intentCategories = intent.getCategories();
+ if (intentCategories == null || intentCategories.isEmpty()
+ || !intentCategories.contains(Intent.CATEGORY_LAUNCHER)) {
+ return null;
+ }
+ if (intent.getComponent() == null) {
+ return null;
+ }
+
+ // TODO(b/280423320): Add new field package name associated in the
+ // KeyboardShortcutEvent atom and log it accordingly.
+ return LAUNCH_APPLICATION_BY_PACKAGE_NAME;
+ }
+
+ @Nullable
+ private static KeyboardLogEvent getEventFromSelectorCategory(String category) {
+ switch (category) {
+ case Intent.CATEGORY_APP_BROWSER:
+ return LAUNCH_DEFAULT_BROWSER;
+ case Intent.CATEGORY_APP_EMAIL:
+ return LAUNCH_DEFAULT_EMAIL;
+ case Intent.CATEGORY_APP_CONTACTS:
+ return LAUNCH_DEFAULT_CONTACTS;
+ case Intent.CATEGORY_APP_CALENDAR:
+ return LAUNCH_DEFAULT_CALENDAR;
+ case Intent.CATEGORY_APP_CALCULATOR:
+ return LAUNCH_DEFAULT_CALCULATOR;
+ case Intent.CATEGORY_APP_MUSIC:
+ return LAUNCH_DEFAULT_MUSIC;
+ case Intent.CATEGORY_APP_MAPS:
+ return LAUNCH_DEFAULT_MAPS;
+ case Intent.CATEGORY_APP_MESSAGING:
+ return LAUNCH_DEFAULT_MESSAGING;
+ case Intent.CATEGORY_APP_GALLERY:
+ return LAUNCH_DEFAULT_GALLERY;
+ case Intent.CATEGORY_APP_FILES:
+ return LAUNCH_DEFAULT_FILES;
+ case Intent.CATEGORY_APP_WEATHER:
+ return LAUNCH_DEFAULT_WEATHER;
+ case Intent.CATEGORY_APP_FITNESS:
+ return LAUNCH_DEFAULT_FITNESS;
+ default:
+ return null;
+ }
+ }
+ }
+
/**
* Log keyboard system shortcuts for the proto
* {@link com.android.os.input.KeyboardSystemsEventReported}
* defined in "stats/atoms/input/input_extension_atoms.proto"
*/
- public static void logKeyboardSystemsEventReportedAtom(InputDevice inputDevice,
- int keyboardSystemEvent, int[] keyCode, int modifierState) {
+ public static void logKeyboardSystemsEventReportedAtom(@Nullable InputDevice inputDevice,
+ @Nullable KeyboardLogEvent keyboardSystemEvent, int modifierState, int... keyCodes) {
+ // Logging Keyboard system event only for an external HW keyboard. We should not log events
+ // for virtual keyboards or internal Key events.
+ if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
+ return;
+ }
int vendorId = inputDevice.getVendorId();
int productId = inputDevice.getProductId();
+ if (keyboardSystemEvent == null) {
+ Slog.w(TAG, "Invalid keyboard event logging, keycode = " + Arrays.toString(keyCodes)
+ + ", modifier state = " + modifierState);
+ return;
+ }
FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED,
- vendorId, productId, keyboardSystemEvent, keyCode, modifierState);
+ vendorId, productId, keyboardSystemEvent.getIntValue(), keyCodes, modifierState);
+
+ if (DEBUG) {
+ Slog.d(TAG, "Logging Keyboard system event: " + keyboardSystemEvent.mName);
+ }
}
/**
@@ -94,8 +387,8 @@
* {@link com.android.os.input.KeyboardConfigured} atom
*
* @param event {@link KeyboardConfigurationEvent} contains information about keyboard
- * configuration. Use {@link KeyboardConfigurationEvent.Builder} to create the
- * configuration event to log.
+ * configuration. Use {@link KeyboardConfigurationEvent.Builder} to create the
+ * configuration event to log.
*/
public static void logKeyboardConfiguredAtom(KeyboardConfigurationEvent event) {
// Creating proto to log nested field KeyboardLayoutConfig in atom
@@ -241,7 +534,7 @@
KeyboardLayout selectedLayout = mSelectedLayoutList.get(i);
@LayoutSelectionCriteria int layoutSelectionCriteria =
mLayoutSelectionCriteriaList.get(i);
- InputMethodSubtype imeSubtype = mImeSubtypeList.get(i);
+ InputMethodSubtype imeSubtype = mImeSubtypeList.get(i);
String keyboardLanguageTag = mInputDevice.getKeyboardLanguageTag();
keyboardLanguageTag = keyboardLanguageTag == null ? DEFAULT_LANGUAGE_TAG
: keyboardLanguageTag;
@@ -328,4 +621,3 @@
|| layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEFAULT;
}
}
-
diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
index a1b67e1..f1698dd 100644
--- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
@@ -37,6 +37,7 @@
import android.util.EventLog;
import android.util.Slog;
import android.view.inputmethod.ImeTracker;
+import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.annotations.GuardedBy;
@@ -75,7 +76,8 @@
@GuardedBy("ImfLock.class")
@Override
public void performShowIme(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
- int showFlags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ @InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {
final IInputMethodInvoker curMethod = mService.getCurMethodLocked();
if (curMethod != null) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index c53f1a5..b12a816 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -30,6 +30,7 @@
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputBinding;
+import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSubtype;
import android.window.ImeOnBackInvokedDispatcher;
@@ -198,8 +199,8 @@
// TODO(b/192412909): Convert this back to void method
@AnyThread
- boolean showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken, int flags,
- ResultReceiver resultReceiver) {
+ boolean showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
+ @InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
try {
mTarget.showSoftInput(showInputToken, statsToken, flags, resultReceiver);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
index 27f6a89..29fa369 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
@@ -21,6 +21,7 @@
import android.os.IBinder;
import android.os.ResultReceiver;
import android.view.inputmethod.ImeTracker;
+import android.view.inputmethod.InputMethod;
import com.android.internal.inputmethod.SoftInputShowHideReason;
@@ -34,13 +35,13 @@
*
* @param showInputToken A token that represents the requester to show IME.
* @param statsToken A token that tracks the progress of an IME request.
- * @param showFlags Provides additional operating flags to show IME.
* @param resultReceiver If non-null, this will be called back to the caller when
* it has processed request to tell what it has done.
* @param reason The reason for requesting to show IME.
*/
default void performShowIme(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
- int showFlags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {}
+ @InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {}
/**
* Performs hiding IME to the given window
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
index f012d91..9ad4628 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
@@ -221,17 +221,21 @@
/**
* Called when {@link InputMethodManagerService} is processing the show IME request.
- * @param statsToken The token for tracking this show request
- * @param showFlags The additional operation flags to indicate whether this show request mode is
- * implicit or explicit.
- * @return {@code true} when the computer has proceed this show request operation.
+ *
+ * @param statsToken The token for tracking this show request.
+ * @return {@code true} when the show request can proceed.
*/
- boolean onImeShowFlags(@NonNull ImeTracker.Token statsToken, int showFlags) {
+ boolean onImeShowFlags(@NonNull ImeTracker.Token statsToken,
+ @InputMethodManager.ShowFlags int showFlags) {
if (mPolicy.mA11yRequestingNoSoftKeyboard || mPolicy.mImeHiddenByDisplayPolicy) {
ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY);
return false;
}
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY);
+ // We only "set" the state corresponding to the flags, as this will be reset
+ // in clearImeShowFlags during a hide request.
+ // Thus, we keep the strongest values set (e.g. an implicit show right after
+ // an explicit show will still be considered explicit, likewise for forced).
if ((showFlags & InputMethodManager.SHOW_FORCED) != 0) {
mRequestedShowExplicitly = true;
mShowForced = true;
@@ -243,12 +247,12 @@
/**
* Called when {@link InputMethodManagerService} is processing the hide IME request.
- * @param statsToken The token for tracking this hide request
- * @param hideFlags The additional operation flags to indicate whether this hide request mode is
- * implicit or explicit.
- * @return {@code true} when the computer has proceed this hide request operations.
+ *
+ * @param statsToken The token for tracking this hide request.
+ * @return {@code true} when the hide request can proceed.
*/
- boolean canHideIme(@NonNull ImeTracker.Token statsToken, int hideFlags) {
+ boolean canHideIme(@NonNull ImeTracker.Token statsToken,
+ @InputMethodManager.HideFlags int hideFlags) {
if ((hideFlags & InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
&& (mRequestedShowExplicitly || mShowForced)) {
if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
@@ -264,13 +268,31 @@
return true;
}
- int getImeShowFlags() {
+ /**
+ * Returns the show flags for IME. This translates from {@link InputMethodManager.ShowFlags}
+ * to {@link InputMethod.ShowFlags}.
+ */
+ @InputMethod.ShowFlags
+ int getShowFlagsForInputMethodServiceOnly() {
int flags = 0;
if (mShowForced) {
flags |= InputMethod.SHOW_FORCED | InputMethod.SHOW_EXPLICIT;
} else if (mRequestedShowExplicitly) {
flags |= InputMethod.SHOW_EXPLICIT;
- } else {
+ }
+ return flags;
+ }
+
+ /**
+ * Returns the show flags for IMM. This translates from {@link InputMethod.ShowFlags}
+ * to {@link InputMethodManager.ShowFlags}.
+ */
+ @InputMethodManager.ShowFlags
+ int getShowFlags() {
+ int flags = 0;
+ if (mShowForced) {
+ flags |= InputMethodManager.SHOW_FORCED;
+ } else if (!mRequestedShowExplicitly) {
flags |= InputMethodManager.SHOW_IMPLICIT;
}
return flags;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 7bda2c1..c5fbcb9 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2468,7 +2468,7 @@
final ImeTracker.Token statsToken = mCurStatsToken;
mCurStatsToken = null;
showCurrentInputLocked(mCurFocusedWindow, statsToken,
- mVisibilityStateComputer.getImeShowFlags(),
+ mVisibilityStateComputer.getShowFlags(),
null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT);
}
@@ -3404,8 +3404,9 @@
@Override
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
- @Nullable ImeTracker.Token statsToken, int flags, int lastClickTooType,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+ int lastClickTooType, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput");
int uid = Binder.getCallingUid();
ImeTracing.getInstance().triggerManagerServiceDump(
@@ -3578,15 +3579,17 @@
@GuardedBy("ImfLock.class")
boolean showCurrentInputLocked(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
- int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ @InputMethodManager.ShowFlags int flags, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {
return showCurrentInputLocked(windowToken, statsToken, flags,
MotionEvent.TOOL_TYPE_UNKNOWN, resultReceiver, reason);
}
@GuardedBy("ImfLock.class")
private boolean showCurrentInputLocked(IBinder windowToken,
- @Nullable ImeTracker.Token statsToken, int flags, int lastClickToolType,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+ int lastClickToolType, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {
// Create statsToken is none exists.
if (statsToken == null) {
statsToken = createStatsTokenForFocusedClient(true /* show */,
@@ -3617,7 +3620,8 @@
curMethod.updateEditorToolType(lastClickToolType);
}
mVisibilityApplier.performShowIme(windowToken, statsToken,
- mVisibilityStateComputer.getImeShowFlags(), resultReceiver, reason);
+ mVisibilityStateComputer.getShowFlagsForInputMethodServiceOnly(),
+ resultReceiver, reason);
mVisibilityStateComputer.setInputShown(true);
return true;
} else {
@@ -3629,8 +3633,8 @@
@Override
public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
- @Nullable ImeTracker.Token statsToken, int flags, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
int uid = Binder.getCallingUid();
ImeTracing.getInstance().triggerManagerServiceDump(
"InputMethodManagerService#hideSoftInput");
@@ -3660,7 +3664,8 @@
@GuardedBy("ImfLock.class")
boolean hideCurrentInputLocked(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
- int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ @InputMethodManager.HideFlags int flags, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {
// Create statsToken is none exists.
if (statsToken == null) {
statsToken = createStatsTokenForFocusedClient(false /* show */,
@@ -4847,7 +4852,7 @@
}
@BinderThread
- private void hideMySoftInput(@NonNull IBinder token, int flags,
+ private void hideMySoftInput(@NonNull IBinder token, @InputMethodManager.HideFlags int flags,
@SoftInputShowHideReason int reason) {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput");
@@ -4869,7 +4874,7 @@
}
@BinderThread
- private void showMySoftInput(@NonNull IBinder token, int flags) {
+ private void showMySoftInput(@NonNull IBinder token, @InputMethodManager.ShowFlags int flags) {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showMySoftInput");
synchronized (ImfLock.class) {
@@ -6828,8 +6833,8 @@
@BinderThread
@Override
- public void hideMySoftInput(int flags, @SoftInputShowHideReason int reason,
- AndroidFuture future /* T=Void */) {
+ public void hideMySoftInput(@InputMethodManager.HideFlags int flags,
+ @SoftInputShowHideReason int reason, AndroidFuture future /* T=Void */) {
@SuppressWarnings("unchecked")
final AndroidFuture<Void> typedFuture = future;
try {
@@ -6842,7 +6847,8 @@
@BinderThread
@Override
- public void showMySoftInput(int flags, AndroidFuture future /* T=Void */) {
+ public void showMySoftInput(@InputMethodManager.ShowFlags int flags,
+ AndroidFuture future /* T=Void */) {
@SuppressWarnings("unchecked")
final AndroidFuture<Void> typedFuture = future;
try {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ecb21d0..b3ef869 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -118,7 +118,6 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.EventLog;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.Slog;
@@ -836,9 +835,6 @@
@Override // binder interface
public void systemReady() {
- if (mContext.checkCallingOrSelfPermission(PERMISSION) != PERMISSION_GRANTED) {
- EventLog.writeEvent(0x534e4554, "28251513", getCallingUid(), ""); // SafetyNet
- }
checkWritePermission();
mHasSecureLockScreen = mContext.getPackageManager()
@@ -1093,9 +1089,6 @@
}
private final void checkPasswordHavePermission() {
- if (mContext.checkCallingOrSelfPermission(PERMISSION) != PERMISSION_GRANTED) {
- EventLog.writeEvent(0x534e4554, "28251513", getCallingUid(), ""); // SafetyNet
- }
mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsHave");
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2db724f..9be86e8 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -118,7 +118,6 @@
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
-import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ALLOW_DISMISS_ONGOING;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.WAKE_LOCK_FOR_POSTING_NOTIFICATION;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
@@ -1221,8 +1220,7 @@
}
}
- int mustNotHaveFlags = mFlagResolver.isEnabled(ALLOW_DISMISS_ONGOING)
- ? FLAG_NO_DISMISS : FLAG_ONGOING_EVENT;
+ int mustNotHaveFlags = FLAG_NO_DISMISS;
cancelNotification(callingUid, callingPid, pkg, tag, id,
/* mustHaveFlags= */ 0,
/* mustNotHaveFlags= */ mustNotHaveFlags,
@@ -2491,16 +2489,6 @@
getContext().registerReceiver(mReviewNotificationPermissionsReceiver,
ReviewNotificationPermissionsReceiver.getFilter(),
Context.RECEIVER_NOT_EXPORTED);
-
- mAppOps.startWatchingMode(AppOpsManager.OP_POST_NOTIFICATION, null,
- new AppOpsManager.OnOpChangedInternalListener() {
- @Override
- public void onOpChanged(@NonNull String op, @NonNull String packageName,
- int userId) {
- mHandler.post(
- () -> handleNotificationPermissionChange(packageName, userId));
- }
- });
}
/**
@@ -3560,16 +3548,20 @@
}
mPermissionHelper.setNotificationPermission(
pkg, UserHandle.getUserId(uid), enabled, true);
+ sendAppBlockStateChangedBroadcast(pkg, uid, !enabled);
mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_BAN_APP_NOTES)
.setType(MetricsEvent.TYPE_ACTION)
.setPackageName(pkg)
.setSubtype(enabled ? 1 : 0));
mNotificationChannelLogger.logAppNotificationsAllowed(uid, pkg, enabled);
+ // Now, cancel any outstanding notifications that are part of a just-disabled app
+ if (!enabled) {
+ cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0,
+ UserHandle.getUserId(uid), REASON_PACKAGE_BANNED);
+ }
- // Outstanding notifications from this package will be cancelled, and the package will
- // be sent the ACTION_APP_BLOCK_STATE_CHANGED broadcast, as soon as we get the
- // callback from AppOpsManager.
+ handleSavePolicyFile();
}
/**
@@ -5889,21 +5881,6 @@
}
};
- private void handleNotificationPermissionChange(String pkg, @UserIdInt int userId) {
- int uid = mPackageManagerInternal.getPackageUid(pkg, 0, userId);
- if (uid == INVALID_UID) {
- Log.e(TAG, String.format("No uid found for %s, %s!", pkg, userId));
- return;
- }
- boolean hasPermission = mPermissionHelper.hasPermission(uid);
- sendAppBlockStateChangedBroadcast(pkg, uid, !hasPermission);
- if (!hasPermission) {
- cancelAllNotificationsInt(MY_UID, MY_PID, pkg, /* channelId= */ null,
- /* mustHaveFlags= */ 0, /* mustNotHaveFlags= */ 0, userId,
- REASON_PACKAGE_BANNED);
- }
- }
-
protected void checkNotificationListenerAccess() {
if (!isCallerSystemOrPhone()) {
getContext().enforceCallingPermission(
@@ -6883,13 +6860,11 @@
}
// Only notifications that can be non-dismissible can have the flag FLAG_NO_DISMISS
- if (mFlagResolver.isEnabled(ALLOW_DISMISS_ONGOING)) {
- if (((notification.flags & FLAG_ONGOING_EVENT) > 0)
- && canBeNonDismissible(ai, notification)) {
- notification.flags |= FLAG_NO_DISMISS;
- } else {
- notification.flags &= ~FLAG_NO_DISMISS;
- }
+ if (((notification.flags & FLAG_ONGOING_EVENT) > 0)
+ && canBeNonDismissible(ai, notification)) {
+ notification.flags |= FLAG_NO_DISMISS;
+ } else {
+ notification.flags &= ~FLAG_NO_DISMISS;
}
int canColorize = getContext().checkPermission(
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 36a0b0c..1f5bd3e 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -75,6 +75,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import com.android.internal.logging.MetricsLogger;
@@ -108,30 +109,34 @@
static final int RULE_LIMIT_PER_PACKAGE = 100;
// pkg|userId => uid
- protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
+ @VisibleForTesting protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
private final Context mContext;
private final H mHandler;
private final SettingsObserver mSettingsObserver;
private final AppOpsManager mAppOps;
- @VisibleForTesting protected final NotificationManager mNotificationManager;
+ private final NotificationManager mNotificationManager;
private final SysUiStatsEvent.BuilderFactory mStatsEventBuilderFactory;
- @VisibleForTesting protected ZenModeConfig mDefaultConfig;
+ private ZenModeConfig mDefaultConfig;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final ZenModeFiltering mFiltering;
- protected final RingerModeDelegate mRingerModeDelegate = new
+ private final RingerModeDelegate mRingerModeDelegate = new
RingerModeDelegate();
@VisibleForTesting protected final ZenModeConditions mConditions;
- Object mConfigsLock = new Object();
+ private final Object mConfigsArrayLock = new Object();
+ @GuardedBy("mConfigsArrayLock")
@VisibleForTesting final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>();
private final Metrics mMetrics = new Metrics();
private final ConditionProviders.Config mServiceConfig;
- private SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
- @VisibleForTesting protected ZenModeEventLogger mZenModeEventLogger;
+ private final SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
+ private final ZenModeEventLogger mZenModeEventLogger;
@VisibleForTesting protected int mZenMode;
@VisibleForTesting protected NotificationManager.Policy mConsolidatedPolicy;
private int mUser = UserHandle.USER_SYSTEM;
+
+ private final Object mConfigLock = new Object();
+ @GuardedBy("mConfigLock")
@VisibleForTesting protected ZenModeConfig mConfig;
@VisibleForTesting protected AudioManagerInternal mAudioManager;
protected PackageManager mPm;
@@ -159,7 +164,7 @@
mDefaultConfig = readDefaultConfig(mContext.getResources());
updateDefaultAutomaticRuleNames();
mConfig = mDefaultConfig.copy();
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
}
mConsolidatedPolicy = mConfig.toNotificationPolicy();
@@ -186,7 +191,7 @@
public boolean matchesCallFilter(UserHandle userHandle, Bundle extras,
ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity,
int callingUid) {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConsolidatedPolicy,
userHandle, extras, validator, contactsTimeoutMs, timeoutAffinity,
callingUid);
@@ -206,7 +211,7 @@
}
public boolean shouldIntercept(NotificationRecord record) {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
return mFiltering.shouldIntercept(mZenMode, mConsolidatedPolicy, record);
}
}
@@ -221,7 +226,7 @@
public void initZenMode() {
if (DEBUG) Log.d(TAG, "initZenMode");
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
// "update" config to itself, which will have no effect in the case where a config
// was read in via XML, but will initialize zen mode if nothing was read in and the
// config remains the default.
@@ -250,7 +255,7 @@
public void onUserRemoved(int user) {
if (user < UserHandle.USER_SYSTEM) return;
if (DEBUG) Log.d(TAG, "onUserRemoved u=" + user);
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.remove(user);
}
}
@@ -268,7 +273,7 @@
mUser = user;
if (DEBUG) Log.d(TAG, reason + " u=" + user);
ZenModeConfig config = null;
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
if (mConfigs.get(user) != null) {
config = mConfigs.get(user).copy();
}
@@ -278,7 +283,7 @@
config = mDefaultConfig.copy();
config.user = user;
}
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
setConfigLocked(config, null, reason, Process.SYSTEM_UID, true);
}
cleanUpZenRules();
@@ -314,7 +319,7 @@
public List<ZenRule> getZenRules() {
List<ZenRule> rules = new ArrayList<>();
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return rules;
for (ZenRule rule : mConfig.automaticRules.values()) {
if (canManageAutomaticZenRule(rule)) {
@@ -327,7 +332,7 @@
public AutomaticZenRule getAutomaticZenRule(String id) {
ZenRule rule;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return null;
rule = mConfig.automaticRules.get(id);
}
@@ -364,7 +369,7 @@
}
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) {
throw new AndroidRuntimeException("Could not create rule");
}
@@ -387,7 +392,7 @@
public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule,
String reason, int callingUid, boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return false;
if (DEBUG) {
Log.d(TAG, "updateAutomaticZenRule zenRule=" + automaticZenRule
@@ -419,7 +424,7 @@
public boolean removeAutomaticZenRule(String id, String reason, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return false;
newConfig = mConfig.copy();
ZenRule ruleToRemove = newConfig.automaticRules.get(id);
@@ -450,7 +455,7 @@
public boolean removeAutomaticZenRules(String packageName, String reason, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return false;
newConfig = mConfig.copy();
for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
@@ -467,7 +472,7 @@
public void setAutomaticZenRuleState(String id, Condition condition, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return;
newConfig = mConfig.copy();
@@ -481,7 +486,7 @@
public void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return;
newConfig = mConfig.copy();
@@ -491,6 +496,7 @@
}
}
+ @GuardedBy("mConfigLock")
private void setAutomaticZenRuleStateLocked(ZenModeConfig config, List<ZenRule> rules,
Condition condition, int callingUid, boolean fromSystemOrSystemUi) {
if (rules == null || rules.isEmpty()) return;
@@ -538,7 +544,7 @@
return 0;
}
int count = 0;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
for (ZenRule rule : mConfig.automaticRules.values()) {
if (cn.equals(rule.component) || cn.equals(rule.configurationActivity)) {
count++;
@@ -555,7 +561,7 @@
return 0;
}
int count = 0;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
for (ZenRule rule : mConfig.automaticRules.values()) {
if (pkg.equals(rule.getPkg())) {
count++;
@@ -588,19 +594,23 @@
protected void updateDefaultZenRules(int callingUid, boolean fromSystemOrSystemUi) {
updateDefaultAutomaticRuleNames();
- for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
- ZenRule currRule = mConfig.automaticRules.get(defaultRule.id);
- // if default rule wasn't user-modified nor enabled, use localized name
- // instead of previous system name
- if (currRule != null && !currRule.modified && !currRule.enabled
- && !defaultRule.name.equals(currRule.name)) {
- if (canManageAutomaticZenRule(currRule)) {
- if (DEBUG) Slog.d(TAG, "Locale change - updating default zen rule name "
- + "from " + currRule.name + " to " + defaultRule.name);
- // update default rule (if locale changed, name of rule will change)
- currRule.name = defaultRule.name;
- updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
- "locale changed", callingUid, fromSystemOrSystemUi);
+ synchronized (mConfigLock) {
+ for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
+ ZenRule currRule = mConfig.automaticRules.get(defaultRule.id);
+ // if default rule wasn't user-modified nor enabled, use localized name
+ // instead of previous system name
+ if (currRule != null && !currRule.modified && !currRule.enabled
+ && !defaultRule.name.equals(currRule.name)) {
+ if (canManageAutomaticZenRule(currRule)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Locale change - updating default zen rule name "
+ + "from " + currRule.name + " to " + defaultRule.name);
+ }
+ // update default rule (if locale changed, name of rule will change)
+ currRule.name = defaultRule.name;
+ updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
+ "locale changed", callingUid, fromSystemOrSystemUi);
+ }
}
}
}
@@ -686,7 +696,7 @@
private void setManualZenMode(int zenMode, Uri conditionId, String reason, String caller,
boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return;
if (!Global.isValidZenMode(zenMode)) return;
if (DEBUG) Log.d(TAG, "setManualZenMode " + Global.zenModeToString(zenMode)
@@ -715,7 +725,7 @@
void dump(ProtoOutputStream proto) {
proto.write(ZenModeProto.ZEN_MODE, mZenMode);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig.manualRule != null) {
mConfig.manualRule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS);
}
@@ -737,14 +747,14 @@
pw.println(Global.zenModeToString(mZenMode));
pw.print(prefix);
pw.println("mConsolidatedPolicy=" + mConsolidatedPolicy.toString());
- synchronized(mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
final int N = mConfigs.size();
for (int i = 0; i < N; i++) {
dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
}
}
pw.print(prefix); pw.print("mUser="); pw.println(mUser);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
dump(pw, prefix, "mConfig", mConfig);
}
@@ -833,7 +843,7 @@
Settings.Secure.ZEN_SETTINGS_UPDATED, 1, userId);
}
if (DEBUG) Log.d(TAG, reason);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
setConfigLocked(config, null, reason, Process.SYSTEM_UID, true);
}
}
@@ -841,7 +851,7 @@
public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId)
throws IOException {
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
final int n = mConfigs.size();
for (int i = 0; i < n; i++) {
if (forBackup && mConfigs.keyAt(i) != userId) {
@@ -856,7 +866,9 @@
* @return user-specified default notification policy for priority only do not disturb
*/
public Policy getNotificationPolicy() {
- return getNotificationPolicy(mConfig);
+ synchronized (mConfigLock) {
+ return getNotificationPolicy(mConfig);
+ }
}
private static Policy getNotificationPolicy(ZenModeConfig config) {
@@ -867,8 +879,8 @@
* Sets the global notification policy used for priority only do not disturb
*/
public void setNotificationPolicy(Policy policy, int callingUid, boolean fromSystemOrSystemUi) {
- if (policy == null || mConfig == null) return;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
+ if (policy == null || mConfig == null) return;
final ZenModeConfig newConfig = mConfig.copy();
newConfig.applyNotificationPolicy(policy);
setConfigLocked(newConfig, null, "setNotificationPolicy", callingUid,
@@ -881,7 +893,7 @@
*/
private void cleanUpZenRules() {
long currentTime = System.currentTimeMillis();
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
final ZenModeConfig newConfig = mConfig.copy();
if (newConfig.automaticRules != null) {
for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
@@ -906,7 +918,7 @@
* @return a copy of the zen mode configuration
*/
public ZenModeConfig getConfig() {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
return mConfig.copy();
}
}
@@ -918,7 +930,8 @@
return mConsolidatedPolicy.copy();
}
- public boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
+ @GuardedBy("mConfigLock")
+ private boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
String reason, int callingUid, boolean fromSystemOrSystemUi) {
return setConfigLocked(config, reason, triggeringComponent, true /*setRingerMode*/,
callingUid, fromSystemOrSystemUi);
@@ -926,11 +939,12 @@
public void setConfig(ZenModeConfig config, ComponentName triggeringComponent, String reason,
int callingUid, boolean fromSystemOrSystemUi) {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
setConfigLocked(config, triggeringComponent, reason, callingUid, fromSystemOrSystemUi);
}
}
+ @GuardedBy("mConfigLock")
private boolean setConfigLocked(ZenModeConfig config, String reason,
ComponentName triggeringComponent, boolean setRingerMode, int callingUid,
boolean fromSystemOrSystemUi) {
@@ -942,7 +956,7 @@
}
if (config.user != mUser) {
// simply store away for background users
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.put(config.user, config);
}
if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
@@ -951,7 +965,7 @@
// handle CPS backed conditions - danger! may modify config
mConditions.evaluateConfig(config, null, false /*processSubscriptions*/);
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.put(config.user, config);
}
if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
@@ -979,6 +993,7 @@
* Carries out a config update (if needed) and (re-)evaluates the zen mode value afterwards.
* If logging is enabled, will also request logging of the outcome of this change if needed.
*/
+ @GuardedBy("mConfigLock")
private void updateConfigAndZenModeLocked(ZenModeConfig config, String reason,
boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) {
final boolean logZenModeEvents = mFlagResolver.isEnabled(
@@ -993,7 +1008,7 @@
}
final String val = Integer.toString(config.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
- evaluateZenMode(reason, setRingerMode);
+ evaluateZenModeLocked(reason, setRingerMode);
// After all changes have occurred, log if requested
if (logZenModeEvents) {
ZenModeEventLogger.ZenModeInfo newInfo = new ZenModeEventLogger.ZenModeInfo(
@@ -1025,7 +1040,8 @@
}
@VisibleForTesting
- protected void evaluateZenMode(String reason, boolean setRingerMode) {
+ @GuardedBy("mConfigLock")
+ protected void evaluateZenModeLocked(String reason, boolean setRingerMode) {
if (DEBUG) Log.d(TAG, "evaluateZenMode");
if (mConfig == null) return;
final int policyHashBefore = mConsolidatedPolicy == null ? 0
@@ -1056,8 +1072,8 @@
}
private int computeZenMode() {
- if (mConfig == null) return Global.ZEN_MODE_OFF;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
+ if (mConfig == null) return Global.ZEN_MODE_OFF;
if (mConfig.manualRule != null) return mConfig.manualRule.zenMode;
int zen = Global.ZEN_MODE_OFF;
for (ZenRule automaticRule : mConfig.automaticRules.values()) {
@@ -1094,8 +1110,8 @@
}
private void updateConsolidatedPolicy(String reason) {
- if (mConfig == null) return;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
+ if (mConfig == null) return;
ZenPolicy policy = new ZenPolicy();
if (mConfig.manualRule != null) {
applyCustomPolicy(policy, mConfig.manualRule);
@@ -1293,7 +1309,7 @@
* Generate pulled atoms about do not disturb configurations.
*/
public void pullRules(List<StatsEvent> events) {
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
final int numConfigs = mConfigs.size();
for (int i = 0; i < numConfigs; i++) {
final int user = mConfigs.keyAt(i);
@@ -1319,6 +1335,7 @@
}
}
+ @GuardedBy("mConfigsArrayLock")
private void ruleToProtoLocked(int user, ZenRule rule, boolean isManualRule,
List<StatsEvent> events) {
// Make the ID safe.
@@ -1389,7 +1406,7 @@
if (mZenMode == Global.ZEN_MODE_OFF
|| (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- && !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(mConfig))) {
+ && !areAllPriorityOnlyRingerSoundsMuted())) {
// in priority only with ringer not muted, save ringer mode changes
// in dnd off, save ringer mode changes
setPreviousRingerModeSetting(ringerModeNew);
@@ -1410,8 +1427,7 @@
&& (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
|| mZenMode == Global.ZEN_MODE_ALARMS
|| (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- && ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(
- mConfig)))) {
+ && areAllPriorityOnlyRingerSoundsMuted()))) {
newZen = Global.ZEN_MODE_OFF;
} else if (mZenMode != Global.ZEN_MODE_OFF) {
ringerModeExternalOut = AudioManager.RINGER_MODE_SILENT;
@@ -1430,6 +1446,12 @@
return ringerModeExternalOut;
}
+ private boolean areAllPriorityOnlyRingerSoundsMuted() {
+ synchronized (mConfigLock) {
+ return ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(mConfig);
+ }
+ }
+
@Override
public int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew, String caller,
int ringerModeInternal, VolumePolicy policy) {
@@ -1633,7 +1655,7 @@
private void emitRules() {
final long now = SystemClock.elapsedRealtime();
final long since = (now - mRuleCountLogTime);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
int numZenRules = mConfig.automaticRules.size();
if (mNumZenRules != numZenRules
|| since > MINIMUM_LOG_PERIOD_MS) {
@@ -1651,7 +1673,7 @@
private void emitDndType() {
final long now = SystemClock.elapsedRealtime();
final long since = (now - mTypeLogTimeMs);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
boolean dndOn = mZenMode != Global.ZEN_MODE_OFF;
int zenType = !dndOn ? DND_OFF
: (mConfig.manualRule != null) ? DND_ON_MANUAL : DND_ON_AUTOMATIC;
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 2206eac..26e70c0 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -356,9 +356,13 @@
/**
* Performs a non-staged install of the given {@code apexFile}.
*
+ * If {@code force} is {@code true}, then update is forced even for APEXes that do not support
+ * non-staged update. This feature is only available on debuggable builds to improve development
+ * velocity of the teams that have their code packaged in an APEX.
+ *
* @return {@code ApeInfo} about the newly installed APEX package.
*/
- abstract ApexInfo installPackage(File apexFile) throws PackageManagerException;
+ abstract ApexInfo installPackage(File apexFile, boolean force) throws PackageManagerException;
/**
* Get a list of apex system services implemented in an apex.
@@ -903,10 +907,11 @@
}
@Override
- ApexInfo installPackage(File apexFile)
+ ApexInfo installPackage(File apexFile, boolean force)
throws PackageManagerException {
try {
- return waitForApexService().installAndActivatePackage(apexFile.getAbsolutePath());
+ return waitForApexService().installAndActivatePackage(apexFile.getAbsolutePath(),
+ force);
} catch (RemoteException e) {
throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
"apexservice not available");
diff --git a/services/core/java/com/android/server/pm/ArchiveManager.java b/services/core/java/com/android/server/pm/ArchiveManager.java
index 99479f0..5435206 100644
--- a/services/core/java/com/android/server/pm/ArchiveManager.java
+++ b/services/core/java/com/android/server/pm/ArchiveManager.java
@@ -77,9 +77,8 @@
snapshot.enforceCrossUserPermission(callingUid, userId, true, true,
"archiveApp");
verifyCaller(callerPackageName, callingPackageName);
-
PackageStateInternal ps = getPackageState(packageName, snapshot, callingUid, user);
- verifyInstallOwnership(packageName, callingPackageName, ps.getInstallSource());
+ verifyInstaller(packageName, ps.getInstallSource());
List<LauncherActivityInfo> mainActivities = getLauncherApps().getActivityList(
ps.getPackageName(),
@@ -125,7 +124,7 @@
Path.of("/TODO"), null);
activityInfos.add(activityInfo);
}
- // TODO(b/278553670) Adapt installer check verifyInstallOwnership and check for null there
+
InstallSource installSource = ps.getInstallSource();
String installerPackageName = installSource.mUpdateOwnerPackageName != null
? installSource.mUpdateOwnerPackageName : installSource.mInstallerPackageName;
@@ -159,19 +158,13 @@
}
}
- private static void verifyInstallOwnership(String packageName, String callingPackageName,
- InstallSource installSource) {
- if (!TextUtils.equals(installSource.mInstallerPackageName,
- callingPackageName)) {
+ private static void verifyInstaller(String packageName, InstallSource installSource) {
+ // TODO(b/291060290) Verify installer supports unarchiving
+ if (installSource.mUpdateOwnerPackageName == null
+ && installSource.mInstallerPackageName == null) {
throw new SecurityException(
- TextUtils.formatSimple("Caller is not the installer of record for %s.",
+ TextUtils.formatSimple("No installer found to archive app %s.",
packageName));
}
- String updateOwnerPackageName = installSource.mUpdateOwnerPackageName;
- if (updateOwnerPackageName != null
- && !TextUtils.equals(updateOwnerPackageName, callingPackageName)) {
- throw new SecurityException(
- TextUtils.formatSimple("Caller is not the update owner for %s.", packageName));
- }
}
}
diff --git a/services/core/java/com/android/server/pm/InstallArgs.java b/services/core/java/com/android/server/pm/InstallArgs.java
index 6de7f07..dd96a2b 100644
--- a/services/core/java/com/android/server/pm/InstallArgs.java
+++ b/services/core/java/com/android/server/pm/InstallArgs.java
@@ -43,6 +43,7 @@
final IPackageInstallObserver2 mObserver;
// Always refers to PackageManager flags only
final int mInstallFlags;
+ final int mDevelopmentInstallFlags;
@NonNull
final InstallSource mInstallSource;
final String mVolumeUuid;
@@ -69,8 +70,8 @@
@Nullable String[] mInstructionSets;
InstallArgs(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
- int installFlags, InstallSource installSource, String volumeUuid,
- UserHandle user, String[] instructionSets, String abiOverride,
+ int installFlags, int developmentInstallFlags, InstallSource installSource,
+ String volumeUuid, UserHandle user, String[] instructionSets, String abiOverride,
@NonNull ArrayMap<String, Integer> permissionStates,
List<String> allowlistedRestrictedPermissions,
int autoRevokePermissionsMode, String traceMethod, int traceCookie,
@@ -80,6 +81,7 @@
mOriginInfo = originInfo;
mMoveInfo = moveInfo;
mInstallFlags = installFlags;
+ mDevelopmentInstallFlags = developmentInstallFlags;
mObserver = observer;
mInstallSource = Preconditions.checkNotNull(installSource);
mVolumeUuid = volumeUuid;
@@ -105,7 +107,7 @@
* when cleaning up old installs, or used as a move source.
*/
InstallArgs(String codePath, String[] instructionSets) {
- this(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY, null, null,
+ this(OriginInfo.fromNothing(), null, null, 0, 0, InstallSource.EMPTY, null, null,
instructionSets, null, new ArrayMap<>(), null, MODE_DEFAULT, null, 0,
SigningDetails.UNKNOWN, PackageManager.INSTALL_REASON_UNKNOWN,
PackageManager.INSTALL_SCENARIO_DEFAULT, false, DataLoaderType.NONE,
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index d737cc4..134b041 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -110,6 +110,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.DataLoaderType;
@@ -119,7 +120,6 @@
import android.content.pm.PackageManager;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
-import android.content.pm.ResolveInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
import android.content.pm.SigningDetails;
@@ -184,7 +184,9 @@
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.SharedLibraryWrapper;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
+import com.android.server.pm.pkg.component.ParsedActivity;
import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
import com.android.server.pm.pkg.component.ParsedPermission;
import com.android.server.pm.pkg.component.ParsedPermissionGroup;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
@@ -207,6 +209,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -3936,23 +3939,6 @@
}
}
- // If this is a system app we hadn't seen before, and this is a first boot or OTA,
- // we need to unstop it if it doesn't have a launcher entry.
- if (mPm.mShouldStopSystemPackagesByDefault && scanResult.mRequest.mPkgSetting == null
- && ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0)
- && ((scanFlags & SCAN_AS_SYSTEM) != 0)) {
- final Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
- launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- launcherIntent.setPackage(parsedPackage.getPackageName());
- final List<ResolveInfo> launcherActivities =
- mPm.snapshotComputer().queryIntentActivitiesInternal(launcherIntent, null,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 0);
- if (launcherActivities.isEmpty()) {
- scanResult.mPkgSetting.setStopped(false, 0);
- }
- }
-
if (mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
if (scanResult.mPkgSetting != null && scanResult.mPkgSetting.isLoading()) {
// Continue monitoring loading progress of active incremental packages
@@ -4325,6 +4311,8 @@
// - It's an APEX or overlay package since stopped state does not affect them.
// - It is enumerated with a <initial-package-state> tag having the stopped attribute
// set to false
+ // - It doesn't have an enabled and exported launcher activity, which means the user
+ // wouldn't have a way to un-stop it
final boolean isApexPkg = (scanFlags & SCAN_AS_APEX) != 0;
if (mPm.mShouldStopSystemPackagesByDefault
&& scanSystemPartition
@@ -4333,8 +4321,9 @@
&& !parsedPackage.isOverlayIsStatic()
) {
String packageName = parsedPackage.getPackageName();
- if (!mPm.mInitialNonStoppedSystemPackages.contains(packageName)
- && !"android".contentEquals(packageName)) {
+ if (!"android".contentEquals(packageName)
+ && !mPm.mInitialNonStoppedSystemPackages.contains(packageName)
+ && hasLauncherEntry(parsedPackage)) {
scanFlags |= SCAN_AS_STOPPED_SYSTEM_APP;
}
}
@@ -4344,6 +4333,26 @@
return new Pair<>(scanResult, shouldHideSystemApp);
}
+ private static boolean hasLauncherEntry(ParsedPackage parsedPackage) {
+ final HashSet<String> categories = new HashSet<>();
+ categories.add(Intent.CATEGORY_LAUNCHER);
+ final List<ParsedActivity> activities = parsedPackage.getActivities();
+ for (int indexActivity = 0; indexActivity < activities.size(); indexActivity++) {
+ final ParsedActivity activity = activities.get(indexActivity);
+ if (!activity.isEnabled() || !activity.isExported()) {
+ continue;
+ }
+ final List<ParsedIntentInfo> intents = activity.getIntents();
+ for (int indexIntent = 0; indexIntent < intents.size(); indexIntent++) {
+ final IntentFilter intentFilter = intents.get(indexIntent).getIntentFilter();
+ if (intentFilter != null && intentFilter.matchCategories(categories) == null) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/**
* Returns if forced apk verification can be skipped for the whole package, including splits.
*/
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 3464874..6fc14e8 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -134,12 +134,13 @@
InstallRequest(InstallingSession params) {
mUserId = params.getUser().getIdentifier();
mInstallArgs = new InstallArgs(params.mOriginInfo, params.mMoveInfo, params.mObserver,
- params.mInstallFlags, params.mInstallSource, params.mVolumeUuid,
- params.getUser(), null /*instructionSets*/, params.mPackageAbiOverride,
- params.mPermissionStates, params.mAllowlistedRestrictedPermissions,
- params.mAutoRevokePermissionsMode, params.mTraceMethod, params.mTraceCookie,
- params.mSigningDetails, params.mInstallReason, params.mInstallScenario,
- params.mForceQueryableOverride, params.mDataLoaderType, params.mPackageSource,
+ params.mInstallFlags, params.mDevelopmentInstallFlags, params.mInstallSource,
+ params.mVolumeUuid, params.getUser(), null /*instructionSets*/,
+ params.mPackageAbiOverride, params.mPermissionStates,
+ params.mAllowlistedRestrictedPermissions, params.mAutoRevokePermissionsMode,
+ params.mTraceMethod, params.mTraceCookie, params.mSigningDetails,
+ params.mInstallReason, params.mInstallScenario, params.mForceQueryableOverride,
+ params.mDataLoaderType, params.mPackageSource,
params.mApplicationEnabledSettingPersistent);
mPackageMetrics = new PackageMetrics(this);
mIsInstallInherit = params.mIsInherit;
@@ -286,6 +287,10 @@
return mInstallArgs == null ? 0 : mInstallArgs.mInstallFlags;
}
+ public int getDevelopmentInstallFlags() {
+ return mInstallArgs == null ? 0 : mInstallArgs.mDevelopmentInstallFlags;
+ }
+
public int getInstallReason() {
return mInstallArgs == null ? INSTALL_REASON_UNKNOWN : mInstallArgs.mInstallReason;
}
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index 35862db..30a23bf 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -68,6 +68,7 @@
final MoveInfo mMoveInfo;
final IPackageInstallObserver2 mObserver;
int mInstallFlags;
+ int mDevelopmentInstallFlags;
@NonNull
final InstallSource mInstallSource;
final String mVolumeUuid;
@@ -102,8 +103,8 @@
// For move install
InstallingSession(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
- int installFlags, InstallSource installSource, String volumeUuid,
- UserHandle user, String packageAbiOverride, int packageSource,
+ int installFlags, int developmentInstallFlags, InstallSource installSource,
+ String volumeUuid, UserHandle user, String packageAbiOverride, int packageSource,
PackageLite packageLite, PackageManagerService pm) {
mPm = pm;
mUser = user;
@@ -113,6 +114,7 @@
mMoveInfo = moveInfo;
mObserver = observer;
mInstallFlags = installFlags;
+ mDevelopmentInstallFlags = developmentInstallFlags;
mInstallSource = Preconditions.checkNotNull(installSource);
mVolumeUuid = volumeUuid;
mPackageAbiOverride = packageAbiOverride;
@@ -149,6 +151,7 @@
mInstallScenario = sessionParams.installScenario;
mObserver = observer;
mInstallFlags = sessionParams.installFlags;
+ mDevelopmentInstallFlags = sessionParams.developmentInstallFlags;
mInstallSource = installSource;
mVolumeUuid = sessionParams.volumeUuid;
mPackageAbiOverride = sessionParams.abiOverride;
@@ -592,6 +595,10 @@
"Only a non-staged install of a single APEX is supported");
}
InstallRequest request = requests.get(0);
+ boolean force =
+ (request.getDevelopmentInstallFlags()
+ & PackageManager.INSTALL_DEVELOPMENT_FORCE_NON_STAGED_APEX_UPDATE)
+ != 0;
try {
// Should directory scanning logic be moved to ApexManager for better test coverage?
final File dir = request.getOriginInfo().mResolvedFile;
@@ -608,7 +615,7 @@
PackageManagerException.INTERNAL_ERROR_APEX_MORE_THAN_ONE_FILE);
}
try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) {
- ApexInfo apexInfo = mPm.mApexManager.installPackage(apexes[0]);
+ ApexInfo apexInfo = mPm.mApexManager.installPackage(apexes[0], force);
// APEX has been handled successfully by apexd. Let's continue the install flow
// so it will be scanned and registered with the system.
// TODO(b/225756739): Improve atomicity of rebootless APEX install.
diff --git a/services/core/java/com/android/server/pm/MovePackageHelper.java b/services/core/java/com/android/server/pm/MovePackageHelper.java
index 15c10aa..1a5591c 100644
--- a/services/core/java/com/android/server/pm/MovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/MovePackageHelper.java
@@ -302,8 +302,9 @@
new File(origin.mResolvedPath), /* flags */ 0);
final PackageLite lite = ret.isSuccess() ? ret.getResult() : null;
final InstallingSession installingSession = new InstallingSession(origin, move,
- installObserver, installFlags, installSource, volumeUuid, user, packageAbiOverride,
- PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED, lite, mPm);
+ installObserver, installFlags, /* developmentInstallFlags= */ 0, installSource,
+ volumeUuid, user, packageAbiOverride, PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED,
+ lite, mPm);
installingSession.movePackage();
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 2c0e74d..80e07f4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -709,6 +709,9 @@
!= PackageManager.PERMISSION_GRANTED) {
params.installFlags &= ~PackageManager.INSTALL_ALLOW_TEST;
}
+
+ // developmentInstallFlags can ony be set by shell or root.
+ params.developmentInstallFlags = 0;
}
String originatingPackageName = null;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9137d44..10fe65df 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2121,8 +2121,8 @@
// Save the names of pre-existing packages prior to scanning, so we can determine
// which system packages are completely new due to an upgrade.
mExistingPackages = new ArraySet<>(packageSettings.size());
- for (PackageSetting ps : packageSettings.values()) {
- mExistingPackages.add(ps.getPackageName());
+ for (int i = 0; i < packageSettings.size(); i++) {
+ mExistingPackages.add(packageSettings.valueAt(i).getPackageName());
}
// Triggering {@link com.android.server.pm.crossprofile.
@@ -2249,8 +2249,10 @@
// If this is the first boot or an update from pre-M, then we need to initialize the
// default preferred apps across all defined users.
if (mPromoteSystemApps || mFirstBoot) {
- for (UserInfo user : mInjector.getUserManagerInternal().getUsers(true)) {
- mSettings.applyDefaultPreferredAppsLPw(user.id);
+ final List<UserInfo> users = mInjector.getUserManagerInternal().getUsers(true);
+ for (int i = 0; i < users.size(); i++) {
+ mSettings.applyDefaultPreferredAppsLPw(users.get(i).id);
+
}
}
@@ -4278,8 +4280,9 @@
final Computer snapshot = snapshotComputer();
for (String packageName : apkList) {
setSystemAppHiddenUntilInstalled(snapshot, packageName, true);
- for (UserInfo user : mInjector.getUserManagerInternal().getUsers(false)) {
- setSystemAppInstallState(snapshot, packageName, false, user.id);
+ final List<UserInfo> users = mInjector.getUserManagerInternal().getUsers(false);
+ for (int i = 0; i < users.size(); i++) {
+ setSystemAppInstallState(snapshot, packageName, false, users.get(i).id);
}
}
}
@@ -4724,7 +4727,8 @@
ArraySet<CrossProfileIntentFilter> set =
new ArraySet<>(resolver.filterSet());
- for (CrossProfileIntentFilter filter : set) {
+ for (int i = 0; i < set.size(); i++) {
+ final CrossProfileIntentFilter filter = set.valueAt(i);
if (IntentFilter.filterEquals(filter.mFilter, intentFilter)
&& filter.getOwnerPackage().equals(ownerPackage)
&& filter.getTargetUserId() == targetUserId
@@ -6066,8 +6070,8 @@
final Computer snapshot = snapshotComputer();
enforceOwnerRights(snapshot, packageName, Binder.getCallingUid());
mimeTypes = CollectionUtils.emptyIfNull(mimeTypes);
- for (String mimeType : mimeTypes) {
- if (mimeType.length() > 255) {
+ for (int i = 0; i < mimeTypes.size(); i++) {
+ if (mimeTypes.get(i).length() > 255) {
throw new IllegalArgumentException("MIME type length exceeds 255 characters");
}
}
@@ -6941,7 +6945,9 @@
if (targetPkg.getLibraryNames() != null) {
// Set the overlay paths for dependencies of the shared library.
- for (final String libName : targetPkg.getLibraryNames()) {
+ List<String> libraryNames = targetPkg.getLibraryNames();
+ for (int j = 0; j < libraryNames.size(); j++) {
+ final String libName = libraryNames.get(j);
ArraySet<String> modifiedDependents = null;
final SharedLibraryInfo info = computer.getSharedLibraryInfo(libName,
@@ -6955,7 +6961,8 @@
if (dependents == null) {
continue;
}
- for (final VersionedPackage dependent : dependents) {
+ for (int k = 0; k < dependents.size(); k++) {
+ final VersionedPackage dependent = dependents.get(k);
final PackageStateInternal dependentState =
computer.getPackageStateInternal(dependent.getPackageName());
if (dependentState == null) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 8d64bd9..6eff241 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3308,6 +3308,13 @@
// Set package source to other by default
sessionParams.setPackageSource(PackageInstaller.PACKAGE_SOURCE_OTHER);
+ // Encodes one of the states:
+ // 1. Install request explicitly specified --staged, then value will be true.
+ // 2. Install request explicitly specified --non-staged, then value will be false.
+ // 3. Install request did not specify either --staged or --non-staged, then for APEX
+ // installs the value will be true, and for apk installs it will be false.
+ Boolean staged = null;
+
String opt;
boolean replaceExisting = true;
boolean forceNonStaged = false;
@@ -3416,7 +3423,6 @@
break;
case "--apex":
sessionParams.setInstallAsApex();
- sessionParams.setStaged();
break;
case "--force-non-staged":
forceNonStaged = true;
@@ -3425,7 +3431,10 @@
sessionParams.setMultiPackage();
break;
case "--staged":
- sessionParams.setStaged();
+ staged = true;
+ break;
+ case "--non-staged":
+ staged = false;
break;
case "--force-queryable":
sessionParams.setForceQueryable();
@@ -3460,11 +3469,18 @@
throw new IllegalArgumentException("Unknown option " + opt);
}
}
+ if (staged == null) {
+ staged = (sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0;
+ }
if (replaceExisting) {
sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
}
if (forceNonStaged) {
sessionParams.isStaged = false;
+ sessionParams.developmentInstallFlags |=
+ PackageManager.INSTALL_DEVELOPMENT_FORCE_NON_STAGED_APEX_UPDATE;
+ } else if (staged) {
+ sessionParams.setStaged();
}
return params;
}
@@ -4346,7 +4362,8 @@
pw.println(" [--preload] [--instant] [--full] [--dont-kill]");
pw.println(" [--enable-rollback]");
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
- pw.println(" [--apex] [--force-non-staged] [--staged-ready-timeout TIMEOUT]");
+ pw.println(" [--apex] [--non-staged] [--force-non-staged]");
+ pw.println(" [--staged-ready-timeout TIMEOUT]");
pw.println(" [PATH [SPLIT...]|-]");
pw.println(" Install an application. Must provide the apk data to install, either as");
pw.println(" file path(s) or '-' to read from stdin. Options are:");
@@ -4375,8 +4392,12 @@
pw.println(" --update-ownership: request the update ownership enforcement");
pw.println(" --force-uuid: force install on to disk volume with given UUID");
pw.println(" --apex: install an .apex file, not an .apk");
+ pw.println(" --non-staged: explicitly set this installation to be non-staged.");
+ pw.println(" This flag is only useful for APEX installs that are implicitly");
+ pw.println(" assumed to be staged.");
pw.println(" --force-non-staged: force the installation to run under a non-staged");
- pw.println(" session, which may complete without requiring a reboot");
+ pw.println(" session, which may complete without requiring a reboot. This will");
+ pw.println(" force a rebootless update even for APEXes that don't support it");
pw.println(" --staged-ready-timeout: By default, staged sessions wait "
+ DEFAULT_STAGED_READY_TIMEOUT_MS);
pw.println(" milliseconds for pre-reboot verification to complete when");
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 9b92697..494709c 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -1195,8 +1195,11 @@
}
public PackageSetting setLoadingProgress(float progress) {
- mLoadingProgress = progress;
- onChanged();
+ // To prevent race conditions, we don't allow progress to ever go down
+ if (mLoadingProgress < progress) {
+ mLoadingProgress = progress;
+ onChanged();
+ }
return this;
}
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 3c846da..5e23765 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -18,7 +18,12 @@
"name": "CtsCompilationTestCases"
},
{
- "name": "CtsAppEnumerationTestCases"
+ "name": "CtsAppEnumerationTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
},
{
"name": "CtsMatchFlagTestCases"
@@ -129,6 +134,9 @@
},
{
"name": "PackageManagerServiceHostTests"
+ },
+ {
+ "name": "CtsAppEnumerationTestCases"
}
],
"imports": [
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 7609073..b01a89e 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1230,8 +1230,7 @@
}
if (!fromDatasource && !checkPermission(context, permissionManagerServiceInt,
- permission, attributionSource.getUid(),
- attributionSource.getRenouncedPermissions())) {
+ permission, attributionSource)) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
@@ -1292,12 +1291,11 @@
}
case AppOpsManager.MODE_DEFAULT: {
if (!skipCurrentChecks && !checkPermission(context,
- permissionManagerServiceInt, permission, attributionSource.getUid(),
- attributionSource.getRenouncedPermissions())) {
+ permissionManagerServiceInt, permission, attributionSource)) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
if (next != null && !checkPermission(context, permissionManagerServiceInt,
- permission, next.getUid(), next.getRenouncedPermissions())) {
+ permission, next)) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
}
@@ -1326,8 +1324,7 @@
// We consider the chain trusted if the start node has UPDATE_APP_OPS_STATS, and
// every attributionSource in the chain is registered with the system.
final boolean isChainStartTrusted = !hasChain || checkPermission(context,
- permissionManagerServiceInt, UPDATE_APP_OPS_STATS, current.getUid(),
- current.getRenouncedPermissions());
+ permissionManagerServiceInt, UPDATE_APP_OPS_STATS, current);
while (true) {
final boolean skipCurrentChecks = (fromDatasource || next != null);
@@ -1342,12 +1339,12 @@
// If we already checked the permission for this one, skip the work
if (!skipCurrentChecks && !checkPermission(context, permissionManagerServiceInt,
- permission, current.getUid(), current.getRenouncedPermissions())) {
+ permission, current)) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
if (next != null && !checkPermission(context, permissionManagerServiceInt,
- permission, next.getUid(), next.getRenouncedPermissions())) {
+ permission, next)) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
@@ -1415,9 +1412,13 @@
private static boolean checkPermission(@NonNull Context context,
@NonNull PermissionManagerServiceInternal permissionManagerServiceInt,
- @NonNull String permission, int uid, @NonNull Set<String> renouncedPermissions) {
- boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1,
- uid) == PackageManager.PERMISSION_GRANTED;
+ @NonNull String permission, AttributionSource attributionSource) {
+ int uid = attributionSource.getUid();
+ int deviceId = attributionSource.getDeviceId();
+ final Context deviceContext = context.getDeviceId() == deviceId ? context
+ : context.createDeviceContext(deviceId);
+ boolean permissionGranted = deviceContext.checkPermission(permission,
+ Process.INVALID_PID, uid) == PackageManager.PERMISSION_GRANTED;
// Override certain permissions checks for the shared isolated process for both
// HotwordDetectionService and VisualQueryDetectionService, which ordinarily cannot hold
@@ -1433,10 +1434,10 @@
permissionGranted = hotwordServiceProvider != null
&& uid == hotwordServiceProvider.getUid();
}
-
+ Set<String> renouncedPermissions = attributionSource.getRenouncedPermissions();
if (permissionGranted && renouncedPermissions.contains(permission)
- && context.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS,
- /*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED) {
+ && deviceContext.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS,
+ Process.INVALID_PID, uid) == PackageManager.PERMISSION_GRANTED) {
return false;
}
return permissionGranted;
@@ -1507,8 +1508,7 @@
// We consider the chain trusted if the start node has UPDATE_APP_OPS_STATS, and
// every attributionSource in the chain is registered with the system.
final boolean isChainStartTrusted = !hasChain || checkPermission(context,
- permissionManagerServiceInt, UPDATE_APP_OPS_STATS, current.getUid(),
- current.getRenouncedPermissions());
+ permissionManagerServiceInt, UPDATE_APP_OPS_STATS, current);
while (true) {
final boolean skipCurrentChecks = (next != null);
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index 44cc3e7..539bb6b 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -16,12 +16,15 @@
package com.android.server.policy;
+import android.annotation.SuppressLint;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.XmlResourceParser;
+import android.hardware.input.InputManager;
+import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -29,11 +32,14 @@
import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import com.android.internal.policy.IShortcutService;
import com.android.internal.util.XmlUtils;
+import com.android.server.input.KeyboardMetricsCollector;
+import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -87,11 +93,13 @@
}
private final Context mContext;
+ private final Handler mHandler;
private boolean mSearchKeyShortcutPending = false;
private boolean mConsumeSearchKeyUp = true;
- ModifierShortcutManager(Context context) {
+ ModifierShortcutManager(Context context, Handler handler) {
mContext = context;
+ mHandler = handler;
loadShortcuts();
}
@@ -267,11 +275,13 @@
* Handle the shortcut to {@link Intent}
*
* @param kcm the {@link KeyCharacterMap} associated with the keyboard device.
- * @param keyCode The key code of the event.
+ * @param keyEvent The key event.
* @param metaState The meta key modifier state.
* @return True if invoked the shortcut, otherwise false.
*/
- private boolean handleIntentShortcut(KeyCharacterMap kcm, int keyCode, int metaState) {
+ @SuppressLint("MissingPermission")
+ private boolean handleIntentShortcut(KeyCharacterMap kcm, KeyEvent keyEvent, int metaState) {
+ final int keyCode = keyEvent.getKeyCode();
// Shortcuts are invoked through Search+key, so intercept those here
// Any printing key that is chorded with Search should be consumed
// even if no shortcut was invoked. This prevents text from being
@@ -301,6 +311,7 @@
+ "keyCode=" + KeyEvent.keyCodeToString(keyCode) + ","
+ " category=" + category);
}
+ logKeyboardShortcut(keyEvent, KeyboardLogEvent.getLogEventFromIntent(intent));
return true;
} else {
return false;
@@ -317,11 +328,24 @@
+ "the activity to which it is registered was not found: "
+ "META+ or SEARCH" + KeyEvent.keyCodeToString(keyCode));
}
+ logKeyboardShortcut(keyEvent, KeyboardLogEvent.getLogEventFromIntent(shortcutIntent));
return true;
}
return false;
}
+ private void logKeyboardShortcut(KeyEvent event, KeyboardLogEvent logEvent) {
+ mHandler.post(() -> handleKeyboardLogging(event, logEvent));
+ }
+
+ private void handleKeyboardLogging(KeyEvent event, KeyboardLogEvent logEvent) {
+ final InputManager inputManager = mContext.getSystemService(InputManager.class);
+ final InputDevice inputDevice = inputManager != null
+ ? inputManager.getInputDevice(event.getDeviceId()) : null;
+ KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(inputDevice,
+ logEvent, event.getMetaState(), event.getKeyCode());
+ }
+
/**
* Handle the shortcut from {@link KeyEvent}
*
@@ -354,7 +378,7 @@
}
final KeyCharacterMap kcm = event.getKeyCharacterMap();
- if (handleIntentShortcut(kcm, keyCode, metaState)) {
+ if (handleIntentShortcut(kcm, event, metaState)) {
return true;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6e441bf..7f86f1d 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -208,7 +208,6 @@
import com.android.internal.policy.PhoneWindow;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.AccessibilityManagerInternal;
@@ -221,6 +220,7 @@
import com.android.server.display.BrightnessUtils;
import com.android.server.input.InputManagerInternal;
import com.android.server.input.KeyboardMetricsCollector;
+import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.KeyCombinationManager.TwoKeysCombinationRule;
@@ -228,6 +228,7 @@
import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.vibrator.HapticFeedbackVibrationProvider;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -371,12 +372,6 @@
private static final String TALKBACK_LABEL = "TalkBack";
private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800;
- private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
- private static final VibrationAttributes PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES =
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION);
- private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
/**
* Keyguard stuff
@@ -425,6 +420,7 @@
ActivityManagerInternal mActivityManagerInternal;
ActivityTaskManagerInternal mActivityTaskManagerInternal;
AutofillManagerInternal mAutofillManagerInternal;
+ InputManager mInputManager;
InputManagerInternal mInputManagerInternal;
DreamManagerInternal mDreamManagerInternal;
PowerManagerInternal mPowerManagerInternal;
@@ -450,6 +446,7 @@
PackageManager mPackageManager;
SideFpsEventHandler mSideFpsEventHandler;
LockPatternUtils mLockPatternUtils;
+ private HapticFeedbackVibrationProvider mHapticFeedbackVibrationProvider;
private boolean mHasFeatureAuto;
private boolean mHasFeatureWatch;
private boolean mHasFeatureLeanback;
@@ -458,9 +455,6 @@
// Assigned on main thread, accessed on UI thread
volatile VrManagerInternal mVrManagerInternal;
- // Vibrator pattern for haptic feedback during boot when safe mode is enabled.
- long[] mSafeModeEnabledVibePattern;
-
/** If true, hitting shift & menu will broadcast Intent.ACTION_BUG_REPORT */
boolean mEnableShiftMenuBugReports = false;
@@ -558,7 +552,6 @@
int mPowerVolUpBehavior;
boolean mStylusButtonsEnabled = true;
boolean mHasSoftInput = false;
- boolean mHapticTextHandleEnabled;
boolean mUseTvRouting;
boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
MetricsLogger mLogger;
@@ -778,7 +771,7 @@
handleSwitchKeyboardLayout(msg.arg1, msg.arg2);
break;
case MSG_LOG_KEYBOARD_SYSTEM_EVENT:
- handleKeyboardSystemEvent(msg.arg2, (KeyEvent) msg.obj);
+ handleKeyboardSystemEvent(KeyboardLogEvent.from(msg.arg1), (KeyEvent) msg.obj);
break;
}
}
@@ -1837,8 +1830,11 @@
}
private void launchAllAppsViaA11y() {
- getAccessibilityManagerInternal().performSystemAction(
- AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
+ AccessibilityManagerInternal accessibilityManager = getAccessibilityManagerInternal();
+ if (accessibilityManager != null) {
+ accessibilityManager.performSystemAction(
+ AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
+ }
}
private void toggleNotificationPanel() {
@@ -1909,6 +1905,7 @@
// If we have released the home key, and didn't do anything else
// while it was pressed, then it is time to go home!
if (!down) {
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.HOME);
if (mDisplayId == DEFAULT_DISPLAY) {
cancelPreloadRecentApps();
}
@@ -2110,6 +2107,7 @@
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+ mInputManager = mContext.getSystemService(InputManager.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
@@ -2179,7 +2177,7 @@
mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
mSettingsObserver.observe();
- mModifierShortcutManager = new ModifierShortcutManager(mContext);
+ mModifierShortcutManager = new ModifierShortcutManager(mContext, mHandler);
mUiMode = mContext.getResources().getInteger(
com.android.internal.R.integer.config_defaultUiModeType);
mHomeIntent = new Intent(Intent.ACTION_MAIN, null);
@@ -2251,9 +2249,6 @@
mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup);
- mHapticTextHandleEnabled = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enableHapticTextHandle);
-
mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION;
mHandleVolumeKeysInWM = mContext.getResources().getBoolean(
@@ -2294,8 +2289,8 @@
mContext.registerReceiver(mMultiuserReceiver, filter);
mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
- mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(),
- com.android.internal.R.array.config_safeModeEnabledVibePattern);
+ mHapticFeedbackVibrationProvider =
+ new HapticFeedbackVibrationProvider(mContext.getResources(), mVibrator);
mGlobalKeyManager = new GlobalKeyManager(mContext);
@@ -3023,20 +3018,33 @@
* We won't log keyboard events when the input device is null
* or when it is virtual.
*/
- private void handleKeyboardSystemEvent(int keyboardSystemEvent, KeyEvent event) {
- final InputManager inputManager = mContext.getSystemService(InputManager.class);
- final InputDevice inputDevice = inputManager != null
- ? inputManager.getInputDevice(event.getDeviceId()) : null;
- if (inputDevice != null && !inputDevice.isVirtual()) {
- KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(
- inputDevice, keyboardSystemEvent,
- new int[]{event.getKeyCode()}, event.getMetaState());
- }
+ private void handleKeyboardSystemEvent(KeyboardLogEvent keyboardLogEvent, KeyEvent event) {
+ final InputDevice inputDevice = mInputManager.getInputDevice(event.getDeviceId());
+ KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(inputDevice,
+ keyboardLogEvent, event.getMetaState(), event.getKeyCode());
+ event.recycle();
}
- private void logKeyboardSystemsEvent(KeyEvent event, int keyboardSystemEvent) {
- mHandler.obtainMessage(MSG_LOG_KEYBOARD_SYSTEM_EVENT, 0, keyboardSystemEvent, event)
- .sendToTarget();
+ private void logKeyboardSystemsEventOnActionUp(KeyEvent event,
+ KeyboardLogEvent keyboardSystemEvent) {
+ if (event.getAction() != KeyEvent.ACTION_UP) {
+ return;
+ }
+ logKeyboardSystemsEvent(event, keyboardSystemEvent);
+ }
+
+ private void logKeyboardSystemsEventOnActionDown(KeyEvent event,
+ KeyboardLogEvent keyboardSystemEvent) {
+ if (event.getAction() != KeyEvent.ACTION_DOWN) {
+ return;
+ }
+ logKeyboardSystemsEvent(event, keyboardSystemEvent);
+ }
+
+ private void logKeyboardSystemsEvent(KeyEvent event, KeyboardLogEvent keyboardSystemEvent) {
+ KeyEvent eventToLog = KeyEvent.obtain(event);
+ mHandler.obtainMessage(MSG_LOG_KEYBOARD_SYSTEM_EVENT, keyboardSystemEvent.getIntValue(), 0,
+ eventToLog).sendToTarget();
}
// TODO(b/117479243): handle it in InputPolicy
@@ -3137,8 +3145,6 @@
switch (keyCode) {
case KeyEvent.KEYCODE_HOME:
- logKeyboardSystemsEvent(event,
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME);
return handleHomeShortcuts(displayId, focusedToken, event);
case KeyEvent.KEYCODE_MENU:
// Hijack modified menu keys for debugging features
@@ -3149,14 +3155,14 @@
Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT,
null, null, null, 0, null, null);
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.TRIGGER_BUG_REPORT);
return true;
}
break;
case KeyEvent.KEYCODE_RECENT_APPS:
if (firstDown) {
showRecentApps(false /* triggeredFromAltTab */);
- logKeyboardSystemsEvent(event,
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RECENT_APPS);
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.RECENT_APPS);
}
return true;
case KeyEvent.KEYCODE_APP_SWITCH:
@@ -3165,6 +3171,7 @@
preloadRecentApps();
} else if (!down) {
toggleRecentApps();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.APP_SWITCH);
}
}
return true;
@@ -3173,6 +3180,7 @@
launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
deviceId, event.getEventTime(),
AssistUtils.INVOCATION_TYPE_UNKNOWN);
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_ASSISTANT);
return true;
}
break;
@@ -3185,12 +3193,14 @@
case KeyEvent.KEYCODE_I:
if (firstDown && event.isMetaPressed()) {
showSystemSettings();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_SYSTEM_SETTINGS);
return true;
}
break;
case KeyEvent.KEYCODE_L:
if (firstDown && event.isMetaPressed()) {
lockNow(null /* options */);
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.LOCK_SCREEN);
return true;
}
break;
@@ -3198,8 +3208,10 @@
if (firstDown && event.isMetaPressed()) {
if (event.isCtrlPressed()) {
sendSystemKeyToStatusBarAsync(event);
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.OPEN_NOTES);
} else {
toggleNotificationPanel();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL);
}
return true;
}
@@ -3207,12 +3219,14 @@
case KeyEvent.KEYCODE_S:
if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.TAKE_SCREENSHOT);
return true;
}
break;
case KeyEvent.KEYCODE_T:
if (firstDown && event.isMetaPressed()) {
toggleTaskbar();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_TASKBAR);
return true;
}
break;
@@ -3221,6 +3235,7 @@
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
statusbar.goToFullscreenFromSplit();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION);
return true;
}
}
@@ -3228,18 +3243,21 @@
case KeyEvent.KEYCODE_DPAD_LEFT:
if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
enterStageSplitFromRunningApp(true /* leftOrTop */);
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION);
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
enterStageSplitFromRunningApp(false /* leftOrTop */);
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION);
return true;
}
break;
case KeyEvent.KEYCODE_SLASH:
if (firstDown && event.isMetaPressed() && !keyguardOn) {
toggleKeyboardShortcutsMenu(event.getDeviceId());
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.OPEN_SHORTCUT_HELPER);
return true;
}
break;
@@ -3308,20 +3326,26 @@
intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true);
startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.getBrightnessEvent(keyCode));
}
return true;
case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN:
if (down) {
mInputManagerInternal.decrementKeyboardBacklight(event.getDeviceId());
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.KEYBOARD_BACKLIGHT_DOWN);
}
return true;
case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP:
if (down) {
mInputManagerInternal.incrementKeyboardBacklight(event.getDeviceId());
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.KEYBOARD_BACKLIGHT_UP);
}
return true;
case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE:
// TODO: Add logic
+ if (!down) {
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.KEYBOARD_BACKLIGHT_TOGGLE);
+ }
return true;
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
@@ -3347,6 +3371,7 @@
if (firstDown && !keyguardOn && isUserSetupComplete()) {
if (event.isMetaPressed()) {
showRecentApps(false);
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.RECENT_APPS);
return true;
} else if (mRecentAppsHeldModifiers == 0) {
final int shiftlessModifiers =
@@ -3355,6 +3380,7 @@
shiftlessModifiers, KeyEvent.META_ALT_ON)) {
mRecentAppsHeldModifiers = shiftlessModifiers;
showRecentApps(true);
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.RECENT_APPS);
return true;
}
}
@@ -3366,11 +3392,13 @@
Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS);
msg.setAsynchronous(true);
msg.sendToTarget();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.ALL_APPS);
}
return true;
case KeyEvent.KEYCODE_NOTIFICATION:
if (!down) {
toggleNotificationPanel();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL);
}
return true;
case KeyEvent.KEYCODE_SEARCH:
@@ -3378,6 +3406,7 @@
switch (mSearchKeyBehavior) {
case SEARCH_BEHAVIOR_TARGET_ACTIVITY: {
launchTargetSearchActivity();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_SEARCH);
return true;
}
case SEARCH_BEHAVIOR_DEFAULT_SEARCH:
@@ -3390,6 +3419,7 @@
if (firstDown) {
int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
sendSwitchKeyboardLayout(event, direction);
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.LANGUAGE_SWITCH);
return true;
}
break;
@@ -3398,6 +3428,7 @@
if (firstDown && event.isMetaPressed()) {
int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
sendSwitchKeyboardLayout(event, direction);
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.LANGUAGE_SWITCH);
return true;
}
break;
@@ -3416,9 +3447,11 @@
if (mPendingCapsLockToggle) {
mInputManagerInternal.toggleCapsLock(event.getDeviceId());
mPendingCapsLockToggle = false;
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_CAPS_LOCK);
} else if (mPendingMetaAction) {
if (!canceled) {
launchAllAppsViaA11y();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.ACCESSIBILITY_ALL_APPS);
}
mPendingMetaAction = false;
}
@@ -3446,10 +3479,16 @@
if (mPendingCapsLockToggle) {
mInputManagerInternal.toggleCapsLock(event.getDeviceId());
mPendingCapsLockToggle = false;
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_CAPS_LOCK);
return true;
}
}
break;
+ case KeyEvent.KEYCODE_CAPS_LOCK:
+ if (!down) {
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_CAPS_LOCK);
+ }
+ break;
case KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
@@ -4289,6 +4328,7 @@
// Handle special keys.
switch (keyCode) {
case KeyEvent.KEYCODE_BACK: {
+ logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.BACK);
if (down) {
mBackKeyHandled = false;
} else {
@@ -4306,6 +4346,8 @@
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
+ logKeyboardSystemsEventOnActionDown(event,
+ KeyboardLogEvent.getVolumeEvent(keyCode));
if (down) {
sendSystemKeyToStatusBarAsync(event);
@@ -4406,6 +4448,7 @@
}
case KeyEvent.KEYCODE_TV_POWER: {
+ logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.TOGGLE_POWER);
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down && hdmiControlManager != null) {
@@ -4415,6 +4458,7 @@
}
case KeyEvent.KEYCODE_POWER: {
+ logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.TOGGLE_POWER);
EventLogTags.writeInterceptPower(
KeyEvent.actionToString(event.getAction()),
mPowerKeyHandled ? 1 : 0,
@@ -4437,12 +4481,14 @@
case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT:
// fall through
case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT: {
+ logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.SYSTEM_NAVIGATION);
result &= ~ACTION_PASS_TO_USER;
interceptSystemNavigationKey(event);
break;
}
case KeyEvent.KEYCODE_SLEEP: {
+ logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.SLEEP);
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false;
if (!mPowerManager.isInteractive()) {
@@ -4457,6 +4503,7 @@
}
case KeyEvent.KEYCODE_SOFT_SLEEP: {
+ logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.SLEEP);
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false;
if (!down) {
@@ -4466,6 +4513,7 @@
}
case KeyEvent.KEYCODE_WAKEUP: {
+ logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.WAKEUP);
result &= ~ACTION_PASS_TO_USER;
isWakeKey = true;
break;
@@ -4474,6 +4522,7 @@
case KeyEvent.KEYCODE_MUTE:
result &= ~ACTION_PASS_TO_USER;
if (down && event.getRepeatCount() == 0) {
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.SYSTEM_MUTE);
toggleMicrophoneMuteFromKey();
}
break;
@@ -4488,6 +4537,7 @@
case KeyEvent.KEYCODE_MEDIA_RECORD:
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
+ logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.MEDIA_KEY);
if (MediaSessionLegacyHelper.getHelper(mContext).isGlobalPriorityActive()) {
// If the global session is active pass all media keys to it
// instead of the active window.
@@ -4532,6 +4582,7 @@
0 /* unused */, event.getEventTime() /* eventTime */);
msg.setAsynchronous(true);
msg.sendToTarget();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_ASSISTANT);
}
result &= ~ACTION_PASS_TO_USER;
break;
@@ -4542,6 +4593,7 @@
Message msg = mHandler.obtainMessage(MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK);
msg.setAsynchronous(true);
msg.sendToTarget();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_VOICE_ASSISTANT);
}
result &= ~ACTION_PASS_TO_USER;
break;
@@ -5499,10 +5551,6 @@
}
}
- static long[] getLongIntArray(Resources r, int resid) {
- return ArrayUtils.convertToLongArray(r.getIntArray(resid));
- }
-
private void bindKeyguard() {
synchronized (mLock) {
if (mKeyguardBound) {
@@ -5989,138 +6037,18 @@
if (!mVibrator.hasVibrator()) {
return false;
}
- VibrationEffect effect = getVibrationEffect(effectId);
+ VibrationEffect effect =
+ mHapticFeedbackVibrationProvider.getVibrationForHapticFeedback(effectId);
if (effect == null) {
return false;
}
- VibrationAttributes attrs = getVibrationAttributes(effectId);
- if (always) {
- attrs = new VibrationAttributes.Builder(attrs)
- .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
- .build();
- }
+ VibrationAttributes attrs =
+ mHapticFeedbackVibrationProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ always);
mVibrator.vibrate(uid, packageName, effect, reason, attrs);
return true;
}
- private VibrationEffect getVibrationEffect(int effectId) {
- long[] pattern;
- switch (effectId) {
- case HapticFeedbackConstants.CONTEXT_CLICK:
- case HapticFeedbackConstants.GESTURE_END:
- case HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE:
- case HapticFeedbackConstants.SCROLL_TICK:
- case HapticFeedbackConstants.SEGMENT_TICK:
- return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
-
- case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
- if (!mHapticTextHandleEnabled) {
- return null;
- }
- // fallthrough
- case HapticFeedbackConstants.CLOCK_TICK:
- case HapticFeedbackConstants.SEGMENT_FREQUENT_TICK:
- return VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
-
- case HapticFeedbackConstants.KEYBOARD_RELEASE:
- case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
- case HapticFeedbackConstants.ENTRY_BUMP:
- case HapticFeedbackConstants.DRAG_CROSSING:
- return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
-
- case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
- case HapticFeedbackConstants.VIRTUAL_KEY:
- case HapticFeedbackConstants.EDGE_RELEASE:
- case HapticFeedbackConstants.CALENDAR_DATE:
- case HapticFeedbackConstants.CONFIRM:
- case HapticFeedbackConstants.GESTURE_START:
- case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
- case HapticFeedbackConstants.SCROLL_LIMIT:
- return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
-
- case HapticFeedbackConstants.LONG_PRESS:
- case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
- case HapticFeedbackConstants.DRAG_START:
- case HapticFeedbackConstants.EDGE_SQUEEZE:
- return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
-
- case HapticFeedbackConstants.REJECT:
- return VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
-
- case HapticFeedbackConstants.SAFE_MODE_ENABLED:
- pattern = mSafeModeEnabledVibePattern;
- break;
-
- case HapticFeedbackConstants.ASSISTANT_BUTTON:
- if (mVibrator.areAllPrimitivesSupported(
- VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
- VibrationEffect.Composition.PRIMITIVE_TICK)) {
- // quiet ramp, short pause, then sharp tick
- return VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50)
- .compose();
- }
- // fallback for devices without composition support
- return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
-
- case HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE:
- return getScaledPrimitiveOrElseEffect(
- VibrationEffect.Composition.PRIMITIVE_TICK, 0.4f,
- VibrationEffect.EFFECT_TEXTURE_TICK);
-
- case HapticFeedbackConstants.TOGGLE_ON:
- return getScaledPrimitiveOrElseEffect(
- VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f,
- VibrationEffect.EFFECT_TICK);
-
- case HapticFeedbackConstants.TOGGLE_OFF:
- return getScaledPrimitiveOrElseEffect(
- VibrationEffect.Composition.PRIMITIVE_LOW_TICK, 0.2f,
- VibrationEffect.EFFECT_TEXTURE_TICK);
-
- case HapticFeedbackConstants.NO_HAPTICS:
- default:
- return null;
- }
- if (pattern.length == 0) {
- // No vibration
- return null;
- } else if (pattern.length == 1) {
- // One-shot vibration
- return VibrationEffect.createOneShot(pattern[0], VibrationEffect.DEFAULT_AMPLITUDE);
- } else {
- // Pattern vibration
- return VibrationEffect.createWaveform(pattern, -1);
- }
- }
-
- private VibrationEffect getScaledPrimitiveOrElseEffect(int primitiveId, float scale,
- int elseEffectId) {
- if (mVibrator.areAllPrimitivesSupported(primitiveId)) {
- return VibrationEffect.startComposition()
- .addPrimitive(primitiveId, scale)
- .compose();
- } else {
- return VibrationEffect.get(elseEffectId);
- }
- }
-
- private VibrationAttributes getVibrationAttributes(int effectId) {
- switch (effectId) {
- case HapticFeedbackConstants.EDGE_SQUEEZE:
- case HapticFeedbackConstants.EDGE_RELEASE:
- return PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES;
- case HapticFeedbackConstants.ASSISTANT_BUTTON:
- case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
- case HapticFeedbackConstants.SCROLL_TICK:
- case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
- case HapticFeedbackConstants.SCROLL_LIMIT:
- return HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES;
- default:
- return TOUCH_VIBRATION_ATTRIBUTES;
- }
- }
@Override
public void keepScreenOnStartedLw() {
@@ -6258,8 +6186,7 @@
pw.print("mAllowStartActivityForLongPressOnPowerDuringSetup=");
pw.println(mAllowStartActivityForLongPressOnPowerDuringSetup);
pw.print(prefix);
- pw.print("mHasSoftInput="); pw.print(mHasSoftInput);
- pw.print(" mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled);
+ pw.print("mHasSoftInput="); pw.println(mHasSoftInput);
pw.print(prefix);
pw.print("mDismissImeOnBackKeyPressed="); pw.print(mDismissImeOnBackKeyPressed);
pw.print(" mIncallPowerBehavior=");
@@ -6284,6 +6211,7 @@
pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout);
pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
+ mHapticFeedbackVibrationProvider.dump(prefix, pw);
mGlobalKeyManager.dump(prefix, pw);
mKeyCombinationManager.dump(prefix, pw);
mSingleKeyGestureDetector.dump(prefix, pw);
@@ -6545,6 +6473,7 @@
private class HdmiVideoExtconUEventObserver extends ExtconStateObserver<Boolean> {
private static final String HDMI_EXIST = "HDMI=1";
+ private static final String DP_EXIST = "DP=1";
private static final String NAME = "hdmi";
private boolean init(ExtconInfo hdmi) {
@@ -6575,7 +6504,8 @@
public Boolean parseState(ExtconInfo extconIfno, String state) {
// extcon event state changes from kernel4.9
// new state will be like STATE=HDMI=1
- return state.contains(HDMI_EXIST);
+ // or like STATE=DP=1 for newer kernel
+ return state.contains(HDMI_EXIST) || state.contains(DP_EXIST);
}
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index fc971e4..5a050ac 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -92,6 +92,7 @@
import android.os.UserManager;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
+import android.provider.DeviceConfigInterface;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.service.dreams.DreamManagerInternal;
@@ -128,6 +129,7 @@
import com.android.server.UserspaceRebootLogger;
import com.android.server.Watchdog;
import com.android.server.am.BatteryStatsService;
+import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
import com.android.server.policy.WindowManagerPolicy;
@@ -323,6 +325,9 @@
private final Injector mInjector;
private final PermissionCheckerWrapper mPermissionCheckerWrapper;
private final PowerPropertiesWrapper mPowerPropertiesWrapper;
+ private final DeviceConfigParameterProvider mDeviceConfigProvider;
+
+ private boolean mDisableScreenWakeLocksWhileCached;
private LightsManager mLightsManager;
private BatteryManagerInternal mBatteryManagerInternal;
@@ -1065,6 +1070,10 @@
}
};
}
+
+ DeviceConfigParameterProvider createDeviceConfigParameterProvider() {
+ return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
+ }
}
/** Interface for checking an app op permission */
@@ -1161,6 +1170,7 @@
mInjector.createInattentiveSleepWarningController();
mPermissionCheckerWrapper = mInjector.createPermissionCheckerWrapper();
mPowerPropertiesWrapper = mInjector.createPowerPropertiesWrapper();
+ mDeviceConfigProvider = mInjector.createDeviceConfigParameterProvider();
mPowerGroupWakefulnessChangeListener = new PowerGroupWakefulnessChangeListener();
@@ -1346,6 +1356,14 @@
mLightsManager = getLocalService(LightsManager.class);
mAttentionLight = mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
+ updateDeviceConfigLocked();
+ mDeviceConfigProvider.addOnPropertiesChangedListener(BackgroundThread.getExecutor(),
+ properties -> {
+ synchronized (mLock) {
+ updateDeviceConfigLocked();
+ updateWakeLockDisabledStatesLocked();
+ }
+ });
// Initialize display power management.
mDisplayManagerInternal.initPowerManagement(
@@ -1545,6 +1563,12 @@
updatePowerStateLocked();
}
+ @GuardedBy("mLock")
+ private void updateDeviceConfigLocked() {
+ mDisableScreenWakeLocksWhileCached = mDeviceConfigProvider
+ .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+ }
+
@RequiresPermission(value = android.Manifest.permission.TURN_SCREEN_ON, conditional = true)
private void acquireWakeLockInternal(IBinder lock, int displayId, int flags, String tag,
String packageName, WorkSource ws, String historyTag, int uid, int pid,
@@ -2760,13 +2784,13 @@
/** Get wake lock summary flags that correspond to the given wake lock. */
@SuppressWarnings("deprecation")
private int getWakeLockSummaryFlags(WakeLock wakeLock) {
+ if (wakeLock.mDisabled) {
+ // We only respect this if the wake lock is not disabled.
+ return 0;
+ }
switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
case PowerManager.PARTIAL_WAKE_LOCK:
- if (!wakeLock.mDisabled) {
- // We only respect this if the wake lock is not disabled.
- return WAKE_LOCK_CPU;
- }
- break;
+ return WAKE_LOCK_CPU;
case PowerManager.FULL_WAKE_LOCK:
return WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT;
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
@@ -4151,7 +4175,7 @@
for (int i = 0; i < numWakeLocks; i++) {
final WakeLock wakeLock = mWakeLocks.get(i);
if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)
- == PowerManager.PARTIAL_WAKE_LOCK) {
+ == PowerManager.PARTIAL_WAKE_LOCK || isScreenLock(wakeLock)) {
if (setWakeLockDisabledStateLocked(wakeLock)) {
changed = true;
if (wakeLock.mDisabled) {
@@ -4205,6 +4229,22 @@
}
}
return wakeLock.setDisabled(disabled);
+ } else if (mDisableScreenWakeLocksWhileCached && isScreenLock(wakeLock)) {
+ boolean disabled = false;
+ final int appid = UserHandle.getAppId(wakeLock.mOwnerUid);
+ final UidState state = wakeLock.mUidState;
+ // Cached inactive processes are never allowed to hold wake locks.
+ if (mConstants.NO_CACHED_WAKE_LOCKS
+ && appid >= Process.FIRST_APPLICATION_UID
+ && !state.mActive
+ && state.mProcState != ActivityManager.PROCESS_STATE_NONEXISTENT
+ && state.mProcState >= ActivityManager.PROCESS_STATE_TOP_SLEEPING) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "disabling full wakelock " + wakeLock);
+ }
+ disabled = true;
+ }
+ return wakeLock.setDisabled(disabled);
}
return false;
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
index d8e6c26..d1d27f3 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
@@ -17,6 +17,7 @@
package com.android.server.powerstats;
import android.content.Context;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.internal.util.FileRotator;
@@ -27,6 +28,7 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
+import java.util.Date;
import java.util.concurrent.locks.ReentrantLock;
/**
@@ -266,4 +268,51 @@
mLock.unlock();
}
}
+
+ /**
+ * Dump stats about stored data.
+ */
+ public void dump(IndentingPrintWriter ipw) {
+ mLock.lock();
+ try {
+ final int versionDot = mDataStorageFilename.lastIndexOf('.');
+ final String beforeVersionDot = mDataStorageFilename.substring(0, versionDot);
+ final File[] files = mDataStorageDir.listFiles();
+
+ int number = 0;
+ int dataSize = 0;
+ long earliestLogEpochTime = Long.MAX_VALUE;
+ for (int i = 0; i < files.length; i++) {
+ // Check that the stems before the version match.
+ final File file = files[i];
+ final String fileName = file.getName();
+ if (files[i].getName().startsWith(beforeVersionDot)) {
+ number++;
+ dataSize += file.length();
+ final int firstTimeChar = fileName.lastIndexOf('.') + 1;
+ final int endChar = fileName.lastIndexOf('-');
+ try {
+ final Long startTime =
+ Long.parseLong(fileName.substring(firstTimeChar, endChar));
+ if (startTime != null && startTime < earliestLogEpochTime) {
+ earliestLogEpochTime = startTime;
+ }
+ } catch (NumberFormatException nfe) {
+ Slog.e(TAG,
+ "Failed to extract start time from file : " + fileName, nfe);
+ }
+ }
+ }
+
+ if (earliestLogEpochTime != Long.MAX_VALUE) {
+ ipw.println("Earliest data time : " + new Date(earliestLogEpochTime));
+ } else {
+ ipw.println("Failed to parse earliest data time!!!");
+ }
+ ipw.println("# files : " + number);
+ ipw.println("Total data size (B) : " + dataSize);
+ } finally {
+ mLock.unlock();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index 39ead13..e80a86d 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -30,6 +30,7 @@
import android.os.Message;
import android.os.SystemClock;
import android.util.AtomicFile;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
@@ -354,4 +355,23 @@
updateCacheFile(residencyCacheFilename, powerEntityBytes);
}
}
+
+ /**
+ * Dump stats about stored data.
+ */
+ public void dump(IndentingPrintWriter ipw) {
+ ipw.println("PowerStats Meter Data:");
+ ipw.increaseIndent();
+ mPowerStatsMeterStorage.dump(ipw);
+ ipw.decreaseIndent();
+ ipw.println("PowerStats Model Data:");
+ ipw.increaseIndent();
+ mPowerStatsModelStorage.dump(ipw);
+ ipw.decreaseIndent();
+ ipw.println("PowerStats State Residency Data:");
+ ipw.increaseIndent();
+ mPowerStatsResidencyStorage.dump(ipw);
+ ipw.decreaseIndent();
+ }
+
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 9832c49..5609f69 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -42,6 +42,7 @@
import android.power.PowerStatsInternal;
import android.provider.DeviceConfig;
import android.provider.DeviceConfigInterface;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
@@ -232,18 +233,31 @@
} else if ("residency".equals(args[1])) {
mPowerStatsLogger.writeResidencyDataToFile(fd);
}
- } else if (args.length == 0) {
- pw.println("PowerStatsService dumpsys: available PowerEntities");
+ } else {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ ipw.println("PowerStatsService dumpsys: available PowerEntities");
PowerEntity[] powerEntity = getPowerStatsHal().getPowerEntityInfo();
- PowerEntityUtils.dumpsys(powerEntity, pw);
+ ipw.increaseIndent();
+ PowerEntityUtils.dumpsys(powerEntity, ipw);
+ ipw.decreaseIndent();
- pw.println("PowerStatsService dumpsys: available Channels");
+ ipw.println("PowerStatsService dumpsys: available Channels");
Channel[] channel = getPowerStatsHal().getEnergyMeterInfo();
- ChannelUtils.dumpsys(channel, pw);
+ ipw.increaseIndent();
+ ChannelUtils.dumpsys(channel, ipw);
+ ipw.decreaseIndent();
- pw.println("PowerStatsService dumpsys: available EnergyConsumers");
+ ipw.println("PowerStatsService dumpsys: available EnergyConsumers");
EnergyConsumer[] energyConsumer = getPowerStatsHal().getEnergyConsumerInfo();
- EnergyConsumerUtils.dumpsys(energyConsumer, pw);
+ ipw.increaseIndent();
+ EnergyConsumerUtils.dumpsys(energyConsumer, ipw);
+ ipw.decreaseIndent();
+
+ ipw.println("PowerStatsService dumpsys: PowerStatsLogger stats");
+ ipw.increaseIndent();
+ mPowerStatsLogger.dump(ipw);
+ ipw.decreaseIndent();
+
}
}
}
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
new file mode 100644
index 0000000..8be3b2d
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.os.VibrationEffect;
+import android.os.vibrator.persistence.VibrationXmlParser;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import com.android.internal.vibrator.persistence.XmlParserException;
+import com.android.internal.vibrator.persistence.XmlReader;
+import com.android.internal.vibrator.persistence.XmlValidator;
+import com.android.modules.utils.TypedXmlPullParser;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+
+/**
+ * Class that loads custom {@link VibrationEffect} to be performed for each
+ * {@link HapticFeedbackConstants} key.
+ *
+ * <p>The system has its default logic to get the {@link VibrationEffect} that will be played for a
+ * given haptic feedback constant. Devices may choose to override some or all of these supported
+ * haptic feedback vibrations via a customization XML.
+ *
+ * <p>The XML simply provides a mapping of a constant from {@link HapticFeedbackConstants} to its
+ * corresponding {@link VibrationEffect}. Its root tag should be `<haptic-feedback-constants>`. It
+ * should have one or more entries for customizing a haptic feedback constant. A customization is
+ * started by a `<constant id="X">` tag (where `X` is the haptic feedback constant being customized
+ * in this entry) and closed by </constant>. Between these two tags, there should be a valid XML
+ * serialization of a non-repeating {@link VibrationEffect}. Such a valid vibration serialization
+ * should be parse-able by {@link VibrationXmlParser}.
+ *
+ * The example below represents a valid customization for effect IDs 10 and 11.
+ *
+ * <pre>
+ * {@code
+ * <haptic-feedback-constants>
+ * <constant id="10">
+ * // Valid Vibration Serialization
+ * </constant>
+ * <constant id="11">
+ * // Valid Vibration Serialization
+ * </constant>
+ * </haptic-feedback-constants>
+ * }
+ * </pre>
+ *
+ * <p>After a successful parsing of the customization XML file, it returns a {@link SparseArray}
+ * that maps each customized haptic feedback effect ID to its respective {@link VibrationEffect}.
+ *
+ * @hide
+ */
+final class HapticFeedbackCustomization {
+ private static final String TAG = "HapticFeedbackCustomization";
+
+ /** The outer-most tag for haptic feedback customizations. */
+ private static final String TAG_CONSTANTS = "haptic-feedback-constants";
+ /** The tag defining a customization for a single haptic feedback constant. */
+ private static final String TAG_CONSTANT = "constant";
+
+ /**
+ * Attribute for {@link TAG_CONSTANT}, specifying the haptic feedback constant to
+ * customize.
+ */
+ private static final String ATTRIBUTE_ID = "id";
+
+ /**
+ * Parses the haptic feedback vibration customization XML file for the device, and provides a
+ * mapping of the customized effect IDs to their respective {@link VibrationEffect}s.
+ *
+ * <p>This is potentially expensive, so avoid calling repeatedly. One call is enough, and the
+ * caller should process the returned mapping (if any) for further queries.
+ *
+ * @param res {@link Resources} object to be used for reading the device's resources.
+ * @return a {@link SparseArray} that maps each customized haptic feedback effect ID to its
+ * respective {@link VibrationEffect}, or {@code null}, if the device has not configured
+ * a file for haptic feedback constants customization.
+ * @throws {@link IOException} if an IO error occurs while parsing the customization XML.
+ * @throws {@link CustomizationParserException} for any non-IO error that occurs when parsing
+ * the XML, like an invalid XML content or an invalid haptic feedback constant.
+ *
+ * @hide
+ */
+ @Nullable
+ static SparseArray<VibrationEffect> loadVibrations(Resources res)
+ throws CustomizationParserException, IOException {
+ try {
+ return loadVibrationsInternal(res);
+ } catch (VibrationXmlParser.VibrationXmlParserException
+ | XmlParserException
+ | XmlPullParserException e) {
+ throw new CustomizationParserException(
+ "Error parsing haptic feedback customization file.", e);
+ }
+ }
+
+ @Nullable
+ private static SparseArray<VibrationEffect> loadVibrationsInternal(Resources res) throws
+ CustomizationParserException,
+ IOException,
+ VibrationXmlParser.VibrationXmlParserException,
+ XmlParserException,
+ XmlPullParserException {
+ String customizationFile =
+ res.getString(
+ com.android.internal.R.string.config_hapticFeedbackCustomizationFile);
+ if (TextUtils.isEmpty(customizationFile)) {
+ Slog.d(TAG, "Customization file not configured.");
+ return null;
+ }
+
+ FileReader fileReader;
+ try {
+ fileReader = new FileReader(customizationFile);
+ } catch (FileNotFoundException e) {
+ Slog.d(TAG, "Specified customization file not found.");
+ return null;
+ }
+
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ parser.setInput(fileReader);
+
+ XmlReader.readDocumentStartTag(parser, TAG_CONSTANTS);
+ XmlValidator.checkTagHasNoUnexpectedAttributes(parser);
+ int rootDepth = parser.getDepth();
+
+ SparseArray<VibrationEffect> mapping = new SparseArray<>();
+ while (XmlReader.readNextTagWithin(parser, rootDepth)) {
+ XmlValidator.checkStartTag(parser, TAG_CONSTANT);
+ int customizationDepth = parser.getDepth();
+
+ // Only attribute in tag is the `id` attribute.
+ XmlValidator.checkTagHasNoUnexpectedAttributes(parser, ATTRIBUTE_ID);
+ int effectId = XmlReader.readAttributeIntNonNegative(parser, ATTRIBUTE_ID);
+ if (mapping.contains(effectId)) {
+ throw new CustomizationParserException(
+ "Multiple customizations found for effect " + effectId);
+ }
+
+ // Move the parser one step into the `<constant>` tag.
+ XmlValidator.checkParserCondition(
+ XmlReader.readNextTagWithin(parser, customizationDepth),
+ "Unsupported empty customization tag");
+
+ VibrationEffect effect = VibrationXmlParser.parseTag(
+ parser, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS);
+ if (effect.getDuration() == Long.MAX_VALUE) {
+ throw new CustomizationParserException(String.format(
+ "Vibration for effect ID %d is repeating, which is not allowed as a"
+ + " haptic feedback: %s", effectId, effect));
+ }
+ mapping.put(effectId, effect);
+
+ XmlReader.readEndTag(parser, TAG_CONSTANT, customizationDepth);
+ }
+
+ // Make checks that the XML ends well.
+ XmlReader.readEndTag(parser, TAG_CONSTANTS, rootDepth);
+ XmlReader.readDocumentEndTag(parser);
+
+ return mapping;
+ }
+
+ /**
+ * Represents an error while parsing a haptic feedback customization XML.
+ *
+ * @hide
+ */
+ static final class CustomizationParserException extends Exception {
+ private CustomizationParserException(String message) {
+ super(message);
+ }
+
+ private CustomizationParserException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
new file mode 100644
index 0000000..308ce4f
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.view.HapticFeedbackConstants;
+
+import java.io.PrintWriter;
+
+/**
+ * Provides the {@link VibrationEffect} and {@link VibrationAttributes} for haptic feedback.
+ *
+ * @hide
+ */
+public final class HapticFeedbackVibrationProvider {
+ private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
+ private static final VibrationAttributes PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION);
+ private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
+
+ private final Vibrator mVibrator;
+ private final boolean mHapticTextHandleEnabled;
+ // Vibrator effect for haptic feedback during boot when safe mode is enabled.
+ private final VibrationEffect mSafeModeEnabledVibrationEffect;
+
+ /** @hide */
+ public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) {
+ mVibrator = vibrator;
+ mHapticTextHandleEnabled = res.getBoolean(
+ com.android.internal.R.bool.config_enableHapticTextHandle);
+ mSafeModeEnabledVibrationEffect =
+ VibrationSettings.createEffectFromResource(
+ res, com.android.internal.R.array.config_safeModeEnabledVibePattern);
+ }
+
+ /**
+ * Provides the {@link VibrationEffect} for a given haptic feedback effect ID (provided in
+ * {@link HapticFeedbackConstants}).
+ *
+ * @param effectId the haptic feedback effect ID whose respective vibration we want to get.
+ * @return a {@link VibrationEffect} for the given haptic feedback effect ID, or {@code null} if
+ * the provided effect ID is not supported.
+ */
+ @Nullable public VibrationEffect getVibrationForHapticFeedback(int effectId) {
+ switch (effectId) {
+ case HapticFeedbackConstants.CONTEXT_CLICK:
+ case HapticFeedbackConstants.GESTURE_END:
+ case HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE:
+ case HapticFeedbackConstants.SCROLL_TICK:
+ case HapticFeedbackConstants.SEGMENT_TICK:
+ return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+
+ case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
+ if (!mHapticTextHandleEnabled) {
+ return null;
+ }
+ // fallthrough
+ case HapticFeedbackConstants.CLOCK_TICK:
+ case HapticFeedbackConstants.SEGMENT_FREQUENT_TICK:
+ return VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
+
+ case HapticFeedbackConstants.KEYBOARD_RELEASE:
+ case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
+ case HapticFeedbackConstants.ENTRY_BUMP:
+ case HapticFeedbackConstants.DRAG_CROSSING:
+ return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
+
+ case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
+ case HapticFeedbackConstants.VIRTUAL_KEY:
+ case HapticFeedbackConstants.EDGE_RELEASE:
+ case HapticFeedbackConstants.CALENDAR_DATE:
+ case HapticFeedbackConstants.CONFIRM:
+ case HapticFeedbackConstants.GESTURE_START:
+ case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
+ case HapticFeedbackConstants.SCROLL_LIMIT:
+ return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+
+ case HapticFeedbackConstants.LONG_PRESS:
+ case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
+ case HapticFeedbackConstants.DRAG_START:
+ case HapticFeedbackConstants.EDGE_SQUEEZE:
+ return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
+
+ case HapticFeedbackConstants.REJECT:
+ return VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+
+ case HapticFeedbackConstants.SAFE_MODE_ENABLED:
+ return mSafeModeEnabledVibrationEffect;
+
+ case HapticFeedbackConstants.ASSISTANT_BUTTON:
+ if (mVibrator.areAllPrimitivesSupported(
+ VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
+ VibrationEffect.Composition.PRIMITIVE_TICK)) {
+ // quiet ramp, short pause, then sharp tick
+ return VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50)
+ .compose();
+ }
+ // fallback for devices without composition support
+ return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
+
+ case HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE:
+ return getScaledPrimitiveOrElseEffect(
+ VibrationEffect.Composition.PRIMITIVE_TICK, 0.4f,
+ VibrationEffect.EFFECT_TEXTURE_TICK);
+
+ case HapticFeedbackConstants.TOGGLE_ON:
+ return getScaledPrimitiveOrElseEffect(
+ VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f,
+ VibrationEffect.EFFECT_TICK);
+
+ case HapticFeedbackConstants.TOGGLE_OFF:
+ return getScaledPrimitiveOrElseEffect(
+ VibrationEffect.Composition.PRIMITIVE_LOW_TICK, 0.2f,
+ VibrationEffect.EFFECT_TEXTURE_TICK);
+
+ case HapticFeedbackConstants.NO_HAPTICS:
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Provides the {@link VibrationAttributes} that should be used for a haptic feedback.
+ *
+ * @param effectId the haptic feedback effect ID whose respective vibration attributes we want
+ * to get.
+ * @param bypassVibrationIntensitySetting {@code true} if the returned attribute should bypass
+ * vibration intensity settings. {@code false} otherwise.
+ * @return the {@link VibrationAttributes} that should be used for the provided haptic feedback.
+ */
+ public VibrationAttributes getVibrationAttributesForHapticFeedback(
+ int effectId, boolean bypassVibrationIntensitySetting) {
+ VibrationAttributes attrs;
+ switch (effectId) {
+ case HapticFeedbackConstants.EDGE_SQUEEZE:
+ case HapticFeedbackConstants.EDGE_RELEASE:
+ attrs = PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES;
+ break;
+ case HapticFeedbackConstants.ASSISTANT_BUTTON:
+ case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
+ case HapticFeedbackConstants.SCROLL_TICK:
+ case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
+ case HapticFeedbackConstants.SCROLL_LIMIT:
+ attrs = HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES;
+ break;
+ default:
+ attrs = TOUCH_VIBRATION_ATTRIBUTES;
+ }
+ if (bypassVibrationIntensitySetting) {
+ attrs = new VibrationAttributes.Builder(attrs)
+ .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
+ .build();
+ }
+ return attrs;
+ }
+
+ /** Dumps relevant state. */
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print("mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled);
+ }
+
+ private VibrationEffect getScaledPrimitiveOrElseEffect(
+ int primitiveId, float scale, int elseEffectId) {
+ if (mVibrator.areAllPrimitivesSupported(primitiveId)) {
+ return VibrationEffect.startComposition()
+ .addPrimitive(primitiveId, scale)
+ .compose();
+ } else {
+ return VibrationEffect.get(elseEffectId);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/TEST_MAPPING b/services/core/java/com/android/server/vibrator/TEST_MAPPING
new file mode 100644
index 0000000..f0a7e47
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/TEST_MAPPING
@@ -0,0 +1,21 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksVibratorServicesTests",
+ "options": [
+ {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksVibratorServicesTests",
+ "options": [
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 4ae7c77..dbd6bf4 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -698,7 +698,23 @@
@Nullable
private VibrationEffect createEffectFromResource(int resId) {
- long[] timings = getLongIntArray(mContext.getResources(), resId);
+ return createEffectFromResource(mContext.getResources(), resId);
+ }
+
+ /**
+ * Provides a {@link VibrationEffect} from a timings-array provided as an int-array resource..
+ *
+ * <p>If the timings array is {@code null} or empty, it returns {@code null}.
+ *
+ * <p>If the timings array has a size of one, it returns a one-shot vibration with duration that
+ * is equal to the single value in the array.
+ *
+ * <p>If the timings array has more than one values, it returns a non-repeating wave-form
+ * vibration with off-on timings as per the provided timings array.
+ */
+ @Nullable
+ static VibrationEffect createEffectFromResource(Resources res, int resId) {
+ long[] timings = getLongIntArray(res, resId);
return createEffectFromTimings(timings);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 7996434..b55af76 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1016,8 +1016,7 @@
&& mWallpaper.userId == mCurrentUserId) {
Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.wallpaperComponent
+ ", reverting to built-in wallpaper!");
- clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId,
- null);
+ clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null);
}
}
};
@@ -1197,7 +1196,7 @@
} else {
// Timeout
Slog.w(TAG, "Reverting to built-in wallpaper!");
- clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
+ clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null);
final String flattened = wpService.flattenToString();
EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
flattened.substring(0, Math.min(flattened.length(),
@@ -1236,8 +1235,7 @@
} else {
if (mLmkLimitRebindRetries <= 0) {
Slog.w(TAG, "Reverting to built-in wallpaper due to lmk!");
- clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId,
- null);
+ clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null);
mLmkLimitRebindRetries = LMK_RECONNECT_REBIND_RETRIES;
return;
}
@@ -1256,7 +1254,7 @@
&& mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME
> SystemClock.uptimeMillis()) {
Slog.w(TAG, "Reverting to built-in wallpaper!");
- clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
+ clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null);
} else {
mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
tryToRebind();
@@ -1497,7 +1495,7 @@
wallpaper, null)) {
Slog.w(TAG, "Wallpaper " + wpService
+ " no longer available; reverting to default");
- clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
}
}
}
@@ -1585,7 +1583,7 @@
if (doit) {
Slog.w(TAG, "Wallpaper uninstalled, removing: "
+ wallpaper.wallpaperComponent);
- clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
}
}
}
@@ -1606,7 +1604,7 @@
} catch (NameNotFoundException e) {
Slog.w(TAG, "Wallpaper component gone, removing: "
+ wallpaper.wallpaperComponent);
- clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
}
}
if (wallpaper.nextWallpaperComponent != null
@@ -1638,7 +1636,8 @@
mShuttingDown = false;
mImageWallpaper = ComponentName.unflattenFromString(
context.getResources().getString(R.string.image_wallpaper_component));
- mDefaultWallpaperComponent = WallpaperManager.getCmfDefaultWallpaperComponent(context);
+ ComponentName defaultComponent = WallpaperManager.getCmfDefaultWallpaperComponent(context);
+ mDefaultWallpaperComponent = defaultComponent == null ? mImageWallpaper : defaultComponent;
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mIPackageManager = AppGlobals.getPackageManager();
@@ -1721,7 +1720,7 @@
if (DEBUG) {
Slog.i(TAG, "Unable to regenerate crop; resetting");
}
- clearWallpaperLocked(false, FLAG_SYSTEM, UserHandle.USER_SYSTEM, null);
+ clearWallpaperLocked(FLAG_SYSTEM, UserHandle.USER_SYSTEM, null);
}
} else {
if (DEBUG) {
@@ -1978,7 +1977,7 @@
}
if (si == null) {
- clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply);
+ clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, reply);
} else {
Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked");
// We might end up persisting the current wallpaper data
@@ -2002,10 +2001,10 @@
if (serviceInfo == null) {
if (wallpaper.mWhich == (FLAG_LOCK | FLAG_SYSTEM)) {
- clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
- clearWallpaperLocked(false, FLAG_LOCK, wallpaper.userId, reply);
+ clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(FLAG_LOCK, wallpaper.userId, reply);
} else {
- clearWallpaperLocked(false, wallpaper.mWhich, wallpaper.userId, reply);
+ clearWallpaperLocked(wallpaper.mWhich, wallpaper.userId, reply);
}
return;
}
@@ -2038,9 +2037,9 @@
WallpaperData data = null;
synchronized (mLock) {
if (mIsLockscreenLiveWallpaperEnabled) {
- clearWallpaperLocked(callingPackage, false, which, userId);
+ clearWallpaperLocked(callingPackage, which, userId);
} else {
- clearWallpaperLocked(false, which, userId, null);
+ clearWallpaperLocked(which, userId, null);
}
if (which == FLAG_LOCK) {
@@ -2058,8 +2057,7 @@
}
}
- private void clearWallpaperLocked(String callingPackage, boolean defaultFailed,
- int which, int userId) {
+ private void clearWallpaperLocked(String callingPackage, int which, int userId) {
// Might need to bring it in the first time to establish our rewrite
if (!mWallpaperMap.contains(userId)) {
@@ -2094,7 +2092,7 @@
component = wallpaper.wallpaperComponent;
finalWhich = FLAG_LOCK | FLAG_SYSTEM;
} else {
- component = defaultFailed ? mImageWallpaper : null;
+ component = null;
finalWhich = which;
}
@@ -2114,8 +2112,7 @@
}
// TODO(b/266818039) remove this version of the method
- private void clearWallpaperLocked(boolean defaultFailed, int which, int userId,
- IRemoteCallback reply) {
+ private void clearWallpaperLocked(int which, int userId, IRemoteCallback reply) {
if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to clear");
}
@@ -2168,9 +2165,7 @@
wallpaper.primaryColors = null;
wallpaper.imageWallpaperPending = false;
if (userId != mCurrentUserId) return;
- if (bindWallpaperComponentLocked(defaultFailed
- ? mImageWallpaper
- : null, true, false, wallpaper, reply)) {
+ if (bindWallpaperComponentLocked(null, true, false, wallpaper, reply)) {
return;
}
} catch (IllegalArgumentException e1) {
@@ -3523,11 +3518,6 @@
try {
if (componentName == null) {
componentName = mDefaultWallpaperComponent;
- if (componentName == null) {
- // Fall back to static image wallpaper
- componentName = mImageWallpaper;
- if (DEBUG_LIVE) Slog.v(TAG, "No default component; using image wallpaper");
- }
}
int serviceUserId = wallpaper.userId;
ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
@@ -3997,7 +3987,8 @@
mFallbackWallpaper = new WallpaperData(systemUserId, FLAG_SYSTEM);
mFallbackWallpaper.allowBackup = false;
mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked();
- bindWallpaperComponentLocked(mImageWallpaper, true, false, mFallbackWallpaper, null);
+ bindWallpaperComponentLocked(mDefaultWallpaperComponent, true, false,
+ mFallbackWallpaper, null);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index c5f63ce..a6d5c19 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -285,9 +285,9 @@
final LaunchingState mLaunchingState;
/** The type can be cold (new process), warm (new activity), or hot (bring to front). */
- final int mTransitionType;
+ int mTransitionType;
/** Whether the process was already running when the transition started. */
- final boolean mProcessRunning;
+ boolean mProcessRunning;
/** whether the process of the launching activity didn't have any active activity. */
final boolean mProcessSwitch;
/** The process state of the launching activity prior to the launch */
@@ -972,6 +972,19 @@
// App isn't attached to record yet, so match with info.
if (info.mLastLaunchedActivity.info.applicationInfo == appInfo) {
info.mBindApplicationDelayMs = info.calculateCurrentDelay();
+ if (info.mProcessRunning) {
+ // It was HOT/WARM launch, but the process was died somehow right after the
+ // launch request.
+ info.mProcessRunning = false;
+ info.mTransitionType = TYPE_TRANSITION_COLD_LAUNCH;
+ final String msg = "Process " + info.mLastLaunchedActivity.info.processName
+ + " restarted";
+ Slog.i(TAG, msg);
+ if (info.mLaunchingState.mTraceName != null) {
+ Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, msg + "#"
+ + LaunchingState.sTraceSeqId);
+ }
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e30673c..ea06b42 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7991,6 +7991,9 @@
mLastReportedConfiguration.getMergedConfiguration())) {
ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */,
false /* ignoreVisibility */, true /* isRequestedOrientationChanged */);
+ if (mTransitionController.inPlayingTransition(this)) {
+ mTransitionController.mValidateActivityCompat.add(this);
+ }
}
mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
@@ -9410,6 +9413,9 @@
if (info.applicationInfo == null) {
return info.getMinAspectRatio();
}
+ if (mLetterboxUiController.shouldApplyUserMinAspectRatioOverride()) {
+ return mLetterboxUiController.getUserMinAspectRatio();
+ }
if (!mLetterboxUiController.shouldOverrideMinAspectRatio()) {
return info.getMinAspectRatio();
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 50948e1..5553600 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2201,7 +2201,8 @@
displaySwapping |= s.isDisplaySleepingAndSwapping();
ProtoLog.v(WM_DEBUG_STATES, "Stopping %s: nowVisible=%b animating=%b "
+ "finishing=%s", s, s.nowVisible, animating, s.finishing);
- if ((!animating && !displaySwapping) || mService.mShuttingDown) {
+ if ((!animating && !displaySwapping) || mService.mShuttingDown
+ || s.getRootTask().isForceHiddenForPinnedTask()) {
if (!processPausingActivities && s.isState(PAUSING)) {
// Defer processing pausing activities in this iteration and reschedule
// a delayed idle to reprocess it again
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e261916..d675753 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2750,6 +2750,10 @@
void onAppTransitionDone() {
super.onAppTransitionDone();
mWmService.mWindowsChanged = true;
+ onTransitionFinished();
+ }
+
+ void onTransitionFinished() {
// If the transition finished callback cannot match the token for some reason, make sure the
// rotated state is cleared if it is already invisible.
if (mFixedRotationLaunchingApp != null && !mFixedRotationLaunchingApp.isVisibleRequested()
@@ -6404,9 +6408,9 @@
// Don't do recursive work.
return;
}
- mInEnsureActivitiesVisible = true;
mAtmService.mTaskSupervisor.beginActivityVisibilityUpdate();
try {
+ mInEnsureActivitiesVisible = true;
forAllRootTasks(rootTask -> {
rootTask.ensureActivitiesVisible(starting, configChanges, preserveWindows,
notifyClients);
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index 56edde0..271d71e 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -24,6 +24,7 @@
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityThread;
@@ -40,7 +41,6 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -48,7 +48,6 @@
import android.util.Slog;
import android.view.Display;
import android.view.Gravity;
-import android.view.IWindowManager;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -56,7 +55,6 @@
import android.view.WindowInsets;
import android.view.WindowInsets.Type;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.Button;
@@ -95,6 +93,10 @@
*/
@Nullable
private Context mWindowContext;
+ /**
+ * The root display area feature id that the {@link #mWindowContext} is attaching to.
+ */
+ private int mWindowContextRootDisplayAreaId = FEATURE_UNDEFINED;
// Local copy of vr mode enabled state, to avoid calling into VrManager with
// the lock held.
private boolean mVrModeEnabled;
@@ -206,12 +208,15 @@
private void handleHide() {
if (mClingWindow != null) {
if (DEBUG) Slog.d(TAG, "Hiding immersive mode confirmation");
- // We don't care which root display area the window manager is specifying for removal.
- try {
- getWindowManager(FEATURE_UNDEFINED).removeView(mClingWindow);
- } catch (WindowManager.InvalidDisplayException e) {
- Slog.w(TAG, "Fail to hide the immersive confirmation window because of " + e);
- return;
+ if (mWindowManager != null) {
+ try {
+ mWindowManager.removeView(mClingWindow);
+ } catch (WindowManager.InvalidDisplayException e) {
+ Slog.w(TAG, "Fail to hide the immersive confirmation window because of "
+ + e);
+ }
+ mWindowManager = null;
+ mWindowContext = null;
}
mClingWindow = null;
}
@@ -394,26 +399,18 @@
* @return the WindowManager specifying with the {@code rootDisplayAreaId} to attach the
* confirmation window.
*/
- private WindowManager getWindowManager(int rootDisplayAreaId) {
- if (mWindowManager == null || mWindowContext == null) {
- // Create window context to specify the RootDisplayArea
- final Bundle options = getOptionsForWindowContext(rootDisplayAreaId);
- mWindowContext = mContext.createWindowContext(
- IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options);
- mWindowManager = mWindowContext.getSystemService(WindowManager.class);
- return mWindowManager;
+ @NonNull
+ private WindowManager createWindowManager(int rootDisplayAreaId) {
+ if (mWindowManager != null) {
+ throw new IllegalStateException(
+ "Must not create a new WindowManager while there is an existing one");
}
-
- // Update the window context and window manager to specify the RootDisplayArea
+ // Create window context to specify the RootDisplayArea
final Bundle options = getOptionsForWindowContext(rootDisplayAreaId);
- final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
- try {
- wms.attachWindowContextToDisplayArea(mWindowContext.getWindowContextToken(),
- IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, mContext.getDisplayId(), options);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
-
+ mWindowContextRootDisplayAreaId = rootDisplayAreaId;
+ mWindowContext = mContext.createWindowContext(
+ IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options);
+ mWindowManager = mWindowContext.getSystemService(WindowManager.class);
return mWindowManager;
}
@@ -434,14 +431,23 @@
}
private void handleShow(int rootDisplayAreaId) {
+ if (mClingWindow != null) {
+ if (rootDisplayAreaId == mWindowContextRootDisplayAreaId) {
+ if (DEBUG) Slog.d(TAG, "Immersive mode confirmation has already been shown");
+ return;
+ } else {
+ // Hide the existing confirmation before show a new one in the new root.
+ if (DEBUG) Slog.d(TAG, "Immersive mode confirmation was shown in a different root");
+ handleHide();
+ }
+ }
+
if (DEBUG) Slog.d(TAG, "Showing immersive mode confirmation");
-
mClingWindow = new ClingWindowView(mContext, mConfirm);
-
// show the confirmation
- WindowManager.LayoutParams lp = getClingWindowLayoutParams();
+ final WindowManager.LayoutParams lp = getClingWindowLayoutParams();
try {
- getWindowManager(rootDisplayAreaId).addView(mClingWindow, lp);
+ createWindowManager(rootDisplayAreaId).addView(mClingWindow, lp);
} catch (WindowManager.InvalidDisplayException e) {
Slog.w(TAG, "Fail to show the immersive confirmation window because of " + e);
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 2b8312c..5f3d517 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -81,6 +81,7 @@
private boolean mIsLeashReadyForDispatching;
private final Rect mSourceFrame = new Rect();
private final Rect mLastSourceFrame = new Rect();
+ private final Rect mLastContainerBounds = new Rect();
private @NonNull Insets mInsetsHint = Insets.NONE;
private @Flags int mFlagsFromFrameProvider;
private @Flags int mFlagsFromServer;
@@ -278,11 +279,31 @@
// visible. (i.e. No surface, pending insets that were given during layout, etc..)
if (mServerVisible) {
mSource.setFrame(mSourceFrame);
+ updateInsetsHint();
} else {
mSource.setFrame(0, 0, 0, 0);
}
}
+ // To be called when mSourceFrame or the window container bounds is changed.
+ private void updateInsetsHint() {
+ if (!mControllable || !mServerVisible) {
+ return;
+ }
+ final Rect bounds = mWindowContainer.getBounds();
+ if (mSourceFrame.equals(mLastSourceFrame) && bounds.equals(mLastContainerBounds)) {
+ return;
+ }
+ mLastSourceFrame.set(mSourceFrame);
+ mLastContainerBounds.set(bounds);
+ mInsetsHint = mSource.calculateInsets(bounds, true /* ignoreVisibility */);
+ }
+
+ @VisibleForTesting
+ Insets getInsetsHint() {
+ return mInsetsHint;
+ }
+
/** @return A new source computed by the specified window frame in the given display frames. */
InsetsSource createSimulatedSource(DisplayFrames displayFrames, Rect frame) {
final InsetsSource source = new InsetsSource(mSource);
@@ -338,15 +359,9 @@
mSetLeashPositionConsumer.accept(t);
}
}
- if (mServerVisible && !mLastSourceFrame.equals(mSource.getFrame())) {
- final Insets insetsHint = mSource.calculateInsets(
- mWindowContainer.getBounds(), true /* ignoreVisibility */);
- if (!insetsHint.equals(mControl.getInsetsHint())) {
- changed = true;
- mControl.setInsetsHint(insetsHint);
- mInsetsHint = insetsHint;
- }
- mLastSourceFrame.set(mSource.getFrame());
+ if (!mControl.getInsetsHint().equals(mInsetsHint)) {
+ mControl.setInsetsHint(mInsetsHint);
+ changed = true;
}
if (changed) {
mStateController.notifyControlChanged(mControlTarget);
@@ -587,6 +602,11 @@
pw.print(prefix + "mControl=");
mControl.dump("", pw);
}
+ if (mControllable) {
+ pw.print(prefix + "mInsetsHint=");
+ pw.print(mInsetsHint);
+ pw.println();
+ }
pw.print(prefix);
pw.print("mIsLeashReadyForDispatching="); pw.print(mIsLeashReadyForDispatching);
pw.println();
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 7a201a7..e945bc1 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -294,16 +294,15 @@
@NonNull private final SynchedDeviceConfig mDeviceConfig;
LetterboxConfiguration(@NonNull final Context systemUiContext) {
- this(systemUiContext,
- new LetterboxConfigurationPersister(systemUiContext,
- () -> readLetterboxHorizontalReachabilityPositionFromConfig(
- systemUiContext, /* forBookMode */ false),
- () -> readLetterboxVerticalReachabilityPositionFromConfig(
- systemUiContext, /* forTabletopMode */ false),
- () -> readLetterboxHorizontalReachabilityPositionFromConfig(
- systemUiContext, /* forBookMode */ true),
- () -> readLetterboxVerticalReachabilityPositionFromConfig(
- systemUiContext, /* forTabletopMode */ true)));
+ this(systemUiContext, new LetterboxConfigurationPersister(
+ () -> readLetterboxHorizontalReachabilityPositionFromConfig(
+ systemUiContext, /* forBookMode */ false),
+ () -> readLetterboxVerticalReachabilityPositionFromConfig(
+ systemUiContext, /* forTabletopMode */ false),
+ () -> readLetterboxHorizontalReachabilityPositionFromConfig(
+ systemUiContext, /* forBookMode */ true),
+ () -> readLetterboxVerticalReachabilityPositionFromConfig(
+ systemUiContext, /* forTabletopMode */ true)));
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java b/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java
index 7563397..38aa903 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java
@@ -23,7 +23,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Context;
import android.os.Environment;
import android.os.StrictMode;
import android.os.StrictMode.ThreadPolicy;
@@ -53,10 +52,8 @@
private static final String TAG =
TAG_WITH_CLASS_NAME ? "LetterboxConfigurationPersister" : TAG_WM;
- @VisibleForTesting
- static final String LETTERBOX_CONFIGURATION_FILENAME = "letterbox_config";
+ private static final String LETTERBOX_CONFIGURATION_FILENAME = "letterbox_config";
- private final Context mContext;
private final Supplier<Integer> mDefaultHorizontalReachabilitySupplier;
private final Supplier<Integer> mDefaultVerticalReachabilitySupplier;
private final Supplier<Integer> mDefaultBookModeReachabilitySupplier;
@@ -97,36 +94,32 @@
@NonNull
private final PersisterQueue mPersisterQueue;
- LetterboxConfigurationPersister(Context systemUiContext,
- Supplier<Integer> defaultHorizontalReachabilitySupplier,
- Supplier<Integer> defaultVerticalReachabilitySupplier,
- Supplier<Integer> defaultBookModeReachabilitySupplier,
- Supplier<Integer> defaultTabletopModeReachabilitySupplier) {
- this(systemUiContext, defaultHorizontalReachabilitySupplier,
- defaultVerticalReachabilitySupplier,
- defaultBookModeReachabilitySupplier,
- defaultTabletopModeReachabilitySupplier,
+ LetterboxConfigurationPersister(
+ @NonNull Supplier<Integer> defaultHorizontalReachabilitySupplier,
+ @NonNull Supplier<Integer> defaultVerticalReachabilitySupplier,
+ @NonNull Supplier<Integer> defaultBookModeReachabilitySupplier,
+ @NonNull Supplier<Integer> defaultTabletopModeReachabilitySupplier) {
+ this(defaultHorizontalReachabilitySupplier, defaultVerticalReachabilitySupplier,
+ defaultBookModeReachabilitySupplier, defaultTabletopModeReachabilitySupplier,
Environment.getDataSystemDirectory(), new PersisterQueue(),
- /* completionCallback */ null);
+ /* completionCallback */ null, LETTERBOX_CONFIGURATION_FILENAME);
}
@VisibleForTesting
- LetterboxConfigurationPersister(Context systemUiContext,
- Supplier<Integer> defaultHorizontalReachabilitySupplier,
- Supplier<Integer> defaultVerticalReachabilitySupplier,
- Supplier<Integer> defaultBookModeReachabilitySupplier,
- Supplier<Integer> defaultTabletopModeReachabilitySupplier,
- File configFolder,
- PersisterQueue persisterQueue, @Nullable Consumer<String> completionCallback) {
- mContext = systemUiContext.createDeviceProtectedStorageContext();
+ LetterboxConfigurationPersister(
+ @NonNull Supplier<Integer> defaultHorizontalReachabilitySupplier,
+ @NonNull Supplier<Integer> defaultVerticalReachabilitySupplier,
+ @NonNull Supplier<Integer> defaultBookModeReachabilitySupplier,
+ @NonNull Supplier<Integer> defaultTabletopModeReachabilitySupplier,
+ @NonNull File configFolder, @NonNull PersisterQueue persisterQueue,
+ @Nullable Consumer<String> completionCallback,
+ @NonNull String letterboxConfigurationFileName) {
mDefaultHorizontalReachabilitySupplier = defaultHorizontalReachabilitySupplier;
mDefaultVerticalReachabilitySupplier = defaultVerticalReachabilitySupplier;
- mDefaultBookModeReachabilitySupplier =
- defaultBookModeReachabilitySupplier;
- mDefaultTabletopModeReachabilitySupplier =
- defaultTabletopModeReachabilitySupplier;
+ mDefaultBookModeReachabilitySupplier = defaultBookModeReachabilitySupplier;
+ mDefaultTabletopModeReachabilitySupplier = defaultTabletopModeReachabilitySupplier;
mCompletionCallback = completionCallback;
- final File prefFiles = new File(configFolder, LETTERBOX_CONFIGURATION_FILENAME);
+ final File prefFiles = new File(configFolder, letterboxConfigurationFileName);
mConfigurationFile = new AtomicFile(prefFiles);
mPersisterQueue = persisterQueue;
runWithDiskReadsThreadPolicy(this::readCurrentConfiguration);
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 39f7570..394105a 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -40,6 +40,12 @@
import static android.content.pm.ActivityInfo.isFixedOrientation;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.screenOrientationToString;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
@@ -103,6 +109,7 @@
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.RemoteException;
import android.util.Slog;
import android.view.InsetsSource;
import android.view.InsetsState;
@@ -237,6 +244,10 @@
// Counter for ActivityRecord#setRequestedOrientation
private int mSetOrientationRequestCounter = 0;
+ // The min aspect ratio override set by user
+ @PackageManager.UserMinAspectRatio
+ private int mUserAspectRatio = USER_MIN_ASPECT_RATIO_UNSET;
+
// The CompatDisplayInsets of the opaque activity beneath the translucent one.
private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets;
@@ -1059,7 +1070,7 @@
private float getDefaultMinAspectRatioForUnresizableApps() {
if (!mLetterboxConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled()
- || mActivityRecord.getDisplayContent() == null) {
+ || mActivityRecord.getDisplayArea() == null) {
return mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()
> MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
? mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()
@@ -1071,8 +1082,8 @@
float getSplitScreenAspectRatio() {
// Getting the same aspect ratio that apps get in split screen.
- final DisplayContent displayContent = mActivityRecord.getDisplayContent();
- if (displayContent == null) {
+ final DisplayArea displayArea = mActivityRecord.getDisplayArea();
+ if (displayArea == null) {
return getDefaultMinAspectRatioForUnresizableApps();
}
int dividerWindowWidth =
@@ -1080,7 +1091,7 @@
int dividerInsets =
getResources().getDimensionPixelSize(R.dimen.docked_stack_divider_insets);
int dividerSize = dividerWindowWidth - dividerInsets * 2;
- final Rect bounds = new Rect(displayContent.getWindowConfiguration().getAppBounds());
+ final Rect bounds = new Rect(displayArea.getWindowConfiguration().getAppBounds());
if (bounds.width() >= bounds.height()) {
bounds.inset(/* dx */ dividerSize / 2, /* dy */ 0);
bounds.right = bounds.centerX();
@@ -1091,14 +1102,57 @@
return computeAspectRatio(bounds);
}
+ boolean shouldApplyUserMinAspectRatioOverride() {
+ if (!mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()) {
+ return false;
+ }
+
+ try {
+ final int userAspectRatio = mActivityRecord.mAtmService.getPackageManager()
+ .getUserMinAspectRatio(mActivityRecord.packageName, mActivityRecord.mUserId);
+ mUserAspectRatio = userAspectRatio;
+ return userAspectRatio != USER_MIN_ASPECT_RATIO_UNSET;
+ } catch (RemoteException e) {
+ // Don't apply user aspect ratio override
+ Slog.w(TAG, "Exception thrown retrieving aspect ratio user override " + this, e);
+ return false;
+ }
+ }
+
+ float getUserMinAspectRatio() {
+ switch (mUserAspectRatio) {
+ case USER_MIN_ASPECT_RATIO_DISPLAY_SIZE:
+ return getDisplaySizeMinAspectRatio();
+ case USER_MIN_ASPECT_RATIO_SPLIT_SCREEN:
+ return getSplitScreenAspectRatio();
+ case USER_MIN_ASPECT_RATIO_16_9:
+ return 16 / 9f;
+ case USER_MIN_ASPECT_RATIO_4_3:
+ return 4 / 3f;
+ case USER_MIN_ASPECT_RATIO_3_2:
+ return 3 / 2f;
+ default:
+ throw new AssertionError("Unexpected user min aspect ratio override: "
+ + mUserAspectRatio);
+ }
+ }
+
+ private float getDisplaySizeMinAspectRatio() {
+ final DisplayArea displayArea = mActivityRecord.getDisplayArea();
+ if (displayArea == null) {
+ return mActivityRecord.info.getMinAspectRatio();
+ }
+ final Rect bounds = new Rect(displayArea.getWindowConfiguration().getAppBounds());
+ return computeAspectRatio(bounds);
+ }
+
private float getDefaultMinAspectRatio() {
- final DisplayContent displayContent = mActivityRecord.getDisplayContent();
- if (displayContent == null
+ if (mActivityRecord.getDisplayArea() == null
|| !mLetterboxConfiguration
.getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox()) {
return mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
}
- return computeAspectRatio(new Rect(displayContent.getBounds()));
+ return getDisplaySizeMinAspectRatio();
}
Resources getResources() {
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index b71d918b..f33ecaa 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -684,6 +684,26 @@
}
}
+ /**
+ * Removes the oldest recent task that is compatible with the given one. This is possible if
+ * the task windowing mode changed after being added to the Recents.
+ */
+ void removeCompatibleRecentTask(Task task) {
+ final int taskIndex = mTasks.indexOf(task);
+ if (taskIndex < 0) {
+ return;
+ }
+
+ final int candidateIndex = findRemoveIndexForTask(task, false /* includingSelf */);
+ if (candidateIndex == -1) {
+ // Nothing to trim
+ return;
+ }
+
+ final Task taskToRemove = taskIndex > candidateIndex ? task : mTasks.get(candidateIndex);
+ remove(taskToRemove);
+ }
+
void removeTasksByPackageName(String packageName, int userId) {
for (int i = mTasks.size() - 1; i >= 0; --i) {
final Task task = mTasks.get(i);
@@ -1546,6 +1566,10 @@
* list (if any).
*/
private int findRemoveIndexForAddTask(Task task) {
+ return findRemoveIndexForTask(task, true /* includingSelf */);
+ }
+
+ private int findRemoveIndexForTask(Task task, boolean includingSelf) {
final int recentsCount = mTasks.size();
final Intent intent = task.intent;
final boolean document = intent != null && intent.isDocument();
@@ -1601,6 +1625,8 @@
// existing task
continue;
}
+ } else if (!includingSelf) {
+ continue;
}
return i;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 6e46f3f..2f0c303 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1849,9 +1849,8 @@
// Don't do recursive work.
return;
}
-
+ mTaskSupervisor.beginActivityVisibilityUpdate();
try {
- mTaskSupervisor.beginActivityVisibilityUpdate();
// First the front root tasks. In case any are not fullscreen and are in front of home.
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
final DisplayContent display = getChildAt(displayNdx);
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index c914fa1..fe3094e 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -293,26 +293,7 @@
throw new SecurityException(msg);
}
// Check if the caller is allowed to launch on the specified display area.
- final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
- TaskDisplayArea taskDisplayArea = daToken != null
- ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
-
- // If we do not have a task display area token, check if the launch task display area
- // feature id is specified.
- if (taskDisplayArea == null) {
- final int launchTaskDisplayAreaFeatureId = options.getLaunchTaskDisplayAreaFeatureId();
- if (launchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) {
- final int launchDisplayId = options.getLaunchDisplayId() == INVALID_DISPLAY
- ? DEFAULT_DISPLAY : options.getLaunchDisplayId();
- final DisplayContent dc = supervisor.mRootWindowContainer
- .getDisplayContent(launchDisplayId);
- if (dc != null) {
- taskDisplayArea = dc.getItemFromTaskDisplayAreas(tda ->
- tda.mFeatureId == launchTaskDisplayAreaFeatureId ? tda : null);
- }
- }
- }
-
+ final TaskDisplayArea taskDisplayArea = getLaunchTaskDisplayArea(options, supervisor);
if (aInfo != null && taskDisplayArea != null
&& !supervisor.isCallerAllowedToLaunchOnTaskDisplayArea(callingPid, callingUid,
taskDisplayArea, aInfo)) {
@@ -428,6 +409,32 @@
}
}
+ @VisibleForTesting
+ TaskDisplayArea getLaunchTaskDisplayArea(ActivityOptions options,
+ ActivityTaskSupervisor supervisor) {
+ final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
+ TaskDisplayArea taskDisplayArea = daToken != null
+ ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
+ if (taskDisplayArea != null) {
+ return taskDisplayArea;
+ }
+
+ // If we do not have a task display area token, check if the launch task display area
+ // feature id is specified.
+ final int launchTaskDisplayAreaFeatureId = options.getLaunchTaskDisplayAreaFeatureId();
+ if (launchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) {
+ final int launchDisplayId = options.getLaunchDisplayId() == INVALID_DISPLAY
+ ? DEFAULT_DISPLAY : options.getLaunchDisplayId();
+ final DisplayContent dc = supervisor.mRootWindowContainer
+ .getDisplayContent(launchDisplayId);
+ if (dc != null) {
+ taskDisplayArea = dc.getItemFromTaskDisplayAreas(tda ->
+ tda.mFeatureId == launchTaskDisplayAreaFeatureId ? tda : null);
+ }
+ }
+ return taskDisplayArea;
+ }
+
private boolean isAssistant(ActivityTaskManagerService atmService, int callingUid) {
if (atmService.mActiveVoiceInteractionServiceComponent == null) {
return false;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 92e90ae..640ae3f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4504,6 +4504,10 @@
return mForceHiddenFlags != 0;
}
+ boolean isForceHiddenForPinnedTask() {
+ return (mForceHiddenFlags & FLAG_FORCE_HIDDEN_FOR_PINNED_TASK) != 0;
+ }
+
@Override
protected boolean isForceTranslucent() {
return mForceTranslucent;
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 0c1f33c..92c0987 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1020,7 +1020,7 @@
final WindowContainer<?> parent = getParent();
final Task thisTask = asTask();
if (thisTask != null && parent.asTask() == null
- && mTransitionController.isTransientHide(thisTask)) {
+ && mTransitionController.isTransientVisible(thisTask)) {
// Keep transient-hide root tasks visible. Non-root tasks still follow standard rule.
return TASK_FRAGMENT_VISIBILITY_VISIBLE;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index a143540..789e3d2 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -407,6 +407,36 @@
return false;
}
+ /** Returns {@code true} if the task should keep visible if this is a transient transition. */
+ boolean isTransientVisible(@NonNull Task task) {
+ if (mTransientLaunches == null) return false;
+ int occludedCount = 0;
+ final int numTransient = mTransientLaunches.size();
+ for (int i = numTransient - 1; i >= 0; --i) {
+ final Task transientRoot = mTransientLaunches.keyAt(i).getRootTask();
+ if (transientRoot == null) continue;
+ final WindowContainer<?> rootParent = transientRoot.getParent();
+ if (rootParent == null || rootParent.getTopChild() == transientRoot) continue;
+ final ActivityRecord topOpaque = mController.mAtm.mTaskSupervisor
+ .mOpaqueActivityHelper.getOpaqueActivity(rootParent);
+ if (transientRoot.compareTo(topOpaque.getRootTask()) < 0) {
+ occludedCount++;
+ }
+ }
+ if (occludedCount == numTransient) {
+ for (int i = mTransientLaunches.size() - 1; i >= 0; --i) {
+ if (mTransientLaunches.keyAt(i).isDescendantOf(task)) {
+ // Keep transient activity visible until transition finished, so it won't pause
+ // with transient-hide tasks that may delay resuming the next top.
+ return true;
+ }
+ }
+ // Let transient-hide activities pause before transition is finished.
+ return false;
+ }
+ return isInTransientHide(task);
+ }
+
boolean canApplyDim(@NonNull Task task) {
if (mTransientLaunches == null) return true;
final Dimmer dimmer = task.getDimmer();
@@ -1298,6 +1328,7 @@
if (asyncRotationController != null && containsChangeFor(dc, mTargets)) {
asyncRotationController.onTransitionFinished();
}
+ dc.onTransitionFinished();
if (hasParticipatedDisplay && dc.mDisplayRotationCompatPolicy != null) {
final ChangeInfo changeInfo = mChanges.get(dc);
if (changeInfo != null
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 881eddc..dfaa174 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -30,6 +30,7 @@
import android.app.ActivityManager;
import android.app.IApplicationThread;
import android.app.WindowConfiguration;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
@@ -141,6 +142,14 @@
final ArrayList<ActivityRecord> mValidateCommitVis = new ArrayList<>();
/**
+ * List of activity-level participants. ActivityRecord is not expected to change independently,
+ * however, recent compatibility logic can now cause this at arbitrary times determined by
+ * client code. If it happens during an animation, the surface can be left at the wrong spot.
+ * TODO(b/290237710) remove when compat logic is moved.
+ */
+ final ArrayList<ActivityRecord> mValidateActivityCompat = new ArrayList<>();
+
+ /**
* Currently playing transitions (in the order they were started). When finished, records are
* removed from this list.
*/
@@ -468,15 +477,22 @@
if (mCollectingTransition != null && mCollectingTransition.isInTransientHide(task)) {
return true;
}
- for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) {
- if (mWaitingTransitions.get(i).isInTransientHide(task)) return true;
- }
for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
if (mPlayingTransitions.get(i).isInTransientHide(task)) return true;
}
return false;
}
+ boolean isTransientVisible(@NonNull Task task) {
+ if (mCollectingTransition != null && mCollectingTransition.isTransientVisible(task)) {
+ return true;
+ }
+ for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
+ if (mPlayingTransitions.get(i).isTransientVisible(task)) return true;
+ }
+ return false;
+ }
+
boolean canApplyDim(@Nullable Task task) {
if (task == null) {
// Always allow non-activity window.
@@ -896,6 +912,14 @@
}
}
mValidateCommitVis.clear();
+ for (int i = 0; i < mValidateActivityCompat.size(); ++i) {
+ ActivityRecord ar = mValidateActivityCompat.get(i);
+ if (ar.getSurfaceControl() == null) continue;
+ final Point tmpPos = new Point();
+ ar.getRelativePosition(tmpPos);
+ ar.getSyncTransaction().setPosition(ar.getSurfaceControl(), tmpPos.x, tmpPos.y);
+ }
+ mValidateActivityCompat.clear();
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 0eb452d..164c8b0 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1615,6 +1615,7 @@
final int count = tasksToReparent.size();
for (int i = 0; i < count; ++i) {
final Task task = tasksToReparent.get(i);
+ final int prevWindowingMode = task.getWindowingMode();
if (syncId >= 0) {
addToSyncSet(syncId, task);
}
@@ -1628,6 +1629,12 @@
hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
false /*moveParents*/, "processChildrenTaskReparentHierarchyOp");
}
+ // Trim the compatible Recent task (if any) after the Task is reparented and now has
+ // a different windowing mode, in order to prevent redundant Recent tasks after
+ // reparenting.
+ if (prevWindowingMode != task.getWindowingMode()) {
+ mService.mTaskSupervisor.mRecentTasks.removeCompatibleRecentTask(task);
+ }
}
if (transition != null) transition.collect(newParent);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/StringSetPolicySerializer.java b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetPolicySerializer.java
index 24d0521..0265453 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/StringSetPolicySerializer.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetPolicySerializer.java
@@ -32,9 +32,8 @@
// TODO(scottjonathan): Replace with generic set implementation
final class StringSetPolicySerializer extends PolicySerializer<Set<String>> {
- private static final String ATTR_VALUES = ":strings";
+ private static final String ATTR_VALUES = "strings";
private static final String ATTR_VALUES_SEPARATOR = ";";
-
@Override
void saveToXml(PolicyKey policyKey, TypedXmlSerializer serializer,
@NonNull Set<String> value) throws IOException {
diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING
index be7feb5..648b904 100644
--- a/services/incremental/TEST_MAPPING
+++ b/services/incremental/TEST_MAPPING
@@ -9,6 +9,9 @@
]
},
{
+ "name": "CtsPackageManagerIncrementalStatsHostTestCases"
+ },
+ {
"name": "CtsIncrementalInstallHostTestCases"
},
{
diff --git a/services/permission/OWNERS b/services/permission/OWNERS
index 6c6c9fc..e464038 100644
--- a/services/permission/OWNERS
+++ b/services/permission/OWNERS
@@ -1,4 +1,5 @@
-ashfall@google.com
+#Bug component: 137825
+
joecastro@google.com
ntmyren@google.com
zhanghai@google.com
diff --git a/services/tests/InputMethodSystemServerTests/AndroidManifest.xml b/services/tests/InputMethodSystemServerTests/AndroidManifest.xml
index 212ec14..bef56ce 100644
--- a/services/tests/InputMethodSystemServerTests/AndroidManifest.xml
+++ b/services/tests/InputMethodSystemServerTests/AndroidManifest.xml
@@ -17,7 +17,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworks.inputmethodtests">
- <uses-sdk android:targetSdkVersion="31" />
<queries>
<intent>
<action android:name="android.view.InputMethod" />
diff --git a/services/tests/InputMethodSystemServerTests/TEST_MAPPING b/services/tests/InputMethodSystemServerTests/TEST_MAPPING
index 77e32a7..cedbfd2b 100644
--- a/services/tests/InputMethodSystemServerTests/TEST_MAPPING
+++ b/services/tests/InputMethodSystemServerTests/TEST_MAPPING
@@ -9,5 +9,16 @@
{"exclude-annotation": "org.junit.Ignore"}
]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksImeTests",
+ "options": [
+ {"include-filter": "com.android.inputmethodservice"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ }
]
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidManifest.xml b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidManifest.xml
index 0104f71..b7de749 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidManifest.xml
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidManifest.xml
@@ -18,8 +18,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.inputmethod.imetests">
- <uses-sdk android:targetSdkVersion="31" />
-
<!-- Permissions required for granting and logging -->
<uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
<uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
index 898658e..e8acb06 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
@@ -20,6 +20,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
import android.app.Instrumentation;
import android.content.Context;
import android.content.res.Configuration;
@@ -45,6 +47,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -56,9 +59,9 @@
private static final String EDIT_TEXT_DESC = "Input box";
private static final long TIMEOUT_IN_SECONDS = 3;
private static final String ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD =
- "settings put secure show_ime_with_hard_keyboard 1";
+ "settings put secure " + Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD + " 1";
private static final String DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD =
- "settings put secure show_ime_with_hard_keyboard 0";
+ "settings put secure " + Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD + " 0";
private Instrumentation mInstrumentation;
private UiDevice mUiDevice;
@@ -82,29 +85,19 @@
mUiDevice.freezeRotation();
mUiDevice.setOrientationNatural();
// Waits for input binding ready.
- eventually(
- () -> {
- mInputMethodService =
- InputMethodServiceWrapper.getInputMethodServiceWrapperForTesting();
- assertThat(mInputMethodService).isNotNull();
+ eventually(() -> {
+ mInputMethodService =
+ InputMethodServiceWrapper.getInputMethodServiceWrapperForTesting();
+ assertThat(mInputMethodService).isNotNull();
- // The editor won't bring up keyboard by default.
- assertThat(mInputMethodService.getCurrentInputStarted()).isTrue();
- assertThat(mInputMethodService.getCurrentInputViewStarted()).isFalse();
- });
- // Save the original value of show_ime_with_hard_keyboard in Settings.
+ // The editor won't bring up keyboard by default.
+ assertThat(mInputMethodService.getCurrentInputStarted()).isTrue();
+ assertThat(mInputMethodService.getCurrentInputViewStarted()).isFalse();
+ });
+ // Save the original value of show_ime_with_hard_keyboard from Settings.
mShowImeWithHardKeyboardEnabled = Settings.Secure.getInt(
mInputMethodService.getContentResolver(),
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0;
- // Disable showing Ime with hard keyboard because it is the precondition the for most test
- // cases
- if (mShowImeWithHardKeyboardEnabled) {
- executeShellCommand(DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD);
- }
- mInputMethodService.getResources().getConfiguration().keyboard =
- Configuration.KEYBOARD_NOKEYS;
- mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
- Configuration.HARDKEYBOARDHIDDEN_YES;
}
@After
@@ -112,82 +105,141 @@
mUiDevice.unfreezeRotation();
executeShellCommand("ime disable " + mInputMethodId);
// Change back the original value of show_ime_with_hard_keyboard in Settings.
- executeShellCommand(mShowImeWithHardKeyboardEnabled ? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD
+ executeShellCommand(mShowImeWithHardKeyboardEnabled
+ ? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD
: DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD);
}
+ /**
+ * This checks that the IME can be shown and hidden by user actions
+ * (i.e. tapping on an EditText, tapping the Home button).
+ */
@Test
- public void testShowHideKeyboard_byUserAction() throws InterruptedException {
+ public void testShowHideKeyboard_byUserAction() throws Exception {
+ setShowImeWithHardKeyboard(true /* enabled */);
+
// Performs click on editor box to bring up the soft keyboard.
Log.i(TAG, "Click on EditText.");
- verifyInputViewStatus(() -> clickOnEditorText(), true /* inputViewStarted */);
-
- // Press back key to hide soft keyboard.
- Log.i(TAG, "Press back");
verifyInputViewStatus(
- () -> assertThat(mUiDevice.pressHome()).isTrue(), false /* inputViewStarted */);
+ () -> clickOnEditorText(),
+ true /* expected */,
+ true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
+
+ // Press home key to hide soft keyboard.
+ Log.i(TAG, "Press home");
+ verifyInputViewStatus(
+ () -> assertThat(mUiDevice.pressHome()).isTrue(),
+ true /* expected */,
+ false /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isFalse();
}
+ /**
+ * This checks that the IME can be shown and hidden using the WindowInsetsController APIs.
+ */
@Test
- public void testShowHideKeyboard_byApi() throws InterruptedException {
+ public void testShowHideKeyboard_byApi() throws Exception {
+ setShowImeWithHardKeyboard(true /* enabled */);
+
// Triggers to show IME via public API.
verifyInputViewStatus(
() -> assertThat(mActivity.showImeWithWindowInsetsController()).isTrue(),
+ true /* expected */,
true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
// Triggers to hide IME via public API.
verifyInputViewStatusOnMainSync(
- () -> assertThat(mActivity.hideImeWithInputMethodManager(0 /* flags */)).isTrue(),
+ () -> assertThat(mActivity.hideImeWithWindowInsetsController()).isTrue(),
+ true /* expected */,
false /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isFalse();
}
+ /**
+ * This checks the result of calling IMS#requestShowSelf and IMS#requestHideSelf.
+ */
@Test
- public void testShowHideSelf() throws InterruptedException {
- // IME requests to show itself without any flags: expect shown.
+ public void testShowHideSelf() throws Exception {
+ setShowImeWithHardKeyboard(true /* enabled */);
+
+ // IME request to show itself without any flags, expect shown.
Log.i(TAG, "Call IMS#requestShowSelf(0)");
verifyInputViewStatusOnMainSync(
- () -> mInputMethodService.requestShowSelf(0), true /* inputViewStarted */);
+ () -> mInputMethodService.requestShowSelf(0 /* flags */),
+ true /* expected */,
+ true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
- // IME requests to hide itself with flag: HIDE_IMPLICIT_ONLY, expect not hide (shown).
+ // IME request to hide itself with flag HIDE_IMPLICIT_ONLY, expect not hide (shown).
Log.i(TAG, "Call IMS#requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY)");
verifyInputViewStatusOnMainSync(
() -> mInputMethodService.requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY),
+ false /* expected */,
true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
- // IME request to hide itself without any flags: expect hidden.
+ // IME request to hide itself without any flags, expect hidden.
Log.i(TAG, "Call IMS#requestHideSelf(0)");
verifyInputViewStatusOnMainSync(
- () -> mInputMethodService.requestHideSelf(0), false /* inputViewStarted */);
+ () -> mInputMethodService.requestHideSelf(0 /* flags */),
+ true /* expected */,
+ false /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isFalse();
- // IME request to show itself with flag SHOW_IMPLICIT: expect shown.
+ // IME request to show itself with flag SHOW_IMPLICIT, expect shown.
Log.i(TAG, "Call IMS#requestShowSelf(InputMethodManager.SHOW_IMPLICIT)");
verifyInputViewStatusOnMainSync(
() -> mInputMethodService.requestShowSelf(InputMethodManager.SHOW_IMPLICIT),
+ true /* expected */,
true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
- // IME request to hide itself with flag: HIDE_IMPLICIT_ONLY, expect hidden.
+ // IME request to hide itself with flag HIDE_IMPLICIT_ONLY, expect hidden.
Log.i(TAG, "Call IMS#requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY)");
verifyInputViewStatusOnMainSync(
() -> mInputMethodService.requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY),
+ true /* expected */,
false /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isFalse();
}
+ /**
+ * This checks the return value of IMS#onEvaluateInputViewShown,
+ * when show_ime_with_hard_keyboard is enabled.
+ */
@Test
public void testOnEvaluateInputViewShown_showImeWithHardKeyboard() throws Exception {
- executeShellCommand(ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD);
- mInstrumentation.waitForIdleSync();
+ setShowImeWithHardKeyboard(true /* enabled */);
- // Simulate connecting a hard keyboard
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_QWERTY;
mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
Configuration.HARDKEYBOARDHIDDEN_NO;
+ eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue());
+ mInputMethodService.getResources().getConfiguration().keyboard =
+ Configuration.KEYBOARD_NOKEYS;
+ mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+ Configuration.HARDKEYBOARDHIDDEN_NO;
+ eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue());
+
+ mInputMethodService.getResources().getConfiguration().keyboard =
+ Configuration.KEYBOARD_QWERTY;
+ mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+ Configuration.HARDKEYBOARDHIDDEN_YES;
eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue());
}
+ /**
+ * This checks the return value of IMSonEvaluateInputViewShown,
+ * when show_ime_with_hard_keyboard is disabled.
+ */
@Test
- public void testOnEvaluateInputViewShown_disableShowImeWithHardKeyboard() {
+ public void testOnEvaluateInputViewShown_disableShowImeWithHardKeyboard() throws Exception {
+ setShowImeWithHardKeyboard(false /* enabled */);
+
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_QWERTY;
mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
@@ -196,6 +248,8 @@
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_NOKEYS;
+ mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+ Configuration.HARDKEYBOARDHIDDEN_NO;
eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue());
mInputMethodService.getResources().getConfiguration().keyboard =
@@ -205,149 +259,386 @@
eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue());
}
+ /**
+ * This checks that any (implicit or explicit) show request,
+ * when IMS#onEvaluateInputViewShown returns false, results in the IME not being shown.
+ */
@Test
public void testShowSoftInput_disableShowImeWithHardKeyboard() throws Exception {
- // Simulate connecting a hard keyboard
+ setShowImeWithHardKeyboard(false /* enabled */);
+
+ // Simulate connecting a hard keyboard.
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_QWERTY;
mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
Configuration.HARDKEYBOARDHIDDEN_NO;
+
// When InputMethodService#onEvaluateInputViewShown() returns false, the Ime should not be
// shown no matter what the show flag is.
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
+ false /* expected */,
false /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isFalse();
+
verifyInputViewStatusOnMainSync(
() -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
+ false /* expected */,
false /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isFalse();
}
+ /**
+ * This checks that an explicit show request results in the IME being shown.
+ */
@Test
public void testShowSoftInputExplicitly() throws Exception {
+ setShowImeWithHardKeyboard(true /* enabled */);
+
// When InputMethodService#onEvaluateInputViewShown() returns true and flag is EXPLICIT, the
// Ime should be shown.
verifyInputViewStatusOnMainSync(
() -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
+ true /* expected */,
true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
}
+ /**
+ * This checks that an implicit show request results in the IME being shown.
+ */
@Test
public void testShowSoftInputImplicitly() throws Exception {
- // When InputMethodService#onEvaluateInputViewShown() returns true and flag is IMPLICIT, the
- // Ime should be shown.
+ setShowImeWithHardKeyboard(true /* enabled */);
+
+ // When InputMethodService#onEvaluateInputViewShown() returns true and flag is IMPLICIT,
+ // the IME should be shown.
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
+ true /* expected */,
true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
}
+ /**
+ * This checks that an explicit show request when the IME is not previously shown,
+ * and it should be shown in fullscreen mode, results in the IME being shown.
+ */
@Test
- public void testShowSoftInputImplicitly_fullScreenMode() throws Exception {
- // When keyboard is off, InputMethodService#onEvaluateInputViewShown returns true, flag is
- // IMPLICIT and InputMethodService#onEvaluateFullScreenMode returns true, the Ime should not
- // be shown.
+ public void testShowSoftInputExplicitly_fullScreenMode() throws Exception {
+ setShowImeWithHardKeyboard(true /* enabled */);
+
+ // Set orientation landscape to enable fullscreen mode.
setOrientation(2);
eventually(() -> assertThat(mUiDevice.isNaturalOrientation()).isFalse());
- // Wait for the TestActivity to be recreated
+ // Wait for the TestActivity to be recreated.
eventually(() ->
assertThat(TestActivity.getLastCreatedInstance()).isNotEqualTo(mActivity));
- // Get the new TestActivity
+ // Get the new TestActivity.
mActivity = TestActivity.getLastCreatedInstance();
assertThat(mActivity).isNotNull();
InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
- // Wait for the new EditText to be served by InputMethodManager
- eventually(() ->
- assertThat(imm.hasActiveInputConnection(mActivity.getEditText())).isTrue());
+ // Wait for the new EditText to be served by InputMethodManager.
+ eventually(() -> assertThat(
+ imm.hasActiveInputConnection(mActivity.getEditText())).isTrue());
+
verifyInputViewStatusOnMainSync(() -> assertThat(
- mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
- false /* inputViewStarted */);
+ mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
+ true /* expected */,
+ true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
}
+ /**
+ * This checks that an implicit show request when the IME is not previously shown,
+ * and it should be shown in fullscreen mode, results in the IME not being shown.
+ */
+ @Test
+ public void testShowSoftInputImplicitly_fullScreenMode() throws Exception {
+ setShowImeWithHardKeyboard(true /* enabled */);
+
+ // Set orientation landscape to enable fullscreen mode.
+ setOrientation(2);
+ eventually(() -> assertThat(mUiDevice.isNaturalOrientation()).isFalse());
+ // Wait for the TestActivity to be recreated.
+ eventually(() ->
+ assertThat(TestActivity.getLastCreatedInstance()).isNotEqualTo(mActivity));
+ // Get the new TestActivity.
+ mActivity = TestActivity.getLastCreatedInstance();
+ assertThat(mActivity).isNotNull();
+ InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
+ // Wait for the new EditText to be served by InputMethodManager.
+ eventually(() -> assertThat(
+ imm.hasActiveInputConnection(mActivity.getEditText())).isTrue());
+
+ verifyInputViewStatusOnMainSync(() -> assertThat(
+ mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
+ false /* expected */,
+ false /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isFalse();
+ }
+
+ /**
+ * This checks that an explicit show request when a hard keyboard is connected,
+ * results in the IME being shown.
+ */
+ @Test
+ public void testShowSoftInputExplicitly_withHardKeyboard() throws Exception {
+ setShowImeWithHardKeyboard(false /* enabled */);
+
+ // Simulate connecting a hard keyboard.
+ mInputMethodService.getResources().getConfiguration().keyboard =
+ Configuration.KEYBOARD_QWERTY;
+ mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+ Configuration.HARDKEYBOARDHIDDEN_YES;
+
+ verifyInputViewStatusOnMainSync(() -> assertThat(
+ mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
+ true /* expected */,
+ true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
+ }
+
+ /**
+ * This checks that an implicit show request when a hard keyboard is connected,
+ * results in the IME not being shown.
+ */
@Test
public void testShowSoftInputImplicitly_withHardKeyboard() throws Exception {
+ setShowImeWithHardKeyboard(false /* enabled */);
+
+ // Simulate connecting a hard keyboard.
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_QWERTY;
- // When connecting to a hard keyboard and the flag is IMPLICIT, the Ime should not be shown.
+ mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+ Configuration.HARDKEYBOARDHIDDEN_YES;
+
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
+ false /* expected */,
false /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isFalse();
}
+ /**
+ * This checks that an explicit show request followed by connecting a hard keyboard
+ * and a configuration change, still results in the IME being shown.
+ */
@Test
- public void testConfigurationChanged_withKeyboardShownExplicitly() throws InterruptedException {
+ public void testShowSoftInputExplicitly_thenConfigurationChanged() throws Exception {
+ setShowImeWithHardKeyboard(false /* enabled */);
+
+ // Start with no hard keyboard.
+ mInputMethodService.getResources().getConfiguration().keyboard =
+ Configuration.KEYBOARD_NOKEYS;
+ mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+ Configuration.HARDKEYBOARDHIDDEN_YES;
+
verifyInputViewStatusOnMainSync(
() -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
+ true /* expected */,
true /* inputViewStarted */);
- // Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
- mInputMethodService.getResources().getConfiguration().orientation =
- Configuration.ORIENTATION_LANDSCAPE;
- verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged(
- mInputMethodService.getResources().getConfiguration()),
- true /* inputViewStarted */);
- }
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
- @Test
- public void testConfigurationChanged_withKeyboardShownImplicitly() throws InterruptedException {
- verifyInputViewStatusOnMainSync(() -> assertThat(
- mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
- true /* inputViewStarted */);
- // Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
- mInputMethodService.getResources().getConfiguration().orientation =
- Configuration.ORIENTATION_LANDSCAPE;
+ // Simulate connecting a hard keyboard.
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_QWERTY;
+ mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+ Configuration.HARDKEYBOARDHIDDEN_YES;
+
+ // Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
+ mInputMethodService.getResources().getConfiguration().orientation =
+ Configuration.ORIENTATION_LANDSCAPE;
+
+ verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged(
+ mInputMethodService.getResources().getConfiguration()),
+ true /* expected */,
+ true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
+ }
+
+ /**
+ * This checks that an implicit show request followed by connecting a hard keyboard
+ * and a configuration change, does not trigger IMS#onFinishInputView,
+ * but results in the IME being hidden.
+ */
+ @Test
+ public void testShowSoftInputImplicitly_thenConfigurationChanged() throws Exception {
+ setShowImeWithHardKeyboard(false /* enabled */);
+
+ // Start with no hard keyboard.
+ mInputMethodService.getResources().getConfiguration().keyboard =
+ Configuration.KEYBOARD_NOKEYS;
+ mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+ Configuration.HARDKEYBOARDHIDDEN_YES;
+
+ verifyInputViewStatusOnMainSync(() -> assertThat(
+ mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
+ true /* expected */,
+ true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
+
+ // Simulate connecting a hard keyboard.
+ mInputMethodService.getResources().getConfiguration().keyboard =
+ Configuration.KEYBOARD_QWERTY;
+ mInputMethodService.getResources().getConfiguration().keyboard =
+ Configuration.HARDKEYBOARDHIDDEN_YES;
+
+ // Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
+ mInputMethodService.getResources().getConfiguration().orientation =
+ Configuration.ORIENTATION_LANDSCAPE;
// Normally, IMS#onFinishInputView will be called when finishing the input view by the user.
// But if IMS#hideWindow is called when receiving a new configuration change, we don't
// expect that it's user-driven to finish the lifecycle of input view with
// IMS#onFinishInputView, because the input view will be re-initialized according to the
- // last mShowSoftRequested state. So in this case we treat the input view is still alive.
+ // last #mShowInputRequested state. So in this case we treat the input view as still alive.
verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged(
- mInputMethodService.getResources().getConfiguration()),
+ mInputMethodService.getResources().getConfiguration()),
+ true /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isFalse();
}
- private void verifyInputViewStatus(Runnable runnable, boolean inputViewStarted)
- throws InterruptedException {
- verifyInputViewStatusInternal(runnable, inputViewStarted, false /*runOnMainSync*/);
+ /**
+ * This checks that an explicit show request directly followed by an implicit show request,
+ * while a hardware keyboard is connected, still results in the IME being shown
+ * (i.e. the implicit show request is treated as explicit).
+ */
+ @Test
+ public void testShowSoftInputExplicitly_thenShowSoftInputImplicitly_withHardKeyboard()
+ throws Exception {
+ setShowImeWithHardKeyboard(false /* enabled */);
+
+ // Simulate connecting a hard keyboard.
+ mInputMethodService.getResources().getConfiguration().keyboard =
+ Configuration.KEYBOARD_QWERTY;
+ mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+ Configuration.HARDKEYBOARDHIDDEN_YES;
+
+ // Explicit show request.
+ verifyInputViewStatusOnMainSync(() -> assertThat(
+ mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
+ true /* expected */,
+ true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
+
+ // Implicit show request.
+ verifyInputViewStatusOnMainSync(() -> assertThat(
+ mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
+ false /* expected */,
+ true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
+
+ // Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
+ // This should now consider the implicit show request, but keep the state from the
+ // explicit show request, and thus not hide the keyboard.
+ verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged(
+ mInputMethodService.getResources().getConfiguration()),
+ true /* expected */,
+ true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
}
- private void verifyInputViewStatusOnMainSync(Runnable runnable, boolean inputViewStarted)
- throws InterruptedException {
- verifyInputViewStatusInternal(runnable, inputViewStarted, true /*runOnMainSync*/);
+ /**
+ * This checks that a forced show request directly followed by an explicit show request,
+ * and then a hide not always request, still results in the IME being shown
+ * (i.e. the explicit show request retains the forced state).
+ */
+ @Test
+ public void testShowSoftInputForced_testShowSoftInputExplicitly_thenHideSoftInputNotAlways()
+ throws Exception {
+ setShowImeWithHardKeyboard(true /* enabled */);
+
+ verifyInputViewStatusOnMainSync(() -> assertThat(
+ mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_FORCED)).isTrue(),
+ true /* expected */,
+ true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
+
+ verifyInputViewStatusOnMainSync(() -> assertThat(
+ mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
+ false /* expected */,
+ true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
+
+ verifyInputViewStatusOnMainSync(() ->
+ mActivity.hideImeWithInputMethodManager(InputMethodManager.HIDE_NOT_ALWAYS),
+ false /* expected */,
+ true /* inputViewStarted */);
+ assertThat(mInputMethodService.isInputViewShown()).isTrue();
}
+ /**
+ * This checks that the IME fullscreen mode state is updated after changing orientation.
+ */
+ @Test
+ public void testFullScreenMode() throws Exception {
+ setShowImeWithHardKeyboard(true /* enabled */);
+
+ Log.i(TAG, "Set orientation natural");
+ verifyFullscreenMode(() -> setOrientation(0),
+ false /* expected */,
+ true /* orientationPortrait */);
+
+ Log.i(TAG, "Set orientation left");
+ verifyFullscreenMode(() -> setOrientation(1),
+ true /* expected */,
+ false /* orientationPortrait */);
+
+ Log.i(TAG, "Set orientation right");
+ verifyFullscreenMode(() -> setOrientation(2),
+ false /* expected */,
+ false /* orientationPortrait */);
+ }
+
+ private void verifyInputViewStatus(
+ Runnable runnable, boolean expected, boolean inputViewStarted)
+ throws InterruptedException {
+ verifyInputViewStatusInternal(runnable, expected, inputViewStarted,
+ false /* runOnMainSync */);
+ }
+
+ private void verifyInputViewStatusOnMainSync(
+ Runnable runnable, boolean expected, boolean inputViewStarted)
+ throws InterruptedException {
+ verifyInputViewStatusInternal(runnable, expected, inputViewStarted,
+ true /* runOnMainSync */);
+ }
+
+ /**
+ * Verifies the status of the Input View after executing the given runnable.
+ *
+ * @param runnable the runnable to execute for showing or hiding the IME.
+ * @param expected whether the runnable is expected to trigger the signal.
+ * @param inputViewStarted the expected state of the Input View after executing the runnable.
+ * @param runOnMainSync whether to execute the runnable on the main thread.
+ */
private void verifyInputViewStatusInternal(
- Runnable runnable, boolean inputViewStarted, boolean runOnMainSync)
+ Runnable runnable, boolean expected, boolean inputViewStarted, boolean runOnMainSync)
throws InterruptedException {
CountDownLatch signal = new CountDownLatch(1);
mInputMethodService.setCountDownLatchForTesting(signal);
- // Runnable to trigger onStartInputView()/ onFinishInputView()
+ // Runnable to trigger onStartInputView() / onFinishInputView() / onConfigurationChanged()
if (runOnMainSync) {
mInstrumentation.runOnMainSync(runnable);
} else {
runnable.run();
}
- // Waits for onStartInputView() to finish.
mInstrumentation.waitForIdleSync();
- signal.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
+ boolean completed = signal.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
+ if (expected && !completed) {
+ fail("Timed out waiting for"
+ + " onStartInputView() / onFinishInputView() / onConfigurationChanged()");
+ } else if (!expected && completed) {
+ fail("Unexpected call"
+ + " onStartInputView() / onFinishInputView() / onConfigurationChanged()");
+ }
// Input is not finished.
assertThat(mInputMethodService.getCurrentInputStarted()).isTrue();
assertThat(mInputMethodService.getCurrentInputViewStarted()).isEqualTo(inputViewStarted);
}
- @Test
- public void testFullScreenMode() throws Exception {
- Log.i(TAG, "Set orientation natural");
- verifyFullscreenMode(() -> setOrientation(0), true /* orientationPortrait */);
-
- Log.i(TAG, "Set orientation left");
- verifyFullscreenMode(() -> setOrientation(1), false /* orientationPortrait */);
-
- Log.i(TAG, "Set orientation right");
- verifyFullscreenMode(() -> setOrientation(2), false /* orientationPortrait */);
- }
-
private void setOrientation(int orientation) {
// Simple wrapper for catching RemoteException.
try {
@@ -366,7 +657,15 @@
}
}
- private void verifyFullscreenMode(Runnable runnable, boolean orientationPortrait)
+ /**
+ * Verifies the IME fullscreen mode state after executing the given runnable.
+ *
+ * @param runnable the runnable to execute for setting the orientation.
+ * @param expected whether the runnable is expected to trigger the signal.
+ * @param orientationPortrait whether the orientation is expected to be portrait.
+ */
+ private void verifyFullscreenMode(
+ Runnable runnable, boolean expected, boolean orientationPortrait)
throws InterruptedException {
CountDownLatch signal = new CountDownLatch(1);
mInputMethodService.setCountDownLatchForTesting(signal);
@@ -379,7 +678,12 @@
}
// Waits for onConfigurationChanged() to finish.
mInstrumentation.waitForIdleSync();
- signal.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
+ boolean completed = signal.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
+ if (expected && !completed) {
+ fail("Timed out waiting for onConfigurationChanged()");
+ } else if (!expected && completed) {
+ fail("Unexpected call onConfigurationChanged()");
+ }
clickOnEditorText();
eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isTrue());
@@ -416,7 +720,21 @@
return mTargetPackageName + "/" + INPUT_METHOD_SERVICE_NAME;
}
- private String executeShellCommand(String cmd) throws Exception {
+ /**
+ * Sets the value of show_ime_with_hard_keyboard, only if it is different to the default value.
+ *
+ * @param enabled the value to be set.
+ */
+ private void setShowImeWithHardKeyboard(boolean enabled) throws IOException {
+ if (mShowImeWithHardKeyboardEnabled != enabled) {
+ executeShellCommand(enabled
+ ? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD
+ : DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD);
+ mInstrumentation.waitForIdleSync();
+ }
+ }
+
+ private String executeShellCommand(String cmd) throws IOException {
Log.i(TAG, "Run command: " + cmd);
return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
.executeShellCommand(cmd);
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 869497c..3199e06 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -40,7 +40,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.view.Display;
-import android.view.inputmethod.InputMethodManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -77,9 +76,9 @@
public void testPerformShowIme() throws Exception {
synchronized (ImfLock.class) {
mVisibilityApplier.performShowIme(new Binder() /* showInputToken */,
- null /* statsToken */, InputMethodManager.SHOW_IMPLICIT, null, SHOW_SOFT_INPUT);
+ null /* statsToken */, 0 /* showFlags */, null, SHOW_SOFT_INPUT);
}
- verifyShowSoftInput(false, true, InputMethodManager.SHOW_IMPLICIT);
+ verifyShowSoftInput(false, true, 0 /* showFlags */);
}
@Test
@@ -126,7 +125,7 @@
@Test
public void testApplyImeVisibility_showImeImplicit() throws Exception {
mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_SHOW_IME_IMPLICIT);
- verifyShowSoftInput(true, true, InputMethodManager.SHOW_IMPLICIT);
+ verifyShowSoftInput(true, true, 0 /* showFlags */);
}
@Test
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
index a38c162..fae5f86 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
@@ -106,7 +106,7 @@
@Test
public void testRequestImeVisibility_showExplicit() {
initImeTargetWindowState(mWindowToken);
- boolean res = mComputer.onImeShowFlags(null, 0 /* show explicit */);
+ boolean res = mComputer.onImeShowFlags(null, 0 /* showFlags */);
mComputer.requestImeVisibility(mWindowToken, res);
final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
@@ -118,6 +118,34 @@
assertThat(mComputer.mRequestedShowExplicitly).isTrue();
}
+ /**
+ * This checks that the state after an explicit show request does not get reset during
+ * a subsequent implicit show request, without an intermediary hide request.
+ */
+ @Test
+ public void testRequestImeVisibility_showExplicit_thenShowImplicit() {
+ initImeTargetWindowState(mWindowToken);
+ mComputer.onImeShowFlags(null, 0 /* showFlags */);
+ assertThat(mComputer.mRequestedShowExplicitly).isTrue();
+
+ mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT);
+ assertThat(mComputer.mRequestedShowExplicitly).isTrue();
+ }
+
+ /**
+ * This checks that the state after a forced show request does not get reset during
+ * a subsequent explicit show request, without an intermediary hide request.
+ */
+ @Test
+ public void testRequestImeVisibility_showForced_thenShowExplicit() {
+ initImeTargetWindowState(mWindowToken);
+ mComputer.onImeShowFlags(null, InputMethodManager.SHOW_FORCED);
+ assertThat(mComputer.mShowForced).isTrue();
+
+ mComputer.onImeShowFlags(null, 0 /* showFlags */);
+ assertThat(mComputer.mShowForced).isTrue();
+ }
+
@Test
public void testRequestImeVisibility_showImplicit_a11yNoImePolicy() {
// Precondition: set AccessibilityService#SHOW_MODE_HIDDEN policy
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodBindingControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodBindingControllerTest.java
index 42d373b..e87a34e 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodBindingControllerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodBindingControllerTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
@@ -162,7 +163,10 @@
assertThat(mBindingController.getCurToken()).isNotNull();
}
// Wait for onServiceConnected()
- mCountDownLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
+ boolean completed = mCountDownLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
+ if (!completed) {
+ fail("Timed out waiting for onServiceConnected()");
+ }
// Verify onServiceConnected() is called and bound successfully.
synchronized (ImfLock.class) {
diff --git a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/Android.bp b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/Android.bp
index 8d0e0c4..e1fd2b3 100644
--- a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/Android.bp
@@ -43,6 +43,8 @@
},
export_package_resources: true,
sdk_version: "current",
+
+ certificate: "platform",
}
android_library {
diff --git a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/AndroidManifest.xml b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/AndroidManifest.xml
index 996322d..cf7d660 100644
--- a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/AndroidManifest.xml
+++ b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/AndroidManifest.xml
@@ -18,8 +18,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.apps.inputmethod.simpleime">
- <uses-sdk android:targetSdkVersion="31" />
-
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application android:debuggable="true"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
index 95c62ae..7e69357 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
@@ -86,7 +86,9 @@
.append("\n brightnessReason:")
.append(displayBrightnessState.getBrightnessReason())
.append("\n shouldUseAutoBrightness:")
- .append(displayBrightnessState.getShouldUseAutoBrightness());
+ .append(displayBrightnessState.getShouldUseAutoBrightness())
+ .append("\n isSlowChange:")
+ .append(displayBrightnessState.isSlowChange());
return sb.toString();
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
index 081f19d..d8569f7 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
@@ -21,8 +21,8 @@
import android.hardware.display.DisplayManagerInternal;
import android.view.Display;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.brightness.BrightnessReason;
@@ -46,7 +46,8 @@
DisplayManagerInternal.DisplayPowerRequest
displayPowerRequest = new DisplayManagerInternal.DisplayPowerRequest();
float brightnessToFollow = 0.2f;
- mFollowerBrightnessStrategy.setBrightnessToFollow(brightnessToFollow);
+ boolean slowChange = true;
+ mFollowerBrightnessStrategy.setBrightnessToFollow(brightnessToFollow, slowChange);
BrightnessReason brightnessReason = new BrightnessReason();
brightnessReason.setReason(BrightnessReason.REASON_FOLLOWER);
DisplayBrightnessState expectedDisplayBrightnessState =
@@ -55,6 +56,7 @@
.setBrightnessReason(brightnessReason)
.setSdrBrightness(brightnessToFollow)
.setDisplayBrightnessStrategyName(mFollowerBrightnessStrategy.getName())
+ .setIsSlowChange(slowChange)
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mFollowerBrightnessStrategy.updateBrightness(displayPowerRequest);
diff --git a/services/tests/dreamservicetests/Android.bp b/services/tests/dreamservicetests/Android.bp
new file mode 100644
index 0000000..b698a60
--- /dev/null
+++ b/services/tests/dreamservicetests/Android.bp
@@ -0,0 +1,35 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "DreamServiceTests",
+
+ // Include all test java files.
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ static_libs: [
+ "androidx.test.ext.truth",
+ "frameworks-base-testutils",
+ "mockito-target-minus-junit4",
+ "services.core",
+ ],
+
+ platform_apis: true,
+
+ test_suites: [
+ "automotive-tests",
+ "device-tests",
+ ],
+
+ certificate: "platform",
+
+ dxflags: ["--multi-dex"],
+
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/dreamservicetests/AndroidManifest.xml b/services/tests/dreamservicetests/AndroidManifest.xml
new file mode 100644
index 0000000..fc3ad34
--- /dev/null
+++ b/services/tests/dreamservicetests/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.dreamservicetests">
+
+ <!--
+ Insert permissions here. eg:
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ -->
+
+ <application android:debuggable="true"
+ android:testOnly="true">
+ <uses-library android:name="android.test.mock" android:required="true" />
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.dreamservicetests"
+ android:label="Frameworks Services Tests"/>
+</manifest>
diff --git a/services/tests/vibrator/AndroidTest.xml b/services/tests/dreamservicetests/AndroidTest.xml
similarity index 73%
rename from services/tests/vibrator/AndroidTest.xml
rename to services/tests/dreamservicetests/AndroidTest.xml
index d5ee3af..ae89281 100644
--- a/services/tests/vibrator/AndroidTest.xml
+++ b/services/tests/dreamservicetests/AndroidTest.xml
@@ -13,22 +13,21 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<configuration description="Runs Frameworks Vibrator Services Tests.">
+<configuration description="Runs Dream Service Tests.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="install-arg" value="-t" />
- <option name="test-file-name" value="FrameworksVibratorServicesTests.apk" />
+ <option name="test-file-name" value="DreamServiceTests.apk" />
</target_preparer>
- <option name="test-tag" value="FrameworksVibratorServicesTests" />
-
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="com.android.framework.services.tests.vibrator" />
+ <option name="test-tag" value="DreamServiceTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.dreamservicetests" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
- <option name="hidden-api-checks" value="false" />
+ <option name="hidden-api-checks" value="false"/>
+ <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
</test>
</configuration>
diff --git a/services/tests/dreamservicetests/OWNERS b/services/tests/dreamservicetests/OWNERS
new file mode 100644
index 0000000..6dd64e7
--- /dev/null
+++ b/services/tests/dreamservicetests/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 78010
+
+include /core/java/android/service/dreams/OWNERS
diff --git a/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/OWNERS b/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/OWNERS
index 999ea0e..2fe78c5 100644
--- a/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/OWNERS
+++ b/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
include /core/java/android/permission/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 72c5333..410ae35 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -328,7 +328,7 @@
eq(ActivityManager.PROCESS_STATE_LAST_ACTIVITY), any());
mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
- mConstants.TIMEOUT = 100;
+ mConstants.TIMEOUT = 200;
mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT = 0;
mConstants.PENDING_COLD_START_CHECK_INTERVAL_MILLIS = 500;
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/OWNERS b/services/tests/mockingservicestests/src/com/android/server/appop/OWNERS
index 999ea0e..2fe78c5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
include /core/java/android/permission/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
index 1ae9124..52044bf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
@@ -368,10 +369,11 @@
File finalApex = extractResource("test.rebootles_apex_v2", "test.rebootless_apex_v2.apex");
ApexInfo newApexInfo = createApexInfo("test.apex_rebootless", 2, /* isActive= */ true,
/* isFactory= */ false, finalApex);
- when(mApexService.installAndActivatePackage(anyString())).thenReturn(newApexInfo);
+ when(mApexService.installAndActivatePackage(anyString(), anyBoolean())).thenReturn(
+ newApexInfo);
File installedApex = extractResource("installed", "test.rebootless_apex_v2.apex");
- newApexInfo = mApexManager.installPackage(installedApex);
+ newApexInfo = mApexManager.installPackage(installedApex, /* force= */ false);
var newPkg = mockParsePackage(mPackageParser2, newApexInfo);
assertThat(newPkg.getBaseApkPath()).isEqualTo(finalApex.getAbsolutePath());
@@ -398,10 +400,11 @@
File finalApex = extractResource("test.rebootles_apex_v2", "test.rebootless_apex_v2.apex");
ApexInfo newApexInfo = createApexInfo("test.apex_rebootless", 2, /* isActive= */ true,
/* isFactory= */ false, finalApex);
- when(mApexService.installAndActivatePackage(anyString())).thenReturn(newApexInfo);
+ when(mApexService.installAndActivatePackage(anyString(), anyBoolean())).thenReturn(
+ newApexInfo);
File installedApex = extractResource("installed", "test.rebootless_apex_v2.apex");
- newApexInfo = mApexManager.installPackage(installedApex);
+ newApexInfo = mApexManager.installPackage(installedApex, /* force= */ false);
var newPkg = mockParsePackage(mPackageParser2, newApexInfo);
assertThat(newPkg.getBaseApkPath()).isEqualTo(finalApex.getAbsolutePath());
@@ -416,13 +419,13 @@
@Test
public void testInstallPackageBinderCallFails() throws Exception {
- when(mApexService.installAndActivatePackage(anyString())).thenThrow(
+ when(mApexService.installAndActivatePackage(anyString(), anyBoolean())).thenThrow(
new RuntimeException("install failed :("));
File installedApex = extractResource("test.apex_rebootless_v1",
"test.rebootless_apex_v1.apex");
assertThrows(PackageManagerException.class,
- () -> mApexManager.installPackage(installedApex));
+ () -> mApexManager.installPackage(installedApex, /* force= */ false));
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
index 7b16545..a8b0a7b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
@@ -40,6 +40,7 @@
import android.os.Process;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.text.TextUtils;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -169,14 +170,14 @@
}
@Test
- public void archiveApp_callerNotInstallerOfRecord() {
+ public void archiveApp_noInstallerFound() {
InstallSource otherInstallSource =
InstallSource.create(
CALLER_PACKAGE,
CALLER_PACKAGE,
- /* installerPackageName= */ "different",
+ /* installerPackageName= */ null,
Binder.getCallingUid(),
- CALLER_PACKAGE,
+ /* updateOwnerPackageName= */ null,
/* installerAttributionTag= */ null,
/* packageSource= */ 0);
when(mPackageState.getInstallSource()).thenReturn(otherInstallSource);
@@ -187,29 +188,8 @@
mIntentSender)
);
assertThat(e).hasMessageThat().isEqualTo(
- String.format("Caller is not the installer of record for %s.", PACKAGE));
- }
-
- @Test
- public void archiveApp_callerNotUpdateOwner() {
- InstallSource otherInstallSource =
- InstallSource.create(
- CALLER_PACKAGE,
- CALLER_PACKAGE,
- CALLER_PACKAGE,
- Binder.getCallingUid(),
- /* updateOwnerPackageName= */ "different",
- /* installerAttributionTag= */ null,
- /* packageSource= */ 0);
- when(mPackageState.getInstallSource()).thenReturn(otherInstallSource);
-
- Exception e = assertThrows(
- SecurityException.class,
- () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT,
- mIntentSender)
- );
- assertThat(e).hasMessageThat().isEqualTo(
- String.format("Caller is not the update owner for %s.", PACKAGE));
+ TextUtils.formatSimple("No installer found to archive app %s.",
+ PACKAGE));
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
index 3ac59e9..f206333 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
@@ -33,6 +33,8 @@
import android.os.Handler;
import android.os.IRemoteCallback;
import android.os.Looper;
+import android.os.Process;
+import android.util.SparseArray;
import org.junit.After;
import org.junit.Before;
@@ -244,6 +246,54 @@
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
}
+ @Test
+ public void testRegisterPackageMonitorCallbackInAllowList_callbackShouldCalled()
+ throws Exception {
+ IRemoteCallback callback = createMockPackageMonitorCallback();
+ SparseArray<int[]> broadcastAllowList = new SparseArray<>();
+ broadcastAllowList.put(0, new int[] {Binder.getCallingUid()});
+
+ mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */,
+ Binder.getCallingUid());
+ mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
+ FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
+ null /* instantUserIds */, broadcastAllowList);
+
+ verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
+ }
+
+ @Test
+ public void testRegisterPackageMonitorCallbackNotInAllowList_callbackShouldNotCalled()
+ throws Exception {
+ IRemoteCallback callback = createMockPackageMonitorCallback();
+ SparseArray<int[]> broadcastAllowList = new SparseArray<>();
+ broadcastAllowList.put(0, new int[] {12345});
+
+ mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */,
+ Binder.getCallingUid());
+ mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
+ FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
+ null /* instantUserIds */, broadcastAllowList);
+
+ verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
+ }
+
+ @Test
+ public void testRegisterPackageMonitorCallbackNotInAllowListSystemUid_callbackShouldCalled()
+ throws Exception {
+ IRemoteCallback callback = createMockPackageMonitorCallback();
+ SparseArray<int[]> broadcastAllowList = new SparseArray<>();
+ broadcastAllowList.put(0, new int[] {12345});
+
+ mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */,
+ Process.SYSTEM_UID);
+ mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
+ FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
+ null /* instantUserIds */, broadcastAllowList);
+
+ verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
+ }
+
private IRemoteCallback createMockPackageMonitorCallback() {
return spy(new IRemoteCallback.Stub() {
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
deleted file mode 100644
index 0f2c27c..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * 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.server.power;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManagerInternal;
-import android.attention.AttentionManagerInternal;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.res.Resources;
-import android.hardware.SensorManager;
-import android.hardware.devicestate.DeviceStateManager;
-import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
-import android.hardware.display.AmbientDisplayConfiguration;
-import android.hardware.display.DisplayManagerInternal;
-import android.os.BatteryManagerInternal;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.PowerSaveState;
-import android.os.test.TestLooper;
-import android.provider.Settings;
-import android.service.dreams.DreamManagerInternal;
-import android.test.mock.MockContentResolver;
-import android.view.Display;
-import android.view.DisplayInfo;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.LocalServices;
-import com.android.server.SystemService;
-import com.android.server.lights.LightsManager;
-import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.power.PowerManagerService.BatteryReceiver;
-import com.android.server.power.PowerManagerService.Injector;
-import com.android.server.power.PowerManagerService.NativeWrapper;
-import com.android.server.power.PowerManagerService.UserSwitchedReceiver;
-import com.android.server.power.batterysaver.BatterySaverController;
-import com.android.server.power.batterysaver.BatterySaverPolicy;
-import com.android.server.power.batterysaver.BatterySaverStateMachine;
-import com.android.server.power.batterysaver.BatterySavingStats;
-import com.android.server.testutils.OffsettableClock;
-
-import java.util.concurrent.Executor;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link com.android.server.power.PowerManagerService}.
- *
- * Build/Install/Run:
- * atest FrameworksServicesTests:PowerManagerServiceMockingTest
- */
-public class PowerManagerServiceMockingTest {
- private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent";
- private static final String SYSTEM_PROPERTY_REBOOT_REASON = "sys.boot.reason";
-
- private static final float BRIGHTNESS_FACTOR = 0.7f;
- private static final boolean BATTERY_SAVER_ENABLED = true;
-
- @Mock private BatterySaverController mBatterySaverControllerMock;
- @Mock private BatterySaverPolicy mBatterySaverPolicyMock;
- @Mock private BatterySaverStateMachine mBatterySaverStateMachineMock;
- @Mock private LightsManager mLightsManagerMock;
- @Mock private DisplayManagerInternal mDisplayManagerInternalMock;
- @Mock private BatteryManagerInternal mBatteryManagerInternalMock;
- @Mock private ActivityManagerInternal mActivityManagerInternalMock;
- @Mock private AttentionManagerInternal mAttentionManagerInternalMock;
- @Mock private DreamManagerInternal mDreamManagerInternalMock;
- @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
- @Mock private Notifier mNotifierMock;
- @Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
- @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock;
- @Mock private SystemPropertiesWrapper mSystemPropertiesMock;
- @Mock private DeviceStateManager mDeviceStateManagerMock;
-
- @Mock
- private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
-
- private PowerManagerService mService;
- private PowerSaveState mPowerSaveState;
- private ContextWrapper mContextSpy;
- private BatteryReceiver mBatteryReceiver;
- private UserSwitchedReceiver mUserSwitchedReceiver;
- private Resources mResourcesSpy;
- private OffsettableClock mClock;
- private TestLooper mTestLooper;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- FakeSettingsProvider.clearSettingsProvider();
-
- mPowerSaveState = new PowerSaveState.Builder()
- .setBatterySaverEnabled(BATTERY_SAVER_ENABLED)
- .setBrightnessFactor(BRIGHTNESS_FACTOR)
- .build();
- when(mBatterySaverPolicyMock.getBatterySaverPolicy(
- eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS)))
- .thenReturn(mPowerSaveState);
- when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false);
- when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false);
- when(mDisplayManagerInternalMock.requestPowerState(anyInt(), any(), anyBoolean()))
- .thenReturn(true);
- when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
- when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
-
- addLocalServiceMock(LightsManager.class, mLightsManagerMock);
- addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
- addLocalServiceMock(BatteryManagerInternal.class, mBatteryManagerInternalMock);
- addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
- addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock);
- addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock);
-
- mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
- mResourcesSpy = spy(mContextSpy.getResources());
- when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
-
- MockContentResolver cr = new MockContentResolver(mContextSpy);
- cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- when(mContextSpy.getContentResolver()).thenReturn(cr);
-
- when(mContextSpy.getSystemService(DeviceStateManager.class))
- .thenReturn(mDeviceStateManagerMock);
-
- Settings.Global.putInt(mContextSpy.getContentResolver(),
- Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
-
- mClock = new OffsettableClock.Stopped();
- mTestLooper = new TestLooper(mClock::now);
- }
-
- private PowerManagerService createService() {
- mService = new PowerManagerService(mContextSpy, new Injector() {
- @Override
- Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
- SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
- FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
- Executor executor) {
- return mNotifierMock;
- }
-
- @Override
- SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
- return super.createSuspendBlocker(service, name);
- }
-
- @Override
- BatterySaverPolicy createBatterySaverPolicy(
- Object lock, Context context, BatterySavingStats batterySavingStats) {
- return mBatterySaverPolicyMock;
- }
-
- @Override
- BatterySaverController createBatterySaverController(
- Object lock, Context context, BatterySaverPolicy batterySaverPolicy,
- BatterySavingStats batterySavingStats) {
- return mBatterySaverControllerMock;
- }
-
- @Override
- BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context,
- BatterySaverController batterySaverController) {
- return mBatterySaverStateMachineMock;
- }
-
- @Override
- NativeWrapper createNativeWrapper() {
- return mNativeWrapperMock;
- }
-
- @Override
- WirelessChargerDetector createWirelessChargerDetector(
- SensorManager sensorManager, SuspendBlocker suspendBlocker, Handler handler) {
- return mWirelessChargerDetectorMock;
- }
-
- @Override
- AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) {
- return mAmbientDisplayConfigurationMock;
- }
-
- @Override
- InattentiveSleepWarningController createInattentiveSleepWarningController() {
- return mInattentiveSleepWarningControllerMock;
- }
-
- @Override
- public SystemPropertiesWrapper createSystemPropertiesWrapper() {
- return mSystemPropertiesMock;
- }
-
- @Override
- PowerManagerService.Clock createClock() {
- return new PowerManagerService.Clock() {
- @Override
- public long uptimeMillis() {
- return mClock.now();
- }
-
- @Override
- public long elapsedRealtime() {
- return mClock.now();
- }
- };
- }
-
- @Override
- Handler createHandler(Looper looper, Handler.Callback callback) {
- return new Handler(mTestLooper.getLooper(), callback);
- }
-
- @Override
- void invalidateIsInteractiveCaches() {
- // Avoids an SELinux failure.
- }
- });
- return mService;
- }
-
- @After
- public void tearDown() throws Exception {
- LocalServices.removeServiceForTest(LightsManager.class);
- LocalServices.removeServiceForTest(DisplayManagerInternal.class);
- LocalServices.removeServiceForTest(BatteryManagerInternal.class);
- LocalServices.removeServiceForTest(ActivityManagerInternal.class);
- LocalServices.removeServiceForTest(AttentionManagerInternal.class);
- LocalServices.removeServiceForTest(DreamManagerInternal.class);
- FakeSettingsProvider.clearSettingsProvider();
- }
-
- /**
- * Creates a mock and registers it to {@link LocalServices}.
- */
- private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
- LocalServices.removeServiceForTest(clazz);
- LocalServices.addService(clazz, mock);
- }
-
- private void advanceTime(long timeMs) {
- mClock.fastForward(timeMs);
- mTestLooper.dispatchAll();
- }
-
- @Test
- public void testUserActivityOnDeviceStateChange() {
- createService();
- mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
- mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
-
- final DisplayInfo info = new DisplayInfo();
- info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
- when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
-
- final ArgumentCaptor<DeviceStateCallback> deviceStateCallbackCaptor =
- ArgumentCaptor.forClass(DeviceStateCallback.class);
- verify(mDeviceStateManagerMock).registerCallback(any(),
- deviceStateCallbackCaptor.capture());
-
- // Advance the time 10001 and verify that the device thinks it has been idle
- // for just less than that.
- mService.onUserActivity();
- advanceTime(10001);
- assertThat(mService.wasDeviceIdleForInternal(10000)).isTrue();
-
- // Send a display state change event and advance the clock 10.
- final DeviceStateCallback deviceStateCallback = deviceStateCallbackCaptor.getValue();
- deviceStateCallback.onStateChanged(1);
- final long timeToAdvance = 10;
- advanceTime(timeToAdvance);
-
- // Ensure that the device has been idle for only 10 (doesn't include the idle time
- // before the display state event).
- assertThat(mService.wasDeviceIdleForInternal(timeToAdvance - 1)).isTrue();
- assertThat(mService.wasDeviceIdleForInternal(timeToAdvance)).isFalse();
-
- // Send the same state and ensure that does not trigger an update.
- deviceStateCallback.onStateChanged(1);
- advanceTime(timeToAdvance);
- final long newTime = timeToAdvance * 2;
-
- assertThat(mService.wasDeviceIdleForInternal(newTime - 1)).isTrue();
- assertThat(mService.wasDeviceIdleForInternal(newTime)).isFalse();
- }
-}
diff --git a/services/tests/powerservicetests/Android.bp b/services/tests/powerservicetests/Android.bp
index 236f90c..9384015 100644
--- a/services/tests/powerservicetests/Android.bp
+++ b/services/tests/powerservicetests/Android.bp
@@ -12,9 +12,19 @@
static_libs: [
"frameworks-base-testutils",
+ "platform-compat-test-rules",
"platform-test-annotations",
"services.core",
"servicestests-utils",
+ "testables",
+ ],
+
+ libs: [
+ "android.test.mock",
+ ],
+
+ defaults: [
+ "modules-utils-testable-device-config-defaults",
],
platform_apis: true,
diff --git a/services/tests/powerservicetests/AndroidManifest.xml b/services/tests/powerservicetests/AndroidManifest.xml
index 3ace9d5..26d9eec 100644
--- a/services/tests/powerservicetests/AndroidManifest.xml
+++ b/services/tests/powerservicetests/AndroidManifest.xml
@@ -17,10 +17,19 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworks.powerservicetests">
- <!--
- Insert permissions here. eg:
- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
- -->
+ <!-- Permissions -->
+ <uses-permission android:name="android.permission.DEVICE_POWER"/>
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
+ <uses-permission android:name="android.permission.READ_DREAM_STATE"/>
+ <uses-permission android:name="android.permission.READ_DREAM_SUPPRESSION"/>
+ <uses-permission android:name="android.permission.STATUS_BAR_SERVICE"/>
+ <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS"/>
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+ <uses-permission android:name="android.permission.WRITE_DREAM_STATE"/>
+
<application android:debuggable="true"
android:testOnly="true">
<uses-library android:name="android.test.mock" android:required="true" />
diff --git a/services/tests/powerservicetests/OWNERS b/services/tests/powerservicetests/OWNERS
index e740577..aff0930 100644
--- a/services/tests/powerservicetests/OWNERS
+++ b/services/tests/powerservicetests/OWNERS
@@ -2,4 +2,3 @@
include /services/core/java/com/android/server/power/OWNERS
-per-file ThermalManagerServiceTest.java=wvw@google.com, xwxw@google.com
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java b/services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java
rename to services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/power/NotifierTest.java
rename to services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerGroupTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/power/PowerGroupTest.java
rename to services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
similarity index 91%
rename from services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
rename to services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index c80ff45..9bce536 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -18,6 +18,8 @@
import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
+import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
import static android.os.PowerManager.USER_ACTIVITY_EVENT_BUTTON;
import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
@@ -60,6 +62,8 @@
import android.content.PermissionChecker;
import android.content.res.Resources;
import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
@@ -78,6 +82,7 @@
import android.os.PowerSaveState;
import android.os.UserHandle;
import android.os.test.TestLooper;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
import android.sysprop.PowerProperties;
@@ -92,6 +97,7 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.lights.LightsManager;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.power.PowerManagerService.BatteryReceiver;
@@ -160,6 +166,8 @@
@Mock private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
@Mock private PowerManagerService.PermissionCheckerWrapper mPermissionCheckerWrapperMock;
@Mock private PowerManagerService.PowerPropertiesWrapper mPowerPropertiesWrapper;
+ @Mock private DeviceStateManager mDeviceStateManagerMock;
+ @Mock private DeviceConfigParameterProvider mDeviceParameterProvider;
@Rule public TestRule compatChangeRule = new PlatformCompatChangeRule();
@@ -341,6 +349,11 @@
PowerManagerService.PowerPropertiesWrapper createPowerPropertiesWrapper() {
return mPowerPropertiesWrapper;
}
+
+ @Override
+ DeviceConfigParameterProvider createDeviceConfigParameterProvider() {
+ return mDeviceParameterProvider;
+ }
});
return mService;
}
@@ -2710,4 +2723,241 @@
verify(mNotifierMock, never()).onUserActivity(anyInt(), anyInt(), anyInt());
}
+ @Test
+ public void testUserActivityOnDeviceStateChange() {
+ when(mContextSpy.getSystemService(DeviceStateManager.class))
+ .thenReturn(mDeviceStateManagerMock);
+
+ createService();
+ mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ final DisplayInfo info = new DisplayInfo();
+ info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
+ when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
+
+ final ArgumentCaptor<DeviceStateCallback> deviceStateCallbackCaptor =
+ ArgumentCaptor.forClass(DeviceStateCallback.class);
+ verify(mDeviceStateManagerMock).registerCallback(any(),
+ deviceStateCallbackCaptor.capture());
+
+ // Advance the time 10001 and verify that the device thinks it has been idle
+ // for just less than that.
+ mService.onUserActivity();
+ advanceTime(10001);
+ assertThat(mService.wasDeviceIdleForInternal(10000)).isTrue();
+
+ // Send a display state change event and advance the clock 10.
+ final DeviceStateCallback deviceStateCallback = deviceStateCallbackCaptor.getValue();
+ deviceStateCallback.onStateChanged(1);
+ final long timeToAdvance = 10;
+ advanceTime(timeToAdvance);
+
+ // Ensure that the device has been idle for only 10 (doesn't include the idle time
+ // before the display state event).
+ assertThat(mService.wasDeviceIdleForInternal(timeToAdvance - 1)).isTrue();
+ assertThat(mService.wasDeviceIdleForInternal(timeToAdvance)).isFalse();
+
+ // Send the same state and ensure that does not trigger an update.
+ deviceStateCallback.onStateChanged(1);
+ advanceTime(timeToAdvance);
+ final long newTime = timeToAdvance * 2;
+
+ assertThat(mService.wasDeviceIdleForInternal(newTime - 1)).isTrue();
+ assertThat(mService.wasDeviceIdleForInternal(newTime)).isFalse();
+ }
+
+ @Test
+ public void testFeatureEnabledProcStateUncachedToCached_fullWakeLockDisabled() {
+ doReturn(true).when(mDeviceParameterProvider)
+ .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+ createService();
+ startSystem();
+ WakeLock wakeLock = acquireWakeLock("fullWakeLock", PowerManager.FULL_WAKE_LOCK);
+ setUncachedUidProcState(wakeLock.mOwnerUid);
+
+ setCachedUidProcState(wakeLock.mOwnerUid);
+ assertThat(wakeLock.mDisabled).isTrue();
+ }
+
+ @Test
+ public void testFeatureDisabledProcStateUncachedToCached_fullWakeLockEnabled() {
+ doReturn(false).when(mDeviceParameterProvider)
+ .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+ createService();
+ startSystem();
+ WakeLock wakeLock = acquireWakeLock("fullWakeLock", PowerManager.FULL_WAKE_LOCK);
+ setUncachedUidProcState(wakeLock.mOwnerUid);
+
+ setCachedUidProcState(wakeLock.mOwnerUid);
+ assertThat(wakeLock.mDisabled).isFalse();
+ }
+
+ @Test
+ public void testFeatureEnabledProcStateUncachedToCached_screenBrightWakeLockDisabled() {
+ doReturn(true).when(mDeviceParameterProvider)
+ .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+ createService();
+ startSystem();
+ WakeLock wakeLock = acquireWakeLock("screenBrightWakeLock",
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK);
+ setUncachedUidProcState(wakeLock.mOwnerUid);
+
+ setCachedUidProcState(wakeLock.mOwnerUid);
+ assertThat(wakeLock.mDisabled).isTrue();
+ }
+
+ @Test
+ public void testFeatureDisabledProcStateUncachedToCached_screenBrightWakeLockEnabled() {
+ doReturn(false).when(mDeviceParameterProvider)
+ .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+ createService();
+ startSystem();
+ WakeLock wakeLock = acquireWakeLock("screenBrightWakeLock",
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK);
+ setUncachedUidProcState(wakeLock.mOwnerUid);
+
+ setCachedUidProcState(wakeLock.mOwnerUid);
+ assertThat(wakeLock.mDisabled).isFalse();
+ }
+
+ @Test
+ public void testFeatureEnabledProcStateUncachedToCached_screenDimWakeLockDisabled() {
+ doReturn(true).when(mDeviceParameterProvider)
+ .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+ createService();
+ startSystem();
+ WakeLock wakeLock = acquireWakeLock("screenDimWakeLock",
+ PowerManager.SCREEN_DIM_WAKE_LOCK);
+ setUncachedUidProcState(wakeLock.mOwnerUid);
+
+ setCachedUidProcState(wakeLock.mOwnerUid);
+ assertThat(wakeLock.mDisabled).isTrue();
+ }
+
+ @Test
+ public void testFeatureDisabledProcStateUncachedToCached_screenDimWakeLockEnabled() {
+ doReturn(false).when(mDeviceParameterProvider)
+ .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+ createService();
+ startSystem();
+ WakeLock wakeLock = acquireWakeLock("screenDimWakeLock",
+ PowerManager.SCREEN_DIM_WAKE_LOCK);
+ setUncachedUidProcState(wakeLock.mOwnerUid);
+
+ setCachedUidProcState(wakeLock.mOwnerUid);
+ assertThat(wakeLock.mDisabled).isFalse();
+ }
+
+ @Test
+ public void testFeatureEnabledProcStateCachedToUncached_fullWakeLockEnabled() {
+ doReturn(true).when(mDeviceParameterProvider)
+ .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+ createService();
+ startSystem();
+ WakeLock wakeLock = acquireWakeLock("fullWakeLock", PowerManager.FULL_WAKE_LOCK);
+ setCachedUidProcState(wakeLock.mOwnerUid);
+
+ setUncachedUidProcState(wakeLock.mOwnerUid);
+ assertThat(wakeLock.mDisabled).isFalse();
+ }
+
+ @Test
+ public void testFeatureDisabledProcStateCachedToUncached_fullWakeLockEnabled() {
+ doReturn(false).when(mDeviceParameterProvider)
+ .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+ createService();
+ startSystem();
+ WakeLock wakeLock = acquireWakeLock("fullWakeLock", PowerManager.FULL_WAKE_LOCK);
+ setCachedUidProcState(wakeLock.mOwnerUid);
+
+ setUncachedUidProcState(wakeLock.mOwnerUid);
+ assertThat(wakeLock.mDisabled).isFalse();
+ }
+
+ @Test
+ public void testFeatureEnabledProcStateCachedToUncached_screenBrightWakeLockEnabled() {
+ doReturn(true).when(mDeviceParameterProvider)
+ .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+ createService();
+ startSystem();
+ WakeLock wakeLock = acquireWakeLock("screenBrightWakeLock",
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK);
+ setCachedUidProcState(wakeLock.mOwnerUid);
+
+ setUncachedUidProcState(wakeLock.mOwnerUid);
+ assertThat(wakeLock.mDisabled).isFalse();
+ }
+
+ @Test
+ public void testFeatureDisabledProcStateCachedToUncached_screenBrightWakeLockEnabled() {
+ doReturn(false).when(mDeviceParameterProvider)
+ .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+ createService();
+ startSystem();
+ WakeLock wakeLock = acquireWakeLock("screenBrightWakeLock",
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK);
+ setCachedUidProcState(wakeLock.mOwnerUid);
+
+ setUncachedUidProcState(wakeLock.mOwnerUid);
+ assertThat(wakeLock.mDisabled).isFalse();
+ }
+
+ @Test
+ public void testFeatureEnabledProcStateCachedToUncached_screenDimWakeLockEnabled() {
+ doReturn(true).when(mDeviceParameterProvider)
+ .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+ createService();
+ startSystem();
+ WakeLock wakeLock = acquireWakeLock("screenDimWakeLock",
+ PowerManager.SCREEN_DIM_WAKE_LOCK);
+ setCachedUidProcState(wakeLock.mOwnerUid);
+
+ setUncachedUidProcState(wakeLock.mOwnerUid);
+ assertThat(wakeLock.mDisabled).isFalse();
+ }
+
+ @Test
+ public void testFeatureDisabledProcStateCachedToUncached_screenDimWakeLockEnabled() {
+ doReturn(false).when(mDeviceParameterProvider)
+ .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+ createService();
+ startSystem();
+ WakeLock wakeLock = acquireWakeLock("screenDimWakeLock",
+ PowerManager.SCREEN_DIM_WAKE_LOCK);
+ setCachedUidProcState(wakeLock.mOwnerUid);
+
+ setUncachedUidProcState(wakeLock.mOwnerUid);
+ assertThat(wakeLock.mDisabled).isFalse();
+ }
+
+ @Test
+ public void testFeatureDynamicallyDisabledProcStateUncachedToCached_fullWakeLockEnabled() {
+ doReturn(true).when(mDeviceParameterProvider)
+ .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+ ArgumentCaptor<DeviceConfig.OnPropertiesChangedListener> listenerCaptor =
+ ArgumentCaptor.forClass(DeviceConfig.OnPropertiesChangedListener.class);
+ createService();
+ startSystem();
+ verify(mDeviceParameterProvider, times(1))
+ .addOnPropertiesChangedListener(any(), listenerCaptor.capture());
+ WakeLock wakeLock = acquireWakeLock("fullWakeLock", PowerManager.FULL_WAKE_LOCK);
+ setUncachedUidProcState(wakeLock.mOwnerUid);
+ // dynamically disable the feature
+ doReturn(false).when(mDeviceParameterProvider)
+ .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+ listenerCaptor.getValue().onPropertiesChanged(
+ new DeviceConfig.Properties("ignored_namespace", null));
+
+ setUncachedUidProcState(wakeLock.mOwnerUid);
+ assertThat(wakeLock.mDisabled).isFalse();
+ }
+
+ private void setCachedUidProcState(int uid) {
+ mService.updateUidProcStateInternal(uid, PROCESS_STATE_TOP_SLEEPING);
+ }
+
+ private void setUncachedUidProcState(int uid) {
+ mService.updateUidProcStateInternal(uid, PROCESS_STATE_RECEIVER);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java b/services/tests/powerservicetests/src/com/android/server/power/ShutdownCheckPointsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java
rename to services/tests/powerservicetests/src/com/android/server/power/ShutdownCheckPointsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/power/ShutdownThreadTest.java b/services/tests/powerservicetests/src/com/android/server/power/ShutdownThreadTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/power/ShutdownThreadTest.java
rename to services/tests/powerservicetests/src/com/android/server/power/ShutdownThreadTest.java
diff --git a/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
rename to services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 32d0c98..989aee0 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -33,6 +33,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -42,6 +43,8 @@
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
import android.os.Handler;
import android.os.Message;
import android.os.UserHandle;
@@ -67,11 +70,13 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.List;
@@ -123,9 +128,10 @@
public static final int STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP = 8;
public static final int STATE_PANNING = 9;
public static final int STATE_SCALING_AND_PANNING = 10;
+ public static final int STATE_SINGLE_PANNING = 11;
public static final int FIRST_STATE = STATE_IDLE;
- public static final int LAST_STATE = STATE_SCALING_AND_PANNING;
+ public static final int LAST_STATE = STATE_SINGLE_PANNING;
// Co-prime x and y, to potentially catch x-y-swapped errors
public static final float DEFAULT_X = 301;
@@ -155,6 +161,10 @@
private float mOriginalMagnificationPersistedScale;
+ static final Rect INITIAL_MAGNIFICATION_BOUNDS = new Rect(0, 0, 800, 800);
+
+ static final Region INITIAL_MAGNIFICATION_REGION = new Region(INITIAL_MAGNIFICATION_BOUNDS);
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -182,11 +192,19 @@
new MagnificationScaleProvider(mContext),
() -> null,
ConcurrentUtils.DIRECT_EXECUTOR) {
- @Override
- public boolean magnificationRegionContains(int displayId, float x, float y) {
- return true;
- }
+ @Override
+ public boolean magnificationRegionContains(int displayId, float x, float y) {
+ return true;
+ }
};
+
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ Object[] args = invocationOnMock.getArguments();
+ Region regionArg = (Region) args[1];
+ regionArg.set(new Rect(INITIAL_MAGNIFICATION_BOUNDS));
+ return null;
+ }).when(mockWindowManager).getMagnificationRegion(anyInt(), any(Region.class));
+
mFullScreenMagnificationController.register(DISPLAY_0);
mFullScreenMagnificationController.setAlwaysOnMagnificationEnabled(true);
mClock = new OffsettableClock.Stopped();
@@ -214,6 +232,7 @@
mContext, mFullScreenMagnificationController, mMockTraceManager, mMockCallback,
detectTripleTap, detectShortcutTrigger,
mWindowMagnificationPromptController, DISPLAY_0);
+ h.setSinglePanningEnabled(true);
mHandler = new TestHandler(h.mDetectingState, mClock) {
@Override
protected String messageToString(Message m) {
@@ -239,6 +258,7 @@
* {@link #returnToNormalFrom} (for navigating back to {@link #STATE_IDLE})
*/
@Test
+ @Ignore("b/291925580")
public void testEachState_isReachableAndRecoverable() {
forEachState(state -> {
goFromStateIdleTo(state);
@@ -526,6 +546,75 @@
}
@Test
+ public void testActionUpNotAtEdge_singlePanningState_detectingState() {
+ goFromStateIdleTo(STATE_SINGLE_PANNING);
+
+ send(upEvent());
+
+ check(mMgh.mCurrentState == mMgh.mDetectingState, STATE_IDLE);
+ assertTrue(isZoomed());
+ }
+
+ @Test
+ public void testScroll_SinglePanningDisabled_delegatingState() {
+ mMgh.setSinglePanningEnabled(false);
+
+ goFromStateIdleTo(STATE_ACTIVATED);
+ allowEventDelegation();
+ swipeAndHold();
+
+ assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+ }
+
+ @Test
+ public void testScroll_zoomedStateAndAtEdge_delegatingState() {
+ goFromStateIdleTo(STATE_ACTIVATED);
+ mFullScreenMagnificationController.setCenter(
+ DISPLAY_0,
+ INITIAL_MAGNIFICATION_BOUNDS.left,
+ INITIAL_MAGNIFICATION_BOUNDS.top / 2,
+ false,
+ 1);
+ final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
+ PointF initCoords =
+ new PointF(
+ mFullScreenMagnificationController.getCenterX(DISPLAY_0),
+ mFullScreenMagnificationController.getCenterY(DISPLAY_0));
+ PointF endCoords = new PointF(initCoords.x, initCoords.y);
+ endCoords.offset(swipeMinDistance, 0);
+ allowEventDelegation();
+
+ swipeAndHold(initCoords, endCoords);
+
+ assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+ assertTrue(isZoomed());
+ }
+
+ @Test
+ public void testScroll_singlePanningAndAtEdge_delegatingState() {
+ goFromStateIdleTo(STATE_SINGLE_PANNING);
+ mFullScreenMagnificationController.setCenter(
+ DISPLAY_0,
+ INITIAL_MAGNIFICATION_BOUNDS.left,
+ INITIAL_MAGNIFICATION_BOUNDS.top / 2,
+ false,
+ 1);
+ final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
+ PointF initCoords =
+ new PointF(
+ mFullScreenMagnificationController.getCenterX(DISPLAY_0),
+ mFullScreenMagnificationController.getCenterY(DISPLAY_0));
+ PointF endCoords = new PointF(initCoords.x, initCoords.y);
+ endCoords.offset(swipeMinDistance, 0);
+ allowEventDelegation();
+
+ swipeAndHold(initCoords, endCoords);
+
+ assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+ assertTrue(isZoomed());
+ }
+
+ @Test
public void testShortcutTriggered_invokeShowWindowPromptAction() {
goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
@@ -740,6 +829,10 @@
state);
check(mMgh.mPanningScalingState.mScaling, state);
} break;
+ case STATE_SINGLE_PANNING: {
+ check(isZoomed(), state);
+ check(mMgh.mCurrentState == mMgh.mSinglePanningState, state);
+ } break;
default: throw new IllegalArgumentException("Illegal state: " + state);
}
}
@@ -803,6 +896,10 @@
send(pointerEvent(ACTION_MOVE, DEFAULT_X * 2, DEFAULT_Y * 4));
send(pointerEvent(ACTION_MOVE, DEFAULT_X * 2, DEFAULT_Y * 5));
} break;
+ case STATE_SINGLE_PANNING: {
+ goFromStateIdleTo(STATE_ACTIVATED);
+ swipeAndHold();
+ } break;
default:
throw new IllegalArgumentException("Illegal state: " + state);
}
@@ -859,6 +956,10 @@
case STATE_SCALING_AND_PANNING: {
returnToNormalFrom(STATE_PANNING);
} break;
+ case STATE_SINGLE_PANNING: {
+ send(upEvent());
+ returnToNormalFrom(STATE_ACTIVATED);
+ } break;
default: throw new IllegalArgumentException("Illegal state: " + state);
}
}
@@ -906,6 +1007,11 @@
send(moveEvent(DEFAULT_X * 2, DEFAULT_Y * 2));
}
+ private void swipeAndHold(PointF start, PointF end) {
+ send(downEvent(start.x, start.y));
+ send(moveEvent(end.x, end.y));
+ }
+
private void longTap() {
send(downEvent());
fastForward(2000);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index 27e6ef1..bbbab21 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -66,7 +66,6 @@
import com.android.server.statusbar.StatusBarManagerInternal;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -308,7 +307,6 @@
MagnificationScaleProvider.MAX_SCALE);
}
- @Ignore("b/278816260: We could refer to b/182561174#comment4 for solution.")
@Test
public void logTrackingTypingFocus_processScroll_logDuration() {
WindowMagnificationManager spyWindowMagnificationManager = spy(mWindowMagnificationManager);
diff --git a/services/tests/servicestests/src/com/android/server/appop/OWNERS b/services/tests/servicestests/src/com/android/server/appop/OWNERS
index 999ea0e..2fe78c5 100644
--- a/services/tests/servicestests/src/com/android/server/appop/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/appop/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
include /core/java/android/permission/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
index e457119..e7777f7 100644
--- a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
@@ -42,6 +42,7 @@
import com.android.server.LocalServices;
import com.android.server.contentprotection.ContentProtectionBlocklistManager;
+import com.android.server.contentprotection.ContentProtectionConsentManager;
import com.android.server.contentprotection.RemoteContentProtectionService;
import com.android.server.pm.UserManagerInternal;
@@ -91,6 +92,8 @@
@Mock private RemoteContentProtectionService mMockRemoteContentProtectionService;
+ @Mock private ContentProtectionConsentManager mMockContentProtectionConsentManager;
+
private boolean mDevCfgEnableContentProtectionReceiver;
private int mContentProtectionBlocklistManagersCreated;
@@ -99,6 +102,8 @@
private int mRemoteContentProtectionServicesCreated;
+ private int mContentProtectionConsentManagersCreated;
+
private String mConfigDefaultContentProtectionService = COMPONENT_NAME.flattenToString();
private boolean mContentProtectionServiceInfoConstructorShouldThrow;
@@ -114,43 +119,51 @@
}
@Test
- public void constructor_contentProtection_flagDisabled_noBlocklistManager() {
+ public void constructor_contentProtection_flagDisabled_noManagers() {
assertThat(mContentProtectionBlocklistManagersCreated).isEqualTo(0);
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
+ assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
verifyZeroInteractions(mMockContentProtectionBlocklistManager);
+ verifyZeroInteractions(mMockContentProtectionConsentManager);
}
@Test
- public void constructor_contentProtection_componentNameNull_noBlocklistManager() {
+ public void constructor_contentProtection_componentNameNull_noManagers() {
mConfigDefaultContentProtectionService = null;
mContentCaptureManagerService = new TestContentCaptureManagerService();
assertThat(mContentProtectionBlocklistManagersCreated).isEqualTo(0);
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
+ assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
verifyZeroInteractions(mMockContentProtectionBlocklistManager);
+ verifyZeroInteractions(mMockContentProtectionConsentManager);
}
@Test
- public void constructor_contentProtection_componentNameBlank_noBlocklistManager() {
+ public void constructor_contentProtection_componentNameBlank_noManagers() {
mConfigDefaultContentProtectionService = " ";
mContentCaptureManagerService = new TestContentCaptureManagerService();
assertThat(mContentProtectionBlocklistManagersCreated).isEqualTo(0);
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
+ assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
verifyZeroInteractions(mMockContentProtectionBlocklistManager);
+ verifyZeroInteractions(mMockContentProtectionConsentManager);
}
@Test
- public void constructor_contentProtection_enabled_createsBlocklistManager() {
+ public void constructor_contentProtection_enabled_createsManagers() {
mDevCfgEnableContentProtectionReceiver = true;
mContentCaptureManagerService = new TestContentCaptureManagerService();
assertThat(mContentProtectionBlocklistManagersCreated).isEqualTo(1);
+ assertThat(mContentProtectionConsentManagersCreated).isEqualTo(1);
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionBlocklistManager).updateBlocklist(anyInt());
+ verifyZeroInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -175,11 +188,13 @@
USER_ID, PACKAGE_NAME);
assertThat(actual).isNull();
- verify(mMockContentProtectionBlocklistManager).isAllowed(PACKAGE_NAME);
+ verify(mMockContentProtectionConsentManager).isConsentGranted(USER_ID);
+ verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
}
@Test
public void getOptions_contentCaptureDisabled_contentProtectionEnabled() {
+ when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true);
when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true);
mDevCfgEnableContentProtectionReceiver = true;
mContentCaptureManagerService = new TestContentCaptureManagerService();
@@ -211,11 +226,13 @@
assertThat(actual.contentProtectionOptions).isNotNull();
assertThat(actual.contentProtectionOptions.enableReceiver).isFalse();
assertThat(actual.whitelistedComponents).isNull();
- verify(mMockContentProtectionBlocklistManager).isAllowed(PACKAGE_NAME);
+ verify(mMockContentProtectionConsentManager).isConsentGranted(USER_ID);
+ verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
}
@Test
public void getOptions_contentCaptureEnabled_contentProtectionEnabled() {
+ when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true);
when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true);
mDevCfgEnableContentProtectionReceiver = true;
mContentCaptureManagerService = new TestContentCaptureManagerService();
@@ -234,7 +251,22 @@
}
@Test
+ public void isWhitelisted_packageName_contentCaptureDisabled_contentProtectionNotGranted() {
+ mDevCfgEnableContentProtectionReceiver = true;
+ mContentCaptureManagerService = new TestContentCaptureManagerService();
+
+ boolean actual =
+ mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted(
+ USER_ID, PACKAGE_NAME);
+
+ assertThat(actual).isFalse();
+ verify(mMockContentProtectionConsentManager).isConsentGranted(USER_ID);
+ verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
+ }
+
+ @Test
public void isWhitelisted_packageName_contentCaptureDisabled_contentProtectionDisabled() {
+ when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true);
mDevCfgEnableContentProtectionReceiver = true;
mContentCaptureManagerService = new TestContentCaptureManagerService();
@@ -248,6 +280,7 @@
@Test
public void isWhitelisted_packageName_contentCaptureDisabled_contentProtectionEnabled() {
+ when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true);
when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true);
mDevCfgEnableContentProtectionReceiver = true;
mContentCaptureManagerService = new TestContentCaptureManagerService();
@@ -271,11 +304,27 @@
USER_ID, PACKAGE_NAME);
assertThat(actual).isTrue();
+ verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt());
+ verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
+ }
+
+ @Test
+ public void isWhitelisted_componentName_contentCaptureDisabled_contentProtectionNotGranted() {
+ mDevCfgEnableContentProtectionReceiver = true;
+ mContentCaptureManagerService = new TestContentCaptureManagerService();
+
+ boolean actual =
+ mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted(
+ USER_ID, COMPONENT_NAME);
+
+ assertThat(actual).isFalse();
+ verify(mMockContentProtectionConsentManager).isConsentGranted(USER_ID);
verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
}
@Test
public void isWhitelisted_componentName_contentCaptureDisabled_contentProtectionDisabled() {
+ when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true);
mDevCfgEnableContentProtectionReceiver = true;
mContentCaptureManagerService = new TestContentCaptureManagerService();
@@ -289,6 +338,7 @@
@Test
public void isWhitelisted_componentName_contentCaptureDisabled_contentProtectionEnabled() {
+ when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true);
when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true);
mDevCfgEnableContentProtectionReceiver = true;
mContentCaptureManagerService = new TestContentCaptureManagerService();
@@ -312,16 +362,18 @@
USER_ID, COMPONENT_NAME);
assertThat(actual).isTrue();
+ verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt());
verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
}
@Test
- public void isContentProtectionReceiverEnabled_withoutBlocklistManager() {
+ public void isContentProtectionReceiverEnabled_withoutManagers() {
boolean actual =
mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted(
USER_ID, PACKAGE_NAME);
assertThat(actual).isFalse();
+ verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt());
verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
}
@@ -336,6 +388,7 @@
USER_ID, PACKAGE_NAME);
assertThat(actual).isFalse();
+ verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt());
verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
}
@@ -423,5 +476,11 @@
mRemoteContentProtectionServicesCreated++;
return mMockRemoteContentProtectionService;
}
+
+ @Override
+ protected ContentProtectionConsentManager createContentProtectionConsentManager() {
+ mContentProtectionConsentManagersCreated++;
+ return mMockContentProtectionConsentManager;
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING
new file mode 100644
index 0000000..0ffa891
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.contentcapture"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
new file mode 100644
index 0000000..0e80bfd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.contentprotection;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.DevicePolicyManagerInternal;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.testing.TestableContentResolver;
+import android.testing.TestableContext;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/**
+ * Test for {@link ContentProtectionConsentManager}.
+ *
+ * <p>Run with: {@code atest
+ * FrameworksServicesTests:com.android.server.contentprotection.ContentProtectionConsentManagerTest}
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ContentProtectionConsentManagerTest {
+
+ private static final String KEY_PACKAGE_VERIFIER_USER_CONSENT = "package_verifier_user_consent";
+
+ private static final Uri URI_PACKAGE_VERIFIER_USER_CONSENT =
+ Settings.Global.getUriFor(KEY_PACKAGE_VERIFIER_USER_CONSENT);
+
+ private static final int VALUE_TRUE = 1;
+
+ private static final int VALUE_FALSE = -1;
+
+ private static final int VALUE_DEFAULT = 0;
+
+ private static final int TEST_USER_ID = 1234;
+
+ @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Rule
+ public final TestableContext mTestableContext =
+ new TestableContext(ApplicationProvider.getApplicationContext());
+
+ private final TestableContentResolver mTestableContentResolver =
+ mTestableContext.getContentResolver();
+
+ @Mock private ContentResolver mMockContentResolver;
+
+ @Mock private DevicePolicyManagerInternal mMockDevicePolicyManagerInternal;
+
+ @Test
+ public void constructor_registersContentObserver() {
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(mMockContentResolver);
+
+ assertThat(manager.mContentObserver).isNotNull();
+ verify(mMockContentResolver)
+ .registerContentObserver(
+ URI_PACKAGE_VERIFIER_USER_CONSENT,
+ /* notifyForDescendants= */ false,
+ manager.mContentObserver,
+ UserHandle.USER_ALL);
+ }
+
+ @Test
+ public void isConsentGranted_packageVerifierNotGranted() {
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_FALSE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isFalse();
+ verifyZeroInteractions(mMockDevicePolicyManagerInternal);
+ }
+
+ @Test
+ public void isConsentGranted_packageVerifierGranted_userNotManaged() {
+ ContentProtectionConsentManager manager = createContentProtectionConsentManager(VALUE_TRUE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isTrue();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ }
+
+ @Test
+ public void isConsentGranted_packageVerifierGranted_userManaged() {
+ when(mMockDevicePolicyManagerInternal.isUserOrganizationManaged(TEST_USER_ID))
+ .thenReturn(true);
+ ContentProtectionConsentManager manager = createContentProtectionConsentManager(VALUE_TRUE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isFalse();
+ }
+
+ @Test
+ public void isConsentGranted_packageVerifierDefault() {
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_DEFAULT);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isFalse();
+ verifyZeroInteractions(mMockDevicePolicyManagerInternal);
+ }
+
+ @Test
+ public void contentObserver() throws Exception {
+ ContentProtectionConsentManager manager = createContentProtectionConsentManager(VALUE_TRUE);
+ boolean firstActual = manager.isConsentGranted(TEST_USER_ID);
+
+ Settings.Global.putInt(
+ mTestableContentResolver, KEY_PACKAGE_VERIFIER_USER_CONSENT, VALUE_FALSE);
+ // Observer has to be called manually, mTestableContentResolver is not propagating
+ manager.mContentObserver.onChange(
+ /* selfChange= */ false, URI_PACKAGE_VERIFIER_USER_CONSENT, TEST_USER_ID);
+ boolean secondActual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(firstActual).isTrue();
+ assertThat(secondActual).isFalse();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ }
+
+ private ContentProtectionConsentManager createContentProtectionConsentManager(
+ ContentResolver contentResolver) {
+ return new ContentProtectionConsentManager(
+ new Handler(Looper.getMainLooper()),
+ contentResolver,
+ mMockDevicePolicyManagerInternal);
+ }
+
+ private ContentProtectionConsentManager createContentProtectionConsentManager(
+ int valuePackageVerifierUserConsent) {
+ Settings.Global.putInt(
+ mTestableContentResolver,
+ KEY_PACKAGE_VERIFIER_USER_CONSENT,
+ valuePackageVerifierUserConsent);
+ return createContentProtectionConsentManager(mTestableContentResolver);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING
new file mode 100644
index 0000000..419508c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.contentprotection"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java b/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java
index d5ad815..b5bf1ea 100644
--- a/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java
@@ -16,7 +16,11 @@
package com.android.server.dreams;
+import static android.os.PowerManager.USER_ACTIVITY_EVENT_OTHER;
+import static android.os.PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS;
+
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
@@ -32,7 +36,9 @@
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IPowerManager;
import android.os.IRemoteCallback;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.test.TestLooper;
import android.service.dreams.IDreamService;
@@ -58,6 +64,8 @@
@Mock
private ActivityTaskManager mActivityTaskManager;
+ @Mock
+ private IPowerManager mPowerManager;
@Mock
private IBinder mIBinder;
@@ -67,6 +75,8 @@
@Captor
private ArgumentCaptor<ServiceConnection> mServiceConnectionACaptor;
@Captor
+ private ArgumentCaptor<IBinder.DeathRecipient> mDeathRecipientCaptor;
+ @Captor
private ArgumentCaptor<IRemoteCallback> mRemoteCallbackCaptor;
private final TestLooper mLooper = new TestLooper();
@@ -90,6 +100,12 @@
when(mContext.getSystemServiceName(ActivityTaskManager.class))
.thenReturn(Context.ACTIVITY_TASK_SERVICE);
+ final PowerManager powerManager = new PowerManager(mContext, mPowerManager, null, null);
+ when(mContext.getSystemService(Context.POWER_SERVICE))
+ .thenReturn(powerManager);
+ when(mContext.getSystemServiceName(PowerManager.class))
+ .thenReturn(Context.POWER_SERVICE);
+
mToken = new Binder();
mDreamName = ComponentName.unflattenFromString("dream");
mOverlayName = ComponentName.unflattenFromString("dream_overlay");
@@ -209,9 +225,51 @@
verify(mIDreamService).detach();
}
+ @Test
+ public void serviceDisconnect_resetsScreenTimeout() throws RemoteException {
+ // Start dream.
+ mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
+ 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
+ ServiceConnection serviceConnection = captureServiceConnection();
+ serviceConnection.onServiceConnected(mDreamName, mIBinder);
+ mLooper.dispatchAll();
+
+ // Dream disconnects unexpectedly.
+ serviceConnection.onServiceDisconnected(mDreamName);
+ mLooper.dispatchAll();
+
+ // Power manager receives user activity signal.
+ verify(mPowerManager).userActivity(/*displayId=*/ anyInt(), /*time=*/ anyLong(),
+ eq(USER_ACTIVITY_EVENT_OTHER),
+ eq(USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS));
+ }
+
+ @Test
+ public void binderDied_resetsScreenTimeout() throws RemoteException {
+ // Start dream.
+ mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
+ 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
+ captureServiceConnection().onServiceConnected(mDreamName, mIBinder);
+ mLooper.dispatchAll();
+
+ // Dream binder dies.
+ captureDeathRecipient().binderDied();
+ mLooper.dispatchAll();
+
+ // Power manager receives user activity signal.
+ verify(mPowerManager).userActivity(/*displayId=*/ anyInt(), /*time=*/ anyLong(),
+ eq(USER_ACTIVITY_EVENT_OTHER),
+ eq(USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS));
+ }
+
private ServiceConnection captureServiceConnection() {
verify(mContext).bindServiceAsUser(any(), mServiceConnectionACaptor.capture(), anyInt(),
any());
return mServiceConnectionACaptor.getValue();
}
+
+ private IBinder.DeathRecipient captureDeathRecipient() throws RemoteException {
+ verify(mIBinder).linkToDeath(mDeathRecipientCaptor.capture(), anyInt());
+ return mDeathRecipientCaptor.getValue();
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index a109d5c..0292bca 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -81,7 +81,6 @@
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
-import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ALLOW_DISMISS_ONGOING;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.WAKE_LOCK_FOR_POSTING_NOTIFICATION;
@@ -406,7 +405,6 @@
UriGrantsManagerInternal mUgmInternal;
@Mock
AppOpsManager mAppOpsManager;
- private AppOpsManager.OnOpChangedListener mOnPermissionChangeListener;
@Mock
private TestableNotificationManagerService.NotificationAssistantAccessGrantedCallback
mNotificationAssistantAccessGrantedCallback;
@@ -606,12 +604,6 @@
tr.addOverride(com.android.internal.R.string.config_defaultSearchSelectorPackageName,
SEARCH_SELECTOR_PKG);
- doAnswer(invocation -> {
- mOnPermissionChangeListener = invocation.getArgument(2);
- return null;
- }).when(mAppOpsManager).startWatchingMode(eq(AppOpsManager.OP_POST_NOTIFICATION), any(),
- any());
-
mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper()));
mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient,
mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr,
@@ -3224,6 +3216,48 @@
}
@Test
+ public void testUpdateAppNotifyCreatorBlock() throws Exception {
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
+
+ mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
+ Thread.sleep(500);
+ waitForIdle();
+
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
+
+ assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
+ captor.getValue().getAction());
+ assertEquals(PKG, captor.getValue().getPackage());
+ assertTrue(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
+ }
+
+ @Test
+ public void testUpdateAppNotifyCreatorBlock_notIfMatchesExistingSetting() throws Exception {
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+
+ mBinderService.setNotificationsEnabledForPackage(PKG, 0, false);
+ verify(mContext, never()).sendBroadcastAsUser(any(), any(), eq(null));
+ }
+
+ @Test
+ public void testUpdateAppNotifyCreatorUnblock() throws Exception {
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+
+ mBinderService.setNotificationsEnabledForPackage(PKG, mUid, true);
+ Thread.sleep(500);
+ waitForIdle();
+
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
+
+ assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
+ captor.getValue().getAction());
+ assertEquals(PKG, captor.getValue().getPackage());
+ assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
+ }
+
+ @Test
public void testUpdateChannelNotifyCreatorBlock() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
@@ -11169,8 +11203,6 @@
ai.packageName)).thenReturn(AppOpsManager.MODE_IGNORED);
// Given: a notification from an app on the system partition has the flag
// FLAG_ONGOING_EVENT set
- // feature flag: ALLOW_DISMISS_ONGOING is on
- mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
Notification n = new Notification.Builder(mContext, "test")
.setOngoing(true)
.build();
@@ -11186,8 +11218,6 @@
public void fixMediaNotification_withOnGoingFlag_shouldBeNonDismissible()
throws Exception {
// Given: a media notification has the flag FLAG_ONGOING_EVENT set
- // feature flag: ALLOW_DISMISS_ONGOING is on
- mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
Notification n = new Notification.Builder(mContext, "test")
.setOngoing(true)
.setStyle(new Notification.MediaStyle()
@@ -11216,8 +11246,6 @@
ai.packageName)).thenReturn(AppOpsManager.MODE_IGNORED);
// Given: a notification from an app on the system partition has the flag
// FLAG_ONGOING_EVENT set
- // feature flag: ALLOW_DISMISS_ONGOING is on
- mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
Notification n = new Notification.Builder(mContext, "test")
.setOngoing(true)
.build();
@@ -11233,9 +11261,6 @@
public void fixCallNotification_withOnGoingFlag_shouldNotBeNonDismissible()
throws Exception {
// Given: a call notification has the flag FLAG_ONGOING_EVENT set
- // feature flag: ALLOW_DISMISS_ONGOING is on
- mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
-
Person person = new Person.Builder()
.setName("caller")
.build();
@@ -11256,8 +11281,6 @@
@Test
public void fixNonExemptNotification_withOnGoingFlag_shouldBeDismissible() throws Exception {
// Given: a non-exempt notification has the flag FLAG_ONGOING_EVENT set
- // feature flag: ALLOW_DISMISS_ONGOING is on
- mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
Notification n = new Notification.Builder(mContext, "test")
.setOngoing(true)
.build();
@@ -11274,8 +11297,6 @@
throws Exception {
// Given: a non-exempt notification has the flag FLAG_NO_DISMISS set (even though this is
// not allowed)
- // feature flag: ALLOW_DISMISS_ONGOING is on
- mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
Notification n = new Notification.Builder(mContext, "test")
.build();
n.flags |= Notification.FLAG_NO_DISMISS;
@@ -11290,8 +11311,6 @@
@Test
public void fixMediaNotification_withoutOnGoingFlag_shouldBeDismissible() throws Exception {
// Given: a media notification doesn't have the flag FLAG_ONGOING_EVENT set
- // feature flag: ALLOW_DISMISS_ONGOING is on
- mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
Notification n = new Notification.Builder(mContext, "test")
.setOngoing(false)
.setStyle(new Notification.MediaStyle()
@@ -11310,8 +11329,6 @@
throws Exception {
// Given: a media notification doesn't have the flag FLAG_ONGOING_EVENT set,
// but has the flag FLAG_NO_DISMISS set
- // feature flag: ALLOW_DISMISS_ONGOING is on
- mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
Notification n = new Notification.Builder(mContext, "test")
.setOngoing(false)
.setStyle(new Notification.MediaStyle()
@@ -11330,8 +11347,6 @@
public void fixNonExempt_Notification_withoutOnGoingFlag_shouldBeDismissible()
throws Exception {
// Given: a non-exempt notification has the flag FLAG_ONGOING_EVENT set
- // feature flag: ALLOW_DISMISS_ONGOING is on
- mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
Notification n = new Notification.Builder(mContext, "test")
.setOngoing(false)
.build();
@@ -11348,8 +11363,6 @@
throws Exception {
when(mDevicePolicyManager.isActiveDeviceOwner(mUid)).thenReturn(true);
// Given: a notification has the flag FLAG_ONGOING_EVENT set
- // feature flag: ALLOW_DISMISS_ONGOING is on
- mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
setDpmAppOppsExemptFromDismissal(false);
Notification n = new Notification.Builder(mContext, "test")
.setOngoing(true)
@@ -11376,8 +11389,6 @@
AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid,
PKG)).thenReturn(AppOpsManager.MODE_ALLOWED);
// Given: a notification has the flag FLAG_ONGOING_EVENT set
- // feature flag: ALLOW_DISMISS_ONGOING is on
- mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
setDpmAppOppsExemptFromDismissal(true);
Notification n = new Notification.Builder(mContext, "test")
.setOngoing(true)
@@ -11397,8 +11408,6 @@
AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid,
PKG)).thenReturn(AppOpsManager.MODE_ALLOWED);
// Given: a notification has the flag FLAG_ONGOING_EVENT set
- // feature flag: ALLOW_DISMISS_ONGOING is on
- mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
setDpmAppOppsExemptFromDismissal(false);
Notification n = new Notification.Builder(mContext, "test")
.setOngoing(true)
@@ -12139,134 +12148,6 @@
any(), eq(FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER | FLAG_SERVICE_SENDER));
}
- @Test
- public void onOpChanged_permissionRevoked_cancelsAllNotificationsFromPackage()
- throws RemoteException {
- // Have preexisting posted notifications from revoked package and other packages.
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("revoked", 1001, 1, 0), mTestNotificationChannel));
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 2, 0), mTestNotificationChannel));
- // Have preexisting enqueued notifications from revoked package and other packages.
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("revoked", 1001, 3, 0), mTestNotificationChannel));
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 4, 0), mTestNotificationChannel));
- assertThat(mService.mNotificationList).hasSize(2);
- assertThat(mService.mEnqueuedNotifications).hasSize(2);
-
- when(mPackageManagerInternal.getPackageUid("revoked", 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(false);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, "revoked", 0);
- waitForIdle();
-
- assertThat(mService.mNotificationList).hasSize(1);
- assertThat(mService.mNotificationList.get(0).getSbn().getPackageName()).isEqualTo("other");
- assertThat(mService.mEnqueuedNotifications).hasSize(1);
- assertThat(mService.mEnqueuedNotifications.get(0).getSbn().getPackageName()).isEqualTo(
- "other");
- }
-
- @Test
- public void onOpChanged_permissionStillGranted_notificationsAreNotAffected()
- throws RemoteException {
- // NOTE: This combination (receiving the onOpChanged broadcast for a package, the permission
- // being now granted, AND having previously posted notifications from said package) should
- // never happen (if we trust the broadcasts are correct). So this test is for a what-if
- // scenario, to verify we still handle it reasonably.
-
- // Have preexisting posted notifications from specific package and other packages.
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("granted", 1001, 1, 0), mTestNotificationChannel));
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 2, 0), mTestNotificationChannel));
- // Have preexisting enqueued notifications from specific package and other packages.
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("granted", 1001, 3, 0), mTestNotificationChannel));
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 4, 0), mTestNotificationChannel));
- assertThat(mService.mNotificationList).hasSize(2);
- assertThat(mService.mEnqueuedNotifications).hasSize(2);
-
- when(mPackageManagerInternal.getPackageUid("granted", 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(true);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, "granted", 0);
- waitForIdle();
-
- assertThat(mService.mNotificationList).hasSize(2);
- assertThat(mService.mEnqueuedNotifications).hasSize(2);
- }
-
- @Test
- public void onOpChanged_permissionGranted_notifiesAppUnblocked() throws Exception {
- when(mPackageManagerInternal.getPackageUid(PKG, 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(true);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0);
- waitForIdle();
- mTestableLooper.moveTimeForward(500);
- waitForIdle();
-
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext).sendBroadcastAsUser(captor.capture(), any(), eq(null));
- assertThat(captor.getValue().getAction()).isEqualTo(
- NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED);
- assertThat(captor.getValue().getPackage()).isEqualTo(PKG);
- assertThat(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true)).isFalse();
- }
-
- @Test
- public void onOpChanged_permissionRevoked_notifiesAppBlocked() throws Exception {
- when(mPackageManagerInternal.getPackageUid(PKG, 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(false);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0);
- waitForIdle();
- mTestableLooper.moveTimeForward(500);
- waitForIdle();
-
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext).sendBroadcastAsUser(captor.capture(), any(), eq(null));
- assertThat(captor.getValue().getAction()).isEqualTo(
- NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED);
- assertThat(captor.getValue().getPackage()).isEqualTo(PKG);
- assertThat(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, false)).isTrue();
- }
-
- @Test
- public void setNotificationsEnabledForPackage_disabling_clearsNotifications() throws Exception {
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("package", 1001, 1, 0), mTestNotificationChannel));
- assertThat(mService.mNotificationList).hasSize(1);
- when(mPackageManagerInternal.getPackageUid("package", 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasRequestedPermission(any(), eq("package"), anyInt())).thenReturn(
- true);
-
- // Start with granted permission and simulate effect of revoking it.
- when(mPermissionHelper.hasPermission(1001)).thenReturn(true);
- doAnswer(invocation -> {
- when(mPermissionHelper.hasPermission(1001)).thenReturn(false);
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, "package", 0);
- return null;
- }).when(mPermissionHelper).setNotificationPermission("package", 0, false, true);
-
- mBinderService.setNotificationsEnabledForPackage("package", 1001, false);
- waitForIdle();
-
- assertThat(mService.mNotificationList).hasSize(0);
-
- mTestableLooper.moveTimeForward(500);
- waitForIdle();
- verify(mContext).sendBroadcastAsUser(any(), eq(UserHandle.of(0)), eq(null));
- }
-
private static <T extends Parcelable> T parcelAndUnparcel(T source,
Parcelable.Creator<T> creator) {
Parcel parcel = Parcel.obtain();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index dedb8f1..3ee75de 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -771,7 +771,7 @@
mZenModeHelper.mConfig = null; // will evaluate config to zen mode off
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -798,7 +798,7 @@
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -825,7 +825,7 @@
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -2269,7 +2269,7 @@
// Artificially turn zen mode "on". Re-evaluating zen mode should cause it to turn back off
// given that we don't have any zen rules active.
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
// Check that the change actually took: zen mode should be off now
assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
diff --git a/services/tests/vibrator/AndroidManifest.xml b/services/tests/vibrator/AndroidManifest.xml
index 2a15c15..a14ea55 100644
--- a/services/tests/vibrator/AndroidManifest.xml
+++ b/services/tests/vibrator/AndroidManifest.xml
@@ -31,8 +31,7 @@
<!-- Required to set always-on vibrations -->
<uses-permission android:name="android.permission.VIBRATE_ALWAYS_ON" />
- <application android:debuggable="true"
- android:testOnly="true">
+ <application android:debuggable="true">
<uses-library android:name="android.test.mock" android:required="true" />
<uses-library android:name="android.test.runner" />
</application>
diff --git a/services/tests/vibrator/TEST_MAPPING b/services/tests/vibrator/TEST_MAPPING
index f0a7e47..22b72fa 100644
--- a/services/tests/vibrator/TEST_MAPPING
+++ b/services/tests/vibrator/TEST_MAPPING
@@ -1,21 +1,7 @@
{
- "presubmit": [
+ "imports": [
{
- "name": "FrameworksVibratorServicesTests",
- "options": [
- {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "androidx.test.filters.FlakyTest"},
- {"exclude-annotation": "org.junit.Ignore"}
- ]
- }
- ],
- "postsubmit": [
- {
- "name": "FrameworksVibratorServicesTests",
- "options": [
- {"exclude-annotation": "org.junit.Ignore"}
- ]
+ "path": "frameworks/base/services/core/java/com/android/server/vibrator"
}
]
}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java
new file mode 100644
index 0000000..a81898d
--- /dev/null
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+
+import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
+import static android.os.VibrationEffect.EFFECT_CLICK;
+
+import static com.android.server.vibrator.HapticFeedbackCustomization.CustomizationParserException;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.os.VibrationEffect;
+import android.util.AtomicFile;
+import android.util.SparseArray;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.R;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+public class HapticFeedbackCustomizationTest {
+ @Rule public MockitoRule rule = MockitoJUnit.rule();
+
+ // Pairs of valid vibration XML along with their equivalent VibrationEffect.
+ private static final String COMPOSITION_VIBRATION_XML = "<vibration>"
+ + "<primitive-effect name=\"tick\" scale=\"0.2497\"/>"
+ + "</vibration>";
+ private static final VibrationEffect COMPOSITION_VIBRATION =
+ VibrationEffect.startComposition().addPrimitive(PRIMITIVE_TICK, 0.2497f).compose();
+
+ private static final String PREDEFINED_VIBRATION_XML =
+ "<vibration><predefined-effect name=\"click\"/></vibration>";
+ private static final VibrationEffect PREDEFINED_VIBRATION =
+ VibrationEffect.createPredefined(EFFECT_CLICK);
+
+ @Mock private Resources mResourcesMock;
+
+ @Test
+ public void testParseCustomizations_noCustomization_success() throws Exception {
+ assertParseCustomizationsSucceeds(
+ /* xml= */ "<haptic-feedback-constants></haptic-feedback-constants>",
+ /* expectedCustomizations= */ new SparseArray<>());
+ }
+
+ @Test
+ public void testParseCustomizations_oneCustomization_success() throws Exception {
+ String xml = "<haptic-feedback-constants>"
+ + "<constant id=\"10\">"
+ + COMPOSITION_VIBRATION_XML
+ + "</constant>"
+ + "</haptic-feedback-constants>";
+ SparseArray<VibrationEffect> expectedMapping = new SparseArray<>();
+ expectedMapping.put(10, COMPOSITION_VIBRATION);
+
+ assertParseCustomizationsSucceeds(xml, expectedMapping);
+ }
+
+ @Test
+ public void testParseCustomizations_multipleCustomizations_success() throws Exception {
+ String xml = "<haptic-feedback-constants>"
+ + "<constant id=\"1\">"
+ + COMPOSITION_VIBRATION_XML
+ + "</constant>"
+ + "<constant id=\"12\">"
+ + PREDEFINED_VIBRATION_XML
+ + "</constant>"
+ + "<constant id=\"150\">"
+ + PREDEFINED_VIBRATION_XML
+ + "</constant>"
+ + "</haptic-feedback-constants>";
+ SparseArray<VibrationEffect> expectedMapping = new SparseArray<>();
+ expectedMapping.put(1, COMPOSITION_VIBRATION);
+ expectedMapping.put(12, PREDEFINED_VIBRATION);
+ expectedMapping.put(150, PREDEFINED_VIBRATION);
+
+ assertParseCustomizationsSucceeds(xml, expectedMapping);
+ }
+
+ @Test
+ public void testParseCustomizations_noCustomizationFile_returnsNull() throws Exception {
+ setCustomizationFilePath("");
+
+ assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock)).isNull();
+
+ setCustomizationFilePath(null);
+
+ assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock)).isNull();
+
+ setCustomizationFilePath("non_existent_file.xml");
+
+ assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock)).isNull();
+ }
+
+ @Test
+ public void testParseCustomizations_disallowedVibrationForHapticFeedback_throwsException()
+ throws Exception {
+ // The XML content is good, but the serialized vibration is not supported for haptic
+ // feedback usage (i.e. repeating vibration).
+ assertParseCustomizationsFails(
+ "<haptic-feedback-constants>"
+ + "<constant id=\"10\">"
+ + "<vibration>"
+ + "<waveform-effect>"
+ + "<repeating>"
+ + "<waveform-entry durationMs=\"10\" amplitude=\"100\"/>"
+ + "</repeating>"
+ + "</waveform-effect>"
+ + "</vibration>"
+ + "</constant>"
+ + "</haptic-feedback-constants>");
+ }
+
+ @Test
+ public void testParseCustomizations_emptyXml_throwsException() throws Exception {
+ assertParseCustomizationsFails("");
+ }
+
+ @Test
+ public void testParseCustomizations_noVibrationXml_throwsException() throws Exception {
+ assertParseCustomizationsFails(
+ "<haptic-feedback-constants>"
+ + "<constant id=\"1\">"
+ + "</constant>"
+ + "</haptic-feedback-constants>");
+ }
+
+ @Test
+ public void testParseCustomizations_badEffectId_throwsException() throws Exception {
+ // Negative id
+ assertParseCustomizationsFails(
+ "<haptic-feedback-constants>"
+ + "<constant id=\"-10\">"
+ + COMPOSITION_VIBRATION_XML
+ + "</constant>"
+ + "</haptic-feedback-constants>");
+
+ // Non-numeral id
+ assertParseCustomizationsFails(
+ "<haptic-feedback-constants>"
+ + "<constant id=\"xyz\">"
+ + COMPOSITION_VIBRATION_XML
+ + "</constant>"
+ + "</haptic-feedback-constants>");
+ }
+
+ @Test
+ public void testParseCustomizations_malformedXml_throwsException() throws Exception {
+ // No start "<constant>" tag
+ assertParseCustomizationsFails(
+ "<haptic-feedback-constants>"
+ + COMPOSITION_VIBRATION_XML
+ + "</constant>"
+ + "</haptic-feedback-constants>");
+
+ // No end "<constant>" tag
+ assertParseCustomizationsFails(
+ "<haptic-feedback-constants>"
+ + "<constant id=\"10\">"
+ + COMPOSITION_VIBRATION_XML
+ + "</haptic-feedback-constants>");
+
+ // No start "<haptic-feedback-constants>" tag
+ assertParseCustomizationsFails(
+ "<constant id=\"10\">"
+ + COMPOSITION_VIBRATION_XML
+ + "</constant>"
+ + "</haptic-feedback-constants>");
+
+ // No end "<haptic-feedback-constants>" tag
+ assertParseCustomizationsFails(
+ "<haptic-feedback-constants>"
+ + "<constant id=\"10\">"
+ + COMPOSITION_VIBRATION_XML
+ + "</constant>");
+ }
+
+ @Test
+ public void testParseCustomizations_badVibrationXml_throwsException() throws Exception {
+ assertParseCustomizationsFails(
+ "<haptic-feedback-constants>"
+ + "<constant id=\"10\">"
+ + "<bad-vibration></bad-vibration>"
+ + "</constant>"
+ + "</haptic-feedback-constants>");
+
+ assertParseCustomizationsFails(
+ "<haptic-feedback-constants>"
+ + "<constant id=\"10\">"
+ + "<vibration><predefined-effect name=\"bad-effect-name\"/></vibration>"
+ + "</constant>"
+ + "</haptic-feedback-constants>");
+ }
+
+ @Test
+ public void testParseCustomizations_badConstantAttribute_throwsException() throws Exception {
+ assertParseCustomizationsFails(
+ "<haptic-feedback-constants>"
+ + "<constant iddddd=\"10\">"
+ + COMPOSITION_VIBRATION_XML
+ + "</constant>"
+ + "</haptic-feedback-constants>");
+
+ assertParseCustomizationsFails(
+ "<haptic-feedback-constants>"
+ + "<constant id=\"10\" unwanted-attr=\"1\">"
+ + COMPOSITION_VIBRATION_XML
+ + "</constant>"
+ + "</haptic-feedback-constants>");
+ }
+
+ @Test
+ public void testParseCustomizations_duplicateEffects_throwsException() throws Exception {
+ assertParseCustomizationsFails(
+ "<haptic-feedback-constants>"
+ + "<constant id=\"10\">"
+ + COMPOSITION_VIBRATION_XML
+ + "</constant>"
+ + "<constant id=\"10\">"
+ + PREDEFINED_VIBRATION_XML
+ + "</constant>"
+ + "<constant id=\"11\">"
+ + PREDEFINED_VIBRATION_XML
+ + "</constant>"
+ + "</haptic-feedback-constants>");
+ }
+
+ private void assertParseCustomizationsSucceeds(
+ String xml, SparseArray<VibrationEffect> expectedCustomizations) throws Exception {
+ setupCustomizationFile(xml);
+ assertThat(expectedCustomizations.contentEquals(
+ HapticFeedbackCustomization.loadVibrations(mResourcesMock))).isTrue();
+ }
+
+ private void assertParseCustomizationsFails(String xml) throws Exception {
+ setupCustomizationFile(xml);
+ assertThrows("Expected haptic feedback customization to fail for " + xml,
+ CustomizationParserException.class,
+ () -> HapticFeedbackCustomization.loadVibrations(mResourcesMock));
+ }
+
+ private void assertParseCustomizationsFails() throws Exception {
+ assertThrows("Expected haptic feedback customization to fail",
+ CustomizationParserException.class,
+ () -> HapticFeedbackCustomization.loadVibrations(mResourcesMock));
+ }
+
+ private void setupCustomizationFile(String xml) throws Exception {
+ File file = createFile(xml);
+ setCustomizationFilePath(file.getAbsolutePath());
+ }
+
+ private void setCustomizationFilePath(String path) {
+ when(mResourcesMock.getString(R.string.config_hapticFeedbackCustomizationFile))
+ .thenReturn(path);
+ }
+
+ private static File createFile(String contents) throws Exception {
+ File file = new File(InstrumentationRegistry.getContext().getCacheDir(), "test.xml");
+ file.createNewFile();
+
+ AtomicFile testAtomicXmlFile = new AtomicFile(file);
+ FileOutputStream fos = testAtomicXmlFile.startWrite();
+ fos.write(contents.getBytes());
+ testAtomicXmlFile.finishWrite(fos);
+
+ return file;
+ }
+}
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index a0e6cf5..c2812a1 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -52,6 +52,7 @@
"service-permission.stubs.system_server",
"androidx.test.runner",
"androidx.test.rules",
+ "junit-params",
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
"servicestests-utils",
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index 2015ae9..bf88ce4 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -63,7 +63,7 @@
final Context mContext = spy(getInstrumentation().getTargetContext());
/** Modifier key to meta state */
- private static final Map<Integer, Integer> MODIFIER;
+ protected static final Map<Integer, Integer> MODIFIER;
static {
final Map<Integer, Integer> map = new ArrayMap<>();
map.put(KEYCODE_CTRL_LEFT, META_CTRL_LEFT_ON | META_CTRL_ON);
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
new file mode 100644
index 0000000..feca326
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.KeyEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.annotations.Keep;
+import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+@Presubmit
+@SmallTest
+@RunWith(JUnitParamsRunner.class)
+public class ShortcutLoggingTests extends ShortcutKeyTestBase {
+
+ private static final int VENDOR_ID = 0x123;
+ private static final int PRODUCT_ID = 0x456;
+ private static final int META_KEY = KeyEvent.KEYCODE_META_LEFT;
+ private static final int META_ON = MODIFIER.get(KeyEvent.KEYCODE_META_LEFT);
+ private static final int ALT_KEY = KeyEvent.KEYCODE_ALT_LEFT;
+ private static final int ALT_ON = MODIFIER.get(KeyEvent.KEYCODE_ALT_LEFT);
+ private static final int CTRL_KEY = KeyEvent.KEYCODE_CTRL_LEFT;
+ private static final int CTRL_ON = MODIFIER.get(KeyEvent.KEYCODE_CTRL_LEFT);
+ private static final int SHIFT_KEY = KeyEvent.KEYCODE_SHIFT_LEFT;
+ private static final int SHIFT_ON = MODIFIER.get(KeyEvent.KEYCODE_SHIFT_LEFT);
+
+ @Keep
+ private static Object[][] shortcutTestArguments() {
+ // testName, testKeys, expectedLogEvent, expectedKey, expectedModifierState
+ return new Object[][]{
+ {"Meta + H -> Open Home", new int[]{META_KEY, KeyEvent.KEYCODE_H},
+ KeyboardLogEvent.HOME, KeyEvent.KEYCODE_H, META_ON},
+ {"Meta + Enter -> Open Home", new int[]{META_KEY, KeyEvent.KEYCODE_ENTER},
+ KeyboardLogEvent.HOME, KeyEvent.KEYCODE_ENTER, META_ON},
+ {"HOME key -> Open Home", new int[]{KeyEvent.KEYCODE_HOME}, KeyboardLogEvent.HOME,
+ KeyEvent.KEYCODE_HOME, 0},
+ {"RECENT_APPS key -> Open Overview", new int[]{KeyEvent.KEYCODE_RECENT_APPS},
+ KeyboardLogEvent.RECENT_APPS, KeyEvent.KEYCODE_RECENT_APPS, 0},
+ {"Meta + Tab -> Open OVerview", new int[]{META_KEY, KeyEvent.KEYCODE_TAB},
+ KeyboardLogEvent.RECENT_APPS, KeyEvent.KEYCODE_TAB, META_ON},
+ {"Alt + Tab -> Open Overview", new int[]{ALT_KEY, KeyEvent.KEYCODE_TAB},
+ KeyboardLogEvent.RECENT_APPS, KeyEvent.KEYCODE_TAB, ALT_ON},
+ {"BACK key -> Go back", new int[]{KeyEvent.KEYCODE_BACK}, KeyboardLogEvent.BACK,
+ KeyEvent.KEYCODE_BACK, 0},
+ {"APP_SWITCH key -> Open App switcher", new int[]{KeyEvent.KEYCODE_APP_SWITCH},
+ KeyboardLogEvent.APP_SWITCH, KeyEvent.KEYCODE_APP_SWITCH, 0},
+ {"ASSIST key -> Launch assistant", new int[]{KeyEvent.KEYCODE_ASSIST},
+ KeyboardLogEvent.LAUNCH_ASSISTANT, KeyEvent.KEYCODE_ASSIST, 0},
+ {"Meta + A -> Launch assistant", new int[]{META_KEY, KeyEvent.KEYCODE_A},
+ KeyboardLogEvent.LAUNCH_ASSISTANT, KeyEvent.KEYCODE_A, META_ON},
+ {"VOICE_ASSIST key -> Launch Voice Assistant",
+ new int[]{KeyEvent.KEYCODE_VOICE_ASSIST},
+ KeyboardLogEvent.LAUNCH_VOICE_ASSISTANT, KeyEvent.KEYCODE_VOICE_ASSIST, 0},
+ {"Meta + I -> Launch System Settings", new int[]{META_KEY, KeyEvent.KEYCODE_I},
+ KeyboardLogEvent.LAUNCH_SYSTEM_SETTINGS, KeyEvent.KEYCODE_I, META_ON},
+ {"Meta + N -> Toggle Notification panel", new int[]{META_KEY, KeyEvent.KEYCODE_N},
+ KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL, KeyEvent.KEYCODE_N, META_ON},
+ {"NOTIFICATION key -> Toggle Notification Panel",
+ new int[]{KeyEvent.KEYCODE_NOTIFICATION},
+ KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL, KeyEvent.KEYCODE_NOTIFICATION,
+ 0},
+ {"Meta + T -> Toggle Taskbar", new int[]{META_KEY, KeyEvent.KEYCODE_T},
+ KeyboardLogEvent.TOGGLE_TASKBAR, KeyEvent.KEYCODE_T, META_ON},
+ {"Meta + Ctrl + S -> Take Screenshot",
+ new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_S},
+ KeyboardLogEvent.TAKE_SCREENSHOT, KeyEvent.KEYCODE_S, META_ON | CTRL_ON},
+ {"Meta + / -> Open Shortcut Helper", new int[]{META_KEY, KeyEvent.KEYCODE_SLASH},
+ KeyboardLogEvent.OPEN_SHORTCUT_HELPER, KeyEvent.KEYCODE_SLASH, META_ON},
+ {"BRIGHTNESS_UP key -> Increase Brightness",
+ new int[]{KeyEvent.KEYCODE_BRIGHTNESS_UP}, KeyboardLogEvent.BRIGHTNESS_UP,
+ KeyEvent.KEYCODE_BRIGHTNESS_UP, 0},
+ {"BRIGHTNESS_DOWN key -> Decrease Brightness",
+ new int[]{KeyEvent.KEYCODE_BRIGHTNESS_DOWN},
+ KeyboardLogEvent.BRIGHTNESS_DOWN, KeyEvent.KEYCODE_BRIGHTNESS_DOWN, 0},
+ {"KEYBOARD_BACKLIGHT_UP key -> Increase Keyboard Backlight",
+ new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP},
+ KeyboardLogEvent.KEYBOARD_BACKLIGHT_UP,
+ KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP, 0},
+ {"KEYBOARD_BACKLIGHT_DOWN key -> Decrease Keyboard Backlight",
+ new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN},
+ KeyboardLogEvent.KEYBOARD_BACKLIGHT_DOWN,
+ KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN, 0},
+ {"KEYBOARD_BACKLIGHT_TOGGLE key -> Toggle Keyboard Backlight",
+ new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE},
+ KeyboardLogEvent.KEYBOARD_BACKLIGHT_TOGGLE,
+ KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE, 0},
+ {"VOLUME_UP key -> Increase Volume", new int[]{KeyEvent.KEYCODE_VOLUME_UP},
+ KeyboardLogEvent.VOLUME_UP, KeyEvent.KEYCODE_VOLUME_UP, 0},
+ {"VOLUME_DOWN key -> Decrease Volume", new int[]{KeyEvent.KEYCODE_VOLUME_DOWN},
+ KeyboardLogEvent.VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_DOWN, 0},
+ {"VOLUME_MUTE key -> Mute Volume", new int[]{KeyEvent.KEYCODE_VOLUME_MUTE},
+ KeyboardLogEvent.VOLUME_MUTE, KeyEvent.KEYCODE_VOLUME_MUTE, 0},
+ {"ALL_APPS key -> Open App Drawer", new int[]{KeyEvent.KEYCODE_ALL_APPS},
+ KeyboardLogEvent.ALL_APPS, KeyEvent.KEYCODE_ALL_APPS, 0},
+ {"SEARCH key -> Launch Search Activity", new int[]{KeyEvent.KEYCODE_SEARCH},
+ KeyboardLogEvent.LAUNCH_SEARCH, KeyEvent.KEYCODE_SEARCH, 0},
+ {"LANGUAGE_SWITCH key -> Switch Keyboard Language",
+ new int[]{KeyEvent.KEYCODE_LANGUAGE_SWITCH},
+ KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_LANGUAGE_SWITCH, 0},
+ {"Meta + Space -> Switch Keyboard Language",
+ new int[]{META_KEY, KeyEvent.KEYCODE_SPACE},
+ KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_SPACE, META_ON},
+ {"Meta + Shift + Space -> Switch Keyboard Language",
+ new int[]{META_KEY, SHIFT_KEY, KeyEvent.KEYCODE_SPACE},
+ KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_SPACE,
+ META_ON | SHIFT_ON},
+ {"META key -> Open App Drawer in Accessibility mode", new int[]{META_KEY},
+ KeyboardLogEvent.ACCESSIBILITY_ALL_APPS, META_KEY, META_ON},
+ {"Meta + Alt -> Toggle CapsLock", new int[]{META_KEY, ALT_KEY},
+ KeyboardLogEvent.TOGGLE_CAPS_LOCK, ALT_KEY, META_ON | ALT_ON},
+ {"Alt + Meta -> Toggle CapsLock", new int[]{ALT_KEY, META_KEY},
+ KeyboardLogEvent.TOGGLE_CAPS_LOCK, META_KEY, META_ON | ALT_ON},
+ {"CAPS_LOCK key -> Toggle CapsLock", new int[]{KeyEvent.KEYCODE_CAPS_LOCK},
+ KeyboardLogEvent.TOGGLE_CAPS_LOCK, KeyEvent.KEYCODE_CAPS_LOCK, 0},
+ {"MUTE key -> Mute System Microphone", new int[]{KeyEvent.KEYCODE_MUTE},
+ KeyboardLogEvent.SYSTEM_MUTE, KeyEvent.KEYCODE_MUTE, 0},
+ {"Meta + Ctrl + DPAD_UP -> Split screen navigation",
+ new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_UP},
+ KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION, KeyEvent.KEYCODE_DPAD_UP,
+ META_ON | CTRL_ON},
+ {"Meta + Ctrl + DPAD_LEFT -> Split screen navigation",
+ new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
+ KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION, KeyEvent.KEYCODE_DPAD_LEFT,
+ META_ON | CTRL_ON},
+ {"Meta + Ctrl + DPAD_RIGHT -> Split screen navigation",
+ new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_RIGHT},
+ KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION, KeyEvent.KEYCODE_DPAD_RIGHT,
+ META_ON | CTRL_ON},
+ {"Shift + Menu -> Trigger Bug Report", new int[]{SHIFT_KEY, KeyEvent.KEYCODE_MENU},
+ KeyboardLogEvent.TRIGGER_BUG_REPORT, KeyEvent.KEYCODE_MENU, SHIFT_ON},
+ {"Meta + L -> Lock Homescreen", new int[]{META_KEY, KeyEvent.KEYCODE_L},
+ KeyboardLogEvent.LOCK_SCREEN, KeyEvent.KEYCODE_L, META_ON},
+ {"Meta + Ctrl + N -> Open Notes", new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_N},
+ KeyboardLogEvent.OPEN_NOTES, KeyEvent.KEYCODE_N, META_ON | CTRL_ON},
+ {"POWER key -> Toggle Power", new int[]{KeyEvent.KEYCODE_POWER},
+ KeyboardLogEvent.TOGGLE_POWER, KeyEvent.KEYCODE_POWER, 0},
+ {"TV_POWER key -> Toggle Power", new int[]{KeyEvent.KEYCODE_TV_POWER},
+ KeyboardLogEvent.TOGGLE_POWER, KeyEvent.KEYCODE_TV_POWER, 0},
+ {"SYSTEM_NAVIGATION_DOWN key -> System Navigation",
+ new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN},
+ KeyboardLogEvent.SYSTEM_NAVIGATION, KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN,
+ 0},
+ {"SYSTEM_NAVIGATION_UP key -> System Navigation",
+ new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP},
+ KeyboardLogEvent.SYSTEM_NAVIGATION, KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP,
+ 0},
+ {"SYSTEM_NAVIGATION_LEFT key -> System Navigation",
+ new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT},
+ KeyboardLogEvent.SYSTEM_NAVIGATION, KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT,
+ 0},
+ {"SYSTEM_NAVIGATION_RIGHT key -> System Navigation",
+ new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT},
+ KeyboardLogEvent.SYSTEM_NAVIGATION,
+ KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT, 0},
+ {"SLEEP key -> System Sleep", new int[]{KeyEvent.KEYCODE_SLEEP},
+ KeyboardLogEvent.SLEEP, KeyEvent.KEYCODE_SLEEP, 0},
+ {"SOFT_SLEEP key -> System Sleep", new int[]{KeyEvent.KEYCODE_SOFT_SLEEP},
+ KeyboardLogEvent.SLEEP, KeyEvent.KEYCODE_SOFT_SLEEP, 0},
+ {"WAKEUP key -> System Wakeup", new int[]{KeyEvent.KEYCODE_WAKEUP},
+ KeyboardLogEvent.WAKEUP, KeyEvent.KEYCODE_WAKEUP, 0},
+ {"MEDIA_PLAY key -> Media Control", new int[]{KeyEvent.KEYCODE_MEDIA_PLAY},
+ KeyboardLogEvent.MEDIA_KEY, KeyEvent.KEYCODE_MEDIA_PLAY, 0},
+ {"MEDIA_PAUSE key -> Media Control", new int[]{KeyEvent.KEYCODE_MEDIA_PAUSE},
+ KeyboardLogEvent.MEDIA_KEY, KeyEvent.KEYCODE_MEDIA_PAUSE, 0},
+ {"MEDIA_PLAY_PAUSE key -> Media Control",
+ new int[]{KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE}, KeyboardLogEvent.MEDIA_KEY,
+ KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, 0},
+ {"Meta + B -> Launch Default Browser", new int[]{META_KEY, KeyEvent.KEYCODE_B},
+ KeyboardLogEvent.LAUNCH_DEFAULT_BROWSER, KeyEvent.KEYCODE_B, META_ON},
+ {"EXPLORER key -> Launch Default Browser", new int[]{KeyEvent.KEYCODE_EXPLORER},
+ KeyboardLogEvent.LAUNCH_DEFAULT_BROWSER, KeyEvent.KEYCODE_EXPLORER, 0},
+ {"Meta + C -> Launch Default Contacts", new int[]{META_KEY, KeyEvent.KEYCODE_C},
+ KeyboardLogEvent.LAUNCH_DEFAULT_CONTACTS, KeyEvent.KEYCODE_C, META_ON},
+ {"CONTACTS key -> Launch Default Contacts", new int[]{KeyEvent.KEYCODE_CONTACTS},
+ KeyboardLogEvent.LAUNCH_DEFAULT_CONTACTS, KeyEvent.KEYCODE_CONTACTS, 0},
+ {"Meta + E -> Launch Default Email", new int[]{META_KEY, KeyEvent.KEYCODE_E},
+ KeyboardLogEvent.LAUNCH_DEFAULT_EMAIL, KeyEvent.KEYCODE_E, META_ON},
+ {"ENVELOPE key -> Launch Default Email", new int[]{KeyEvent.KEYCODE_ENVELOPE},
+ KeyboardLogEvent.LAUNCH_DEFAULT_EMAIL, KeyEvent.KEYCODE_ENVELOPE, 0},
+ {"Meta + K -> Launch Default Calendar", new int[]{META_KEY, KeyEvent.KEYCODE_K},
+ KeyboardLogEvent.LAUNCH_DEFAULT_CALENDAR, KeyEvent.KEYCODE_K, META_ON},
+ {"CALENDAR key -> Launch Default Calendar", new int[]{KeyEvent.KEYCODE_CALENDAR},
+ KeyboardLogEvent.LAUNCH_DEFAULT_CALENDAR, KeyEvent.KEYCODE_CALENDAR, 0},
+ {"Meta + P -> Launch Default Music", new int[]{META_KEY, KeyEvent.KEYCODE_P},
+ KeyboardLogEvent.LAUNCH_DEFAULT_MUSIC, KeyEvent.KEYCODE_P, META_ON},
+ {"MUSIC key -> Launch Default Music", new int[]{KeyEvent.KEYCODE_MUSIC},
+ KeyboardLogEvent.LAUNCH_DEFAULT_MUSIC, KeyEvent.KEYCODE_MUSIC, 0},
+ {"Meta + U -> Launch Default Calculator", new int[]{META_KEY, KeyEvent.KEYCODE_U},
+ KeyboardLogEvent.LAUNCH_DEFAULT_CALCULATOR, KeyEvent.KEYCODE_U, META_ON},
+ {"CALCULATOR key -> Launch Default Calculator",
+ new int[]{KeyEvent.KEYCODE_CALCULATOR},
+ KeyboardLogEvent.LAUNCH_DEFAULT_CALCULATOR, KeyEvent.KEYCODE_CALCULATOR, 0},
+ {"Meta + M -> Launch Default Maps", new int[]{META_KEY, KeyEvent.KEYCODE_M},
+ KeyboardLogEvent.LAUNCH_DEFAULT_MAPS, KeyEvent.KEYCODE_M, META_ON},
+ {"Meta + S -> Launch Default Messaging App",
+ new int[]{META_KEY, KeyEvent.KEYCODE_S},
+ KeyboardLogEvent.LAUNCH_DEFAULT_MESSAGING, KeyEvent.KEYCODE_S, META_ON}};
+ }
+
+ @Before
+ @Override
+ public void setUp() {
+ super.setUp();
+ mPhoneWindowManager.overrideKeyEventSource(VENDOR_ID, PRODUCT_ID);
+ mPhoneWindowManager.overrideLaunchHome();
+ mPhoneWindowManager.overrideSearchKeyBehavior(
+ PhoneWindowManager.SEARCH_BEHAVIOR_TARGET_ACTIVITY);
+ mPhoneWindowManager.overrideEnableBugReportTrigger(true);
+ mPhoneWindowManager.overrideStatusBarManagerInternal();
+ mPhoneWindowManager.overrideStartActivity();
+ mPhoneWindowManager.overrideUserSetupComplete();
+ }
+
+ @Test
+ @Parameters(method = "shortcutTestArguments")
+ public void testShortcuts(String testName, int[] testKeys, KeyboardLogEvent expectedLogEvent,
+ int expectedKey, int expectedModifierState) {
+ sendKeyCombination(testKeys, 0);
+ mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent,
+ expectedKey, expectedModifierState, "Failed while executing " + testName);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 766a88f..1866767 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -26,6 +26,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.description;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
@@ -35,6 +36,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_ASSISTANT;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_GLOBAL_ACTIONS;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_GO_TO_VOICE_ASSIST;
@@ -50,7 +52,6 @@
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mockingDetails;
import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.withSettings;
import android.app.ActivityManagerInternal;
@@ -60,8 +61,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.hardware.SensorPrivacyManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
+import android.hardware.input.InputManager;
import android.media.AudioManagerInternal;
import android.os.Handler;
import android.os.HandlerThread;
@@ -75,14 +78,17 @@
import android.telecom.TelecomManager;
import android.util.FeatureFlagUtils;
import android.view.Display;
+import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.autofill.AutofillManagerInternal;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.GestureLauncherService;
import com.android.server.LocalServices;
import com.android.server.input.InputManagerInternal;
+import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.keyguard.KeyguardServiceDelegate;
@@ -102,6 +108,7 @@
import org.mockito.MockSettings;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import org.mockito.quality.Strictness;
import java.util.function.Supplier;
@@ -117,6 +124,8 @@
@Mock private ActivityManagerInternal mActivityManagerInternal;
@Mock private ActivityTaskManagerInternal mActivityTaskManagerInternal;
@Mock private InputManagerInternal mInputManagerInternal;
+ @Mock private InputManager mInputManager;
+ @Mock private SensorPrivacyManager mSensorPrivacyManager;
@Mock private DreamManagerInternal mDreamManagerInternal;
@Mock private PowerManagerInternal mPowerManagerInternal;
@Mock private DisplayManagerInternal mDisplayManagerInternal;
@@ -186,6 +195,8 @@
// Return mocked services: LocalServices.getService
mMockitoSession = mockitoSession()
.mockStatic(LocalServices.class, spyStubOnly)
+ .mockStatic(FrameworkStatsLog.class)
+ .strictness(Strictness.LENIENT)
.startMocking();
doReturn(mWindowManagerInternal).when(
@@ -213,7 +224,10 @@
doReturn(mAppOpsManager).when(mContext).getSystemService(eq(AppOpsManager.class));
doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class));
+ doReturn(mInputManager).when(mContext).getSystemService(eq(InputManager.class));
doReturn(mPackageManager).when(mContext).getPackageManager();
+ doReturn(mSensorPrivacyManager).when(mContext).getSystemService(
+ eq(SensorPrivacyManager.class));
doReturn(false).when(mPackageManager).hasSystemFeature(any());
try {
doThrow(new PackageManager.NameNotFoundException("test")).when(mPackageManager)
@@ -409,6 +423,31 @@
doReturn(isShowing).when(mKeyguardServiceDelegate).isShowing();
}
+ void overrideKeyEventSource(int vendorId, int productId) {
+ InputDevice device = new InputDevice.Builder().setId(1).setVendorId(vendorId).setProductId(
+ productId).setSources(InputDevice.SOURCE_KEYBOARD).setKeyboardType(
+ InputDevice.KEYBOARD_TYPE_ALPHABETIC).build();
+ doReturn(mInputManager).when(mContext).getSystemService(eq(InputManager.class));
+ doReturn(device).when(mInputManager).getInputDevice(anyInt());
+ }
+
+ void overrideSearchKeyBehavior(int behavior) {
+ mPhoneWindowManager.mSearchKeyBehavior = behavior;
+ }
+
+ void overrideEnableBugReportTrigger(boolean enable) {
+ mPhoneWindowManager.mEnableShiftMenuBugReports = enable;
+ }
+
+ void overrideStartActivity() {
+ doNothing().when(mContext).startActivityAsUser(any(), any());
+ doNothing().when(mContext).startActivityAsUser(any(), any(), any());
+ }
+
+ void overrideUserSetupComplete() {
+ doReturn(true).when(mPhoneWindowManager).isUserSetupComplete();
+ }
+
/**
* Below functions will check the policy behavior could be invoked.
*/
@@ -563,4 +602,12 @@
verify(mContext, after(TEST_SINGLE_KEY_DELAY_MILLIS).never())
.startActivityAsUser(any(Intent.class), any(), any(UserHandle.class));
}
+
+ void assertShortcutLogged(int vendorId, int productId, KeyboardLogEvent logEvent,
+ int expectedKey, int expectedModifierState, String errorMsg) {
+ waitForIdle();
+ verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED,
+ vendorId, productId, logEvent.getIntValue(), new int[]{expectedKey},
+ expectedModifierState), description(errorMsg));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 5c3102d..65e77dc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -182,12 +182,12 @@
@Test
public void testLaunchState() {
- final ToIntFunction<Boolean> launchTemplate = doRelaunch -> {
+ final ToIntFunction<Runnable> launchTemplate = action -> {
clearInvocations(mLaunchObserver);
onActivityLaunched(mTopActivity);
notifyTransitionStarting(mTopActivity);
- if (doRelaunch) {
- mActivityMetricsLogger.notifyActivityRelaunched(mTopActivity);
+ if (action != null) {
+ action.run();
}
final ActivityMetricsLogger.TransitionInfoSnapshot info =
notifyWindowsDrawn(mTopActivity);
@@ -199,21 +199,27 @@
// Assume that the process is started (ActivityBuilder has mocked the returned value of
// ATMS#getProcessController) but the activity has not attached process.
mTopActivity.app = null;
- assertWithMessage("Warm launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+ assertWithMessage("Warm launch").that(launchTemplate.applyAsInt(null))
.isEqualTo(WaitResult.LAUNCH_STATE_WARM);
mTopActivity.app = app;
mNewActivityCreated = false;
- assertWithMessage("Hot launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+ assertWithMessage("Hot launch").that(launchTemplate.applyAsInt(null))
.isEqualTo(WaitResult.LAUNCH_STATE_HOT);
- assertWithMessage("Relaunch").that(launchTemplate.applyAsInt(true /* doRelaunch */))
+ assertWithMessage("Relaunch").that(launchTemplate.applyAsInt(
+ () -> mActivityMetricsLogger.notifyActivityRelaunched(mTopActivity)))
.isEqualTo(WaitResult.LAUNCH_STATE_RELAUNCH);
+ assertWithMessage("Cold launch by restart").that(launchTemplate.applyAsInt(
+ () -> mActivityMetricsLogger.notifyBindApplication(
+ mTopActivity.info.applicationInfo)))
+ .isEqualTo(WaitResult.LAUNCH_STATE_COLD);
+
mTopActivity.app = null;
mNewActivityCreated = true;
doReturn(null).when(mAtm).getProcessController(app.mName, app.mUid);
- assertWithMessage("Cold launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+ assertWithMessage("Cold launch").that(launchTemplate.applyAsInt(null))
.isEqualTo(WaitResult.LAUNCH_STATE_COLD);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
index fe1ea0d..f6f3f03 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
@@ -22,11 +22,17 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.verify;
import android.app.Activity;
import android.app.ActivityManager.RunningTaskInfo;
@@ -41,6 +47,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
+import android.util.Log;
import android.util.Rational;
import android.view.SurfaceControl;
import android.window.TaskOrganizer;
@@ -48,7 +55,10 @@
import androidx.test.filters.MediumTest;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -166,6 +176,122 @@
}
}
+ /**
+ * Tests if any unknown key is being used in the ActivityOptions bundle. If so, please review
+ * if the newly added bundle should be protected with permissions to avoid malicious attacks.
+ *
+ * @see SafeActivityOptionsTest#test_getOptions
+ */
+ @Test
+ public void testActivityOptionsFromBundle() {
+ // Spy on a bundle that is generated from a basic ActivityOptions.
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ Bundle bundle = options.toBundle();
+ spyOn(bundle);
+
+ // Create a new ActivityOptions from the bundle
+ new ActivityOptions(bundle);
+
+ // Verify the keys that are being used.
+ final ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
+ verify(bundle, atLeastOnce()).getString(stringCaptor.capture());
+ verify(bundle, atLeastOnce()).getBoolean(stringCaptor.capture());
+ verify(bundle, atLeastOnce()).getParcelable(stringCaptor.capture(), any());
+ verify(bundle, atLeastOnce()).getInt(stringCaptor.capture(), anyInt());
+ verify(bundle, atLeastOnce()).getBinder(stringCaptor.capture());
+ verify(bundle, atLeastOnce()).getBundle(stringCaptor.capture());
+ final List<String> keys = stringCaptor.getAllValues();
+ final List<String> unknownKeys = new ArrayList<>();
+ for (String key : keys) {
+ switch (key) {
+ case ActivityOptions.KEY_PACKAGE_NAME:
+ case ActivityOptions.KEY_LAUNCH_BOUNDS:
+ case ActivityOptions.KEY_ANIM_TYPE:
+ case ActivityOptions.KEY_ANIM_ENTER_RES_ID:
+ case ActivityOptions.KEY_ANIM_EXIT_RES_ID:
+ case ActivityOptions.KEY_ANIM_IN_PLACE_RES_ID:
+ case ActivityOptions.KEY_ANIM_BACKGROUND_COLOR:
+ case ActivityOptions.KEY_ANIM_THUMBNAIL:
+ case ActivityOptions.KEY_ANIM_START_X:
+ case ActivityOptions.KEY_ANIM_START_Y:
+ case ActivityOptions.KEY_ANIM_WIDTH:
+ case ActivityOptions.KEY_ANIM_HEIGHT:
+ case ActivityOptions.KEY_ANIM_START_LISTENER:
+ case ActivityOptions.KEY_SPLASH_SCREEN_THEME:
+ case ActivityOptions.KEY_LEGACY_PERMISSION_PROMPT_ELIGIBLE:
+ case ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN:
+ case ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN:
+ case ActivityOptions.KEY_TRANSIENT_LAUNCH:
+ case "android:activity.animationFinishedListener":
+ // KEY_ANIMATION_FINISHED_LISTENER
+ case "android:activity.animSpecs": // KEY_ANIM_SPECS
+ case "android:activity.lockTaskMode": // KEY_LOCK_TASK_MODE
+ case "android:activity.shareIdentity": // KEY_SHARE_IDENTITY
+ case "android.activity.launchDisplayId": // KEY_LAUNCH_DISPLAY_ID
+ case "android.activity.callerDisplayId": // KEY_CALLER_DISPLAY_ID
+ case "android.activity.launchTaskDisplayAreaToken":
+ // KEY_LAUNCH_TASK_DISPLAY_AREA_TOKEN
+ case "android.activity.launchTaskDisplayAreaFeatureId":
+ // KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID
+ case "android.activity.windowingMode": // KEY_LAUNCH_WINDOWING_MODE
+ case "android.activity.activityType": // KEY_LAUNCH_ACTIVITY_TYPE
+ case "android.activity.launchTaskId": // KEY_LAUNCH_TASK_ID
+ case "android.activity.disableStarting": // KEY_DISABLE_STARTING_WINDOW
+ case "android.activity.pendingIntentLaunchFlags":
+ // KEY_PENDING_INTENT_LAUNCH_FLAGS
+ case "android.activity.alwaysOnTop": // KEY_TASK_ALWAYS_ON_TOP
+ case "android.activity.taskOverlay": // KEY_TASK_OVERLAY
+ case "android.activity.taskOverlayCanResume": // KEY_TASK_OVERLAY_CAN_RESUME
+ case "android.activity.avoidMoveToFront": // KEY_AVOID_MOVE_TO_FRONT
+ case "android.activity.freezeRecentTasksReordering":
+ // KEY_FREEZE_RECENT_TASKS_REORDERING
+ case "android:activity.disallowEnterPictureInPictureWhileLaunching":
+ // KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING
+ case "android:activity.applyActivityFlagsForBubbles":
+ // KEY_APPLY_ACTIVITY_FLAGS_FOR_BUBBLES
+ case "android:activity.applyMultipleTaskFlagForShortcut":
+ // KEY_APPLY_MULTIPLE_TASK_FLAG_FOR_SHORTCUT
+ case "android:activity.applyNoUserActionFlagForShortcut":
+ // KEY_APPLY_NO_USER_ACTION_FLAG_FOR_SHORTCUT
+ case "android:activity.transitionCompleteListener":
+ // KEY_TRANSITION_COMPLETE_LISTENER
+ case "android:activity.transitionIsReturning": // KEY_TRANSITION_IS_RETURNING
+ case "android:activity.sharedElementNames": // KEY_TRANSITION_SHARED_ELEMENTS
+ case "android:activity.resultData": // KEY_RESULT_DATA
+ case "android:activity.resultCode": // KEY_RESULT_CODE
+ case "android:activity.exitCoordinatorIndex": // KEY_EXIT_COORDINATOR_INDEX
+ case "android.activity.sourceInfo": // KEY_SOURCE_INFO
+ case "android:activity.usageTimeReport": // KEY_USAGE_TIME_REPORT
+ case "android:activity.rotationAnimationHint": // KEY_ROTATION_ANIMATION_HINT
+ case "android:instantapps.installerbundle": // KEY_INSTANT_APP_VERIFICATION_BUNDLE
+ case "android:activity.specsFuture": // KEY_SPECS_FUTURE
+ case "android:activity.remoteAnimationAdapter": // KEY_REMOTE_ANIMATION_ADAPTER
+ case "android:activity.remoteTransition": // KEY_REMOTE_TRANSITION
+ case "android:activity.overrideTaskTransition": // KEY_OVERRIDE_TASK_TRANSITION
+ case "android.activity.removeWithTaskOrganizer": // KEY_REMOVE_WITH_TASK_ORGANIZER
+ case "android.activity.launchTypeBubble": // KEY_LAUNCHED_FROM_BUBBLE
+ case "android.activity.splashScreenStyle": // KEY_SPLASH_SCREEN_STYLE
+ case "android.activity.launchIntoPipParams": // KEY_LAUNCH_INTO_PIP_PARAMS
+ case "android.activity.dismissKeyguard": // KEY_DISMISS_KEYGUARD
+ case "android.activity.pendingIntentCreatorBackgroundActivityStartMode":
+ // KEY_PENDING_INTENT_CREATOR_BACKGROUND_ACTIVITY_START_MODE
+ case "android.activity.launchCookie": // KEY_LAUNCH_COOKIE
+ // Existing keys
+ break;
+ default:
+ unknownKeys.add(key);
+ break;
+ }
+ }
+
+ // Report if any unknown key exists.
+ for (String key : unknownKeys) {
+ Log.e("ActivityOptionsTests", "Unknown key " + key + " is found. "
+ + "Please review if the given bundle should be protected with permissions.");
+ }
+ assertTrue(unknownKeys.isEmpty());
+ }
+
public static class TrampolineActivity extends Activity {
static int sTaskId;
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 3934b02..4034dbc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -62,28 +62,31 @@
@Test
public void testPostLayout() {
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+ statusBar.setBounds(0, 0, 500, 1000);
statusBar.getFrame().set(0, 0, 500, 100);
statusBar.mHasSurface = true;
mProvider.setWindowContainer(statusBar, null, null);
mProvider.updateSourceFrame(statusBar.getFrame());
mProvider.onPostLayout();
assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame());
- assertEquals(Insets.of(0, 100, 0, 0),
- mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
- false /* ignoreVisibility */));
- assertEquals(Insets.of(0, 100, 0, 0),
- mProvider.getSource().calculateVisibleInsets(new Rect(0, 0, 500, 500)));
+ assertEquals(Insets.of(0, 100, 0, 0), mProvider.getInsetsHint());
+
+ // Change the bounds and call onPostLayout. Make sure the insets hint gets updated.
+ statusBar.setBounds(0, 10, 500, 1000);
+ mProvider.onPostLayout();
+ assertEquals(Insets.of(0, 90, 0, 0), mProvider.getInsetsHint());
}
@Test
public void testPostLayout_invisible() {
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+ statusBar.setBounds(0, 0, 500, 1000);
statusBar.getFrame().set(0, 0, 500, 100);
mProvider.setWindowContainer(statusBar, null, null);
mProvider.updateSourceFrame(statusBar.getFrame());
mProvider.onPostLayout();
- assertEquals(Insets.NONE, mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
- false /* ignoreVisibility */));
+ assertTrue(mProvider.getSource().getFrame().isEmpty());
+ assertEquals(Insets.NONE, mProvider.getInsetsHint());
}
@Test
@@ -160,6 +163,36 @@
}
@Test
+ public void testUpdateSourceFrame() {
+ final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+ mProvider.setWindowContainer(statusBar, null, null);
+ statusBar.setBounds(0, 0, 500, 1000);
+
+ mProvider.setServerVisible(true);
+ statusBar.getFrame().set(0, 0, 500, 100);
+ mProvider.updateSourceFrame(statusBar.getFrame());
+ assertEquals(statusBar.getFrame(), mProvider.getSource().getFrame());
+ assertEquals(Insets.of(0, 100, 0, 0), mProvider.getInsetsHint());
+
+ // Only change the source frame but not the visibility.
+ statusBar.getFrame().set(0, 0, 500, 90);
+ mProvider.updateSourceFrame(statusBar.getFrame());
+ assertEquals(statusBar.getFrame(), mProvider.getSource().getFrame());
+ assertEquals(Insets.of(0, 90, 0, 0), mProvider.getInsetsHint());
+
+ mProvider.setServerVisible(false);
+ statusBar.getFrame().set(0, 0, 500, 80);
+ mProvider.updateSourceFrame(statusBar.getFrame());
+ assertTrue(mProvider.getSource().getFrame().isEmpty());
+ assertEquals(Insets.of(0, 90, 0, 0), mProvider.getInsetsHint());
+
+ // Only change the visibility but not the frame.
+ mProvider.setServerVisible(true);
+ assertEquals(statusBar.getFrame(), mProvider.getSource().getFrame());
+ assertEquals(Insets.of(0, 80, 0, 0), mProvider.getInsetsHint());
+ }
+
+ @Test
public void testUpdateSourceFrameForIme() {
final WindowState inputMethod = createWindow(null, TYPE_INPUT_METHOD, "inputMethod");
@@ -184,7 +217,7 @@
}
@Test
- public void testInsetsModified() {
+ public void testSetRequestedVisibleTypes() {
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
statusBar.getFrame().set(0, 0, 500, 100);
@@ -196,7 +229,7 @@
}
@Test
- public void testInsetsModified_noControl() {
+ public void testSetRequestedVisibleTypes_noControl() {
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
statusBar.getFrame().set(0, 0, 500, 100);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java
index 51a7e74..06033c7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java
@@ -20,7 +20,6 @@
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
-import static com.android.server.wm.LetterboxConfigurationPersister.LETTERBOX_CONFIGURATION_FILENAME;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -42,13 +41,26 @@
import java.io.File;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
+import java.util.function.Supplier;
+/**
+ * Tests for the {@link LetterboxConfigurationPersister} class.
+ *
+ * Build/Install/Run:
+ * atest WmTests:LetterboxConfigurationPersisterTest
+ */
@SmallTest
@Presubmit
public class LetterboxConfigurationPersisterTest {
private static final long TIMEOUT = 2000L; // 2 secs
+ private static final int DEFAULT_REACHABILITY_TEST = -1;
+ private static final Supplier<Integer> DEFAULT_REACHABILITY_SUPPLIER_TEST =
+ () -> DEFAULT_REACHABILITY_TEST;
+
+ private static final String LETTERBOX_CONFIGURATION_TEST_FILENAME = "letterbox_config_test";
+
private LetterboxConfigurationPersister mLetterboxConfigurationPersister;
private Context mContext;
private PersisterQueue mPersisterQueue;
@@ -62,7 +74,7 @@
mConfigFolder = mContext.getFilesDir();
mPersisterQueue = new PersisterQueue();
mQueueState = new QueueState();
- mLetterboxConfigurationPersister = new LetterboxConfigurationPersister(mContext,
+ mLetterboxConfigurationPersister = new LetterboxConfigurationPersister(
() -> mContext.getResources().getInteger(
R.integer.config_letterboxDefaultPositionForHorizontalReachability),
() -> mContext.getResources().getInteger(
@@ -72,7 +84,8 @@
() -> mContext.getResources().getInteger(
R.integer.config_letterboxDefaultPositionForTabletopModeReachability
),
- mConfigFolder, mPersisterQueue, mQueueState);
+ mConfigFolder, mPersisterQueue, mQueueState,
+ LETTERBOX_CONFIGURATION_TEST_FILENAME);
mQueueListener = queueEmpty -> mQueueState.onItemAdded();
mPersisterQueue.addListener(mQueueListener);
mLetterboxConfigurationPersister.start();
@@ -127,8 +140,10 @@
public void test_whenUpdatedWithNewValues_valuesAreReadAfterRestart() {
final PersisterQueue firstPersisterQueue = new PersisterQueue();
final LetterboxConfigurationPersister firstPersister = new LetterboxConfigurationPersister(
- mContext, () -> -1, () -> -1, () -> -1, () -> -1, mContext.getFilesDir(),
- firstPersisterQueue, mQueueState);
+ DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+ DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+ mContext.getFilesDir(), firstPersisterQueue, mQueueState,
+ LETTERBOX_CONFIGURATION_TEST_FILENAME);
firstPersister.start();
firstPersister.setLetterboxPositionForHorizontalReachability(false,
LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT);
@@ -138,8 +153,10 @@
stopPersisterSafe(firstPersisterQueue);
final PersisterQueue secondPersisterQueue = new PersisterQueue();
final LetterboxConfigurationPersister secondPersister = new LetterboxConfigurationPersister(
- mContext, () -> -1, () -> -1, () -> -1, () -> -1, mContext.getFilesDir(),
- secondPersisterQueue, mQueueState);
+ DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+ DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+ mContext.getFilesDir(), secondPersisterQueue, mQueueState,
+ LETTERBOX_CONFIGURATION_TEST_FILENAME);
secondPersister.start();
final int newPositionForHorizontalReachability =
secondPersister.getLetterboxPositionForHorizontalReachability(false);
@@ -156,37 +173,46 @@
@Test
public void test_whenUpdatedWithNewValuesAndDeleted_valuesAreDefaults() {
- mLetterboxConfigurationPersister.setLetterboxPositionForHorizontalReachability(false,
+ final PersisterQueue firstPersisterQueue = new PersisterQueue();
+ final LetterboxConfigurationPersister firstPersister = new LetterboxConfigurationPersister(
+ DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+ DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+ mContext.getFilesDir(), firstPersisterQueue, mQueueState,
+ LETTERBOX_CONFIGURATION_TEST_FILENAME);
+ firstPersister.start();
+ firstPersister.setLetterboxPositionForHorizontalReachability(false,
LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT);
- mLetterboxConfigurationPersister.setLetterboxPositionForVerticalReachability(false,
+ firstPersister.setLetterboxPositionForVerticalReachability(false,
LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP);
waitForCompletion(mPersisterQueue);
final int newPositionForHorizontalReachability =
- mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability(
- false);
+ firstPersister.getLetterboxPositionForHorizontalReachability(false);
final int newPositionForVerticalReachability =
- mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability(false);
+ firstPersister.getLetterboxPositionForVerticalReachability(false);
Assert.assertEquals(LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT,
newPositionForHorizontalReachability);
Assert.assertEquals(LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP,
newPositionForVerticalReachability);
- deleteConfiguration(mLetterboxConfigurationPersister, mPersisterQueue);
- waitForCompletion(mPersisterQueue);
+ deleteConfiguration(firstPersister, firstPersisterQueue);
+ waitForCompletion(firstPersisterQueue);
+ stopPersisterSafe(firstPersisterQueue);
+
+ final PersisterQueue secondPersisterQueue = new PersisterQueue();
+ final LetterboxConfigurationPersister secondPersister = new LetterboxConfigurationPersister(
+ DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+ DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+ mContext.getFilesDir(), secondPersisterQueue, mQueueState,
+ LETTERBOX_CONFIGURATION_TEST_FILENAME);
+ secondPersister.start();
final int positionForHorizontalReachability =
- mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability(
- false);
- final int defaultPositionForHorizontalReachability =
- mContext.getResources().getInteger(
- R.integer.config_letterboxDefaultPositionForHorizontalReachability);
- Assert.assertEquals(defaultPositionForHorizontalReachability,
- positionForHorizontalReachability);
+ secondPersister.getLetterboxPositionForHorizontalReachability(false);
final int positionForVerticalReachability =
- mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability(false);
- final int defaultPositionForVerticalReachability =
- mContext.getResources().getInteger(
- R.integer.config_letterboxDefaultPositionForVerticalReachability);
- Assert.assertEquals(defaultPositionForVerticalReachability,
- positionForVerticalReachability);
+ secondPersister.getLetterboxPositionForVerticalReachability(false);
+ Assert.assertEquals(DEFAULT_REACHABILITY_TEST, positionForHorizontalReachability);
+ Assert.assertEquals(DEFAULT_REACHABILITY_TEST, positionForVerticalReachability);
+ deleteConfiguration(secondPersister, secondPersisterQueue);
+ waitForCompletion(secondPersisterQueue);
+ stopPersisterSafe(secondPersisterQueue);
}
private void stopPersisterSafe(PersisterQueue persisterQueue) {
@@ -222,7 +248,7 @@
private void deleteConfiguration(LetterboxConfigurationPersister persister,
PersisterQueue persisterQueue) {
final AtomicFile fileToDelete = new AtomicFile(
- new File(mConfigFolder, LETTERBOX_CONFIGURATION_FILENAME));
+ new File(mConfigFolder, LETTERBOX_CONFIGURATION_TEST_FILENAME));
persisterQueue.addItem(
new DeleteFileCommand(fileToDelete, mQueueState.andThen(
s -> persister.useDefaultValue())), true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index f23e56d..7cb5802 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1303,6 +1303,26 @@
assertTrue(info.supportsMultiWindow);
}
+ @Test
+ public void testRemoveCompatibleRecentTask() {
+ final Task task1 = createTaskBuilder(".Task").setWindowingMode(
+ WINDOWING_MODE_FULLSCREEN).build();
+ mRecentTasks.add(task1);
+ final Task task2 = createTaskBuilder(".Task").setWindowingMode(
+ WINDOWING_MODE_MULTI_WINDOW).build();
+ mRecentTasks.add(task2);
+ assertEquals(2, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
+ true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
+
+ // Set windowing mode and ensure the same fullscreen task that created earlier is removed.
+ task2.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ mRecentTasks.removeCompatibleRecentTask(task2);
+ assertEquals(1, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
+ true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
+ assertEquals(task2.mTaskId, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
+ true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().get(0).taskId);
+ }
+
private TaskSnapshot createSnapshot(Point taskSize, Point bufferSize) {
HardwareBuffer buffer = null;
if (bufferSize != null) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index abf21a5..7eab06a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -656,7 +656,7 @@
topSplitPrimary.getVisibility(null /* starting */));
// Make primary split root transient-hide.
spyOn(splitPrimary.mTransitionController);
- doReturn(true).when(splitPrimary.mTransitionController).isTransientHide(
+ doReturn(true).when(splitPrimary.mTransitionController).isTransientVisible(
organizer.mPrimary);
// The split root and its top become visible.
assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
diff --git a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
index 24e932f..6c48a69 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
@@ -16,17 +16,36 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import android.app.ActivityOptions;
+import android.content.pm.ActivityInfo;
+import android.os.Looper;
import android.platform.test.annotations.Presubmit;
+import android.view.RemoteAnimationAdapter;
+import android.window.RemoteTransition;
import android.window.WindowContainerToken;
import androidx.test.filters.MediumTest;
import org.junit.Test;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
/**
* Build/Install/Run:
@@ -73,4 +92,119 @@
assertSame(clone.getOriginalOptions().getLaunchRootTask(), token);
}
+
+ @Test
+ public void test_getOptions() {
+ // Mock everything necessary
+ MockitoSession mockingSession = mockitoSession()
+ .mockStatic(ActivityTaskManagerService.class)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+ doReturn(PERMISSION_DENIED).when(() -> ActivityTaskManagerService.checkPermission(
+ any(), anyInt(), anyInt()));
+
+ final LockTaskController lockTaskController = mock(LockTaskController.class);
+ doReturn(false).when(lockTaskController).isPackageAllowlisted(anyInt(), any());
+
+ final ActivityTaskManagerService atm = mock(ActivityTaskManagerService.class);
+ doReturn(lockTaskController).when(atm).getLockTaskController();
+
+ final ActivityTaskSupervisor taskSupervisor =
+ new ActivityTaskSupervisor(atm, mock(Looper.class));
+ spyOn(taskSupervisor);
+ doReturn(false).when(taskSupervisor).isCallerAllowedToLaunchOnDisplay(anyInt(),
+ anyInt(), anyInt(), any());
+ doReturn(false).when(taskSupervisor).isCallerAllowedToLaunchOnTaskDisplayArea(anyInt(),
+ anyInt(), any(), any());
+
+ taskSupervisor.mRecentTasks = mock(RecentTasks.class);
+ doReturn(false).when(taskSupervisor.mRecentTasks).isCallerRecents(anyInt());
+
+ // Ensure exceptions are thrown when lack of permissions.
+ ActivityOptions activityOptions = ActivityOptions.makeBasic();
+ try {
+ activityOptions.setLaunchTaskId(100);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setDisableStartingWindow(true);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setTransientLaunch();
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setDismissKeyguard();
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setLaunchActivityType(ACTIVITY_TYPE_STANDARD);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setLaunchedFromBubble(true);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setLaunchDisplayId(DEFAULT_DISPLAY);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setLockTaskEnabled(true);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeCustomTaskAnimation(
+ getInstrumentation().getContext(), 0, 0, null, null, null);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ RemoteAnimationAdapter remoteAnimationAdapter = mock(RemoteAnimationAdapter.class);
+ RemoteTransition remoteTransition = mock(RemoteTransition.class);
+ activityOptions = ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter,
+ remoteTransition);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setRemoteAnimationAdapter(remoteAnimationAdapter);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeRemoteTransition(remoteTransition);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setRemoteTransition(remoteTransition);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ verifySecureExceptionThrown(activityOptions, taskSupervisor,
+ mock(TaskDisplayArea.class));
+ } finally {
+ mockingSession.finishMocking();
+ }
+ }
+
+ private void verifySecureExceptionThrown(ActivityOptions activityOptions,
+ ActivityTaskSupervisor taskSupervisor) {
+ verifySecureExceptionThrown(activityOptions, taskSupervisor, null /* mockTda */);
+ }
+
+ private void verifySecureExceptionThrown(ActivityOptions activityOptions,
+ ActivityTaskSupervisor taskSupervisor, TaskDisplayArea mockTda) {
+ SafeActivityOptions safeActivityOptions = new SafeActivityOptions(activityOptions);
+ if (mockTda != null) {
+ spyOn(safeActivityOptions);
+ doReturn(mockTda).when(safeActivityOptions).getLaunchTaskDisplayArea(any(), any());
+ }
+
+ boolean isExceptionThrow = false;
+ final ActivityInfo aInfo = mock(ActivityInfo.class);
+ try {
+ safeActivityOptions.getOptions(null, aInfo, null, taskSupervisor);
+ } catch (SecurityException ex) {
+ isExceptionThrow = true;
+ }
+ assertTrue(isExceptionThrow);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
index b181213..fb95748 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
@@ -91,7 +91,7 @@
}
@Test
- public void testScreenshotSecureLayers() {
+ public void testScreenshotSecureLayers() throws InterruptedException {
SurfaceControl secureSC = new SurfaceControl.Builder()
.setName("SecureChildSurfaceControl")
.setBLASTLayer()
@@ -197,6 +197,8 @@
private static final long WAIT_TIMEOUT_S = 5;
private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final CountDownLatch mAttachedLatch = new CountDownLatch(1);
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -204,7 +206,16 @@
PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL));
}
- SurfaceControl.Transaction addChildSc(SurfaceControl surfaceControl) {
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mAttachedLatch.countDown();
+ }
+
+ SurfaceControl.Transaction addChildSc(SurfaceControl surfaceControl)
+ throws InterruptedException {
+ assertTrue("Failed to wait for onAttachedToWindow",
+ mAttachedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
CountDownLatch countDownLatch = new CountDownLatch(1);
mHandler.post(() -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 3908947..d5afe3b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -27,6 +27,11 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
@@ -91,9 +96,12 @@
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.ScreenOrientation;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
@@ -2255,6 +2263,169 @@
}
@Test
+ public void testUserOverrideSplitScreenAspectRatioForLandscapeDisplay() {
+ final int displayWidth = 1600;
+ final int displayHeight = 1400;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+
+ float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth);
+
+ testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN);
+ }
+
+ @Test
+ public void testUserOverrideSplitScreenAspectRatioForPortraitDisplay() {
+ final int displayWidth = 1400;
+ final int displayHeight = 1600;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+
+ float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight);
+
+ testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN);
+ }
+
+ @Test
+ public void testUserOverrideDisplaySizeAspectRatioForLandscapeDisplay() {
+ final int displayWidth = 1600;
+ final int displayHeight = 1400;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+
+ float expectedAspectRatio = 1f * displayWidth / displayHeight;
+
+ testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE);
+ }
+
+ @Test
+ public void testUserOverrideDisplaySizeAspectRatioForPortraitDisplay() {
+ final int displayWidth = 1400;
+ final int displayHeight = 1600;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+
+ float expectedAspectRatio = 1f * displayHeight / displayWidth;
+
+ testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE);
+ }
+
+ @Test
+ public void testUserOverride32AspectRatioForPortraitDisplay() {
+ setUpDisplaySizeWithApp(/* dw */ 1400, /* dh */ 1600);
+ testUserOverrideAspectRatio(3 / 2f, USER_MIN_ASPECT_RATIO_3_2);
+ }
+
+ @Test
+ public void testUserOverride32AspectRatioForLandscapeDisplay() {
+ setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400);
+ testUserOverrideAspectRatio(3 / 2f, USER_MIN_ASPECT_RATIO_3_2);
+ }
+
+ @Test
+ public void testUserOverride43AspectRatioForPortraitDisplay() {
+ setUpDisplaySizeWithApp(/* dw */ 1400, /* dh */ 1600);
+ testUserOverrideAspectRatio(4 / 3f, USER_MIN_ASPECT_RATIO_4_3);
+ }
+
+ @Test
+ public void testUserOverride43AspectRatioForLandscapeDisplay() {
+ setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400);
+ testUserOverrideAspectRatio(4 / 3f, USER_MIN_ASPECT_RATIO_4_3);
+ }
+
+ @Test
+ public void testUserOverride169AspectRatioForPortraitDisplay() {
+ setUpDisplaySizeWithApp(/* dw */ 1800, /* dh */ 1500);
+ testUserOverrideAspectRatio(16 / 9f, USER_MIN_ASPECT_RATIO_16_9);
+ }
+
+ @Test
+ public void testUserOverride169AspectRatioForLandscapeDisplay() {
+ setUpDisplaySizeWithApp(/* dw */ 1500, /* dh */ 1800);
+ testUserOverrideAspectRatio(16 / 9f, USER_MIN_ASPECT_RATIO_16_9);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+ public void testUserOverrideAspectRatioOverSystemOverride() {
+ setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400);
+
+ testUserOverrideAspectRatio(false,
+ SCREEN_ORIENTATION_PORTRAIT,
+ 3 / 2f,
+ USER_MIN_ASPECT_RATIO_3_2,
+ true);
+ }
+
+ @Test
+ public void testUserOverrideAspectRatioNotEnabled() {
+ setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400);
+
+ // App aspect ratio doesn't change
+ testUserOverrideAspectRatio(false,
+ SCREEN_ORIENTATION_PORTRAIT,
+ 1f * 1600 / 1400,
+ USER_MIN_ASPECT_RATIO_3_2,
+ false);
+ }
+
+ private void testUserOverrideAspectRatio(float expectedAspectRatio,
+ @PackageManager.UserMinAspectRatio int aspectRatio) {
+ testUserOverrideAspectRatio(true,
+ SCREEN_ORIENTATION_PORTRAIT,
+ expectedAspectRatio,
+ aspectRatio,
+ true);
+
+ testUserOverrideAspectRatio(false,
+ SCREEN_ORIENTATION_PORTRAIT,
+ expectedAspectRatio,
+ aspectRatio,
+ true);
+
+ testUserOverrideAspectRatio(true,
+ SCREEN_ORIENTATION_LANDSCAPE,
+ expectedAspectRatio,
+ aspectRatio,
+ true);
+
+ testUserOverrideAspectRatio(false,
+ SCREEN_ORIENTATION_LANDSCAPE,
+ expectedAspectRatio,
+ aspectRatio,
+ true);
+ }
+
+ private void testUserOverrideAspectRatio(boolean isUnresizable, int screenOrientation,
+ float expectedAspectRatio, @PackageManager.UserMinAspectRatio int aspectRatio,
+ boolean enabled) {
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+ activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ activity.mWmService.mLetterboxConfiguration
+ .setUserAppAspectRatioSettingsOverrideEnabled(enabled);
+ // Set user aspect ratio override
+ final IPackageManager pm = mAtm.getPackageManager();
+ try {
+ doReturn(aspectRatio).when(pm)
+ .getUserMinAspectRatio(activity.packageName, activity.mUserId);
+ } catch (RemoteException ignored) {
+ }
+
+ prepareLimitedBounds(activity, screenOrientation, isUnresizable);
+
+ final Rect afterBounds = activity.getBounds();
+ final int width = afterBounds.width();
+ final int height = afterBounds.height();
+ final float afterAspectRatio =
+ (float) Math.max(width, height) / (float) Math.min(width, height);
+
+ assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ }
+
+ @Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
public void testOverrideSplitScreenAspectRatioForUnresizablePortraitApps() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index ffecafb..5154d17 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1488,6 +1488,47 @@
}
@Test
+ public void testIsTransientVisible() {
+ final ActivityRecord appB = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setVisible(false).build();
+ final ActivityRecord recent = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setVisible(false).build();
+ final ActivityRecord appA = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task taskA = appA.getTask();
+ final Task taskB = appB.getTask();
+ final Task taskRecent = recent.getTask();
+ registerTestTransitionPlayer();
+ final TransitionController controller = mRootWindowContainer.mTransitionController;
+ final Transition transition = createTestTransition(TRANSIT_OPEN, controller);
+ controller.moveToCollecting(transition);
+ transition.collect(recent);
+ transition.collect(taskA);
+ transition.setTransientLaunch(recent, taskA);
+ taskRecent.moveToFront("move-recent-to-front");
+
+ // During collecting and playing, the recent is on top so it is visible naturally.
+ // While B needs isTransientVisible to keep visibility because it is occluded by recents.
+ assertFalse(controller.isTransientVisible(taskB));
+ assertTrue(controller.isTransientVisible(taskA));
+ assertFalse(controller.isTransientVisible(taskRecent));
+ // Switch to playing state.
+ transition.onTransactionReady(transition.getSyncId(), mMockT);
+ assertTrue(controller.isTransientVisible(taskA));
+
+ // Switch to another task. For example, use gesture navigation to switch tasks.
+ taskB.moveToFront("move-b-to-front");
+ // The previous app (taskA) should be paused first so it loses transient visible. Because
+ // visually it is taskA -> taskB, the pause -> resume order should be the same.
+ assertFalse(controller.isTransientVisible(taskA));
+ // Keep the recent visible so there won't be 2 activities pausing at the same time. It is
+ // to avoid the latency to resume the current top, i.e. appB.
+ assertTrue(controller.isTransientVisible(taskRecent));
+ // The recent is paused after the transient transition is finished.
+ controller.finishTransition(transition);
+ assertFalse(controller.isTransientVisible(taskRecent));
+ }
+
+ @Test
public void testNotReadyPushPop() {
final TransitionController controller = new TestTransitionController(mAtm);
controller.setSyncEngine(mWm.mSyncEngine);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 248cc26..ccc4ac2 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -27,6 +27,7 @@
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__SERVICE_CRASH;
+import android.app.AppOpsManager;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.ChangeId;
@@ -548,13 +549,15 @@
static final class SoundTriggerCallback extends IRecognitionStatusCallback.Stub {
private final HotwordDetectionConnection mHotwordDetectionConnection;
private final IHotwordRecognitionStatusCallback mExternalCallback;
- private final int mVoiceInteractionServiceUid;
+ private final Identity mVoiceInteractorIdentity;
+ private final Context mContext;
- SoundTriggerCallback(IHotwordRecognitionStatusCallback callback,
- HotwordDetectionConnection connection, int uid) {
+ SoundTriggerCallback(Context context, IHotwordRecognitionStatusCallback callback,
+ HotwordDetectionConnection connection, Identity voiceInteractorIdentity) {
+ mContext = context;
mHotwordDetectionConnection = connection;
mExternalCallback = callback;
- mVoiceInteractionServiceUid = uid;
+ mVoiceInteractorIdentity = voiceInteractorIdentity;
}
@Override
@@ -568,15 +571,30 @@
HotwordMetricsLogger.writeKeyphraseTriggerEvent(
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP,
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER,
- mVoiceInteractionServiceUid);
+ mVoiceInteractorIdentity.uid);
mHotwordDetectionConnection.detectFromDspSource(
recognitionEvent, mExternalCallback);
} else {
- HotwordMetricsLogger.writeKeyphraseTriggerEvent(
- HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR,
- HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER,
- mVoiceInteractionServiceUid);
- mExternalCallback.onKeyphraseDetected(recognitionEvent, null);
+ // We have to attribute ops here, since we configure all st clients as trusted to
+ // enable a partial exemption.
+ // TODO (b/292012931) remove once trusted uniformly required.
+ int result = mContext.getSystemService(AppOpsManager.class)
+ .noteOpNoThrow(AppOpsManager.OP_RECORD_AUDIO_HOTWORD,
+ mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
+ mVoiceInteractorIdentity.attributionTag,
+ "Non-HDS keyphrase recognition to VoiceInteractionService");
+
+ if (result != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "onKeyphraseDetected suppressed, permission check returned: "
+ + result);
+ mExternalCallback.onRecognitionPaused();
+ } else {
+ HotwordMetricsLogger.writeKeyphraseTriggerEvent(
+ HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR,
+ HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER,
+ mVoiceInteractorIdentity.uid);
+ mExternalCallback.onKeyphraseDetected(recognitionEvent, null);
+ }
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 605af03..3502a3f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -104,6 +104,7 @@
import com.android.server.UiThread;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
+import com.android.server.policy.AppOpsPolicy;
import com.android.server.utils.Slogf;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -336,6 +337,9 @@
/** The start value of showSessionId */
private static final int SHOW_SESSION_START_ID = 0;
+ private final boolean IS_HDS_REQUIRED = AppOpsPolicy.isHotwordDetectionServiceRequired(
+ mContext.getPackageManager());
+
@GuardedBy("this")
private int mShowSessionId = SHOW_SESSION_START_ID;
@@ -393,8 +397,14 @@
}
try (SafeCloseable ignored = PermissionUtil.establishIdentityDirect(
originatorIdentity)) {
+ if (!IS_HDS_REQUIRED) {
+ // For devices which still have hotword exemption, any client (not just HDS
+ // clients) are trusted.
+ // TODO (b/292012931) remove once trusted uniformly required.
+ forHotwordDetectionService = true;
+ }
return new SoundTriggerSession(mSoundTriggerInternal.attach(client,
- moduleProperties, forHotwordDetectionService));
+ moduleProperties, forHotwordDetectionService), originatorIdentity);
}
}
@@ -1674,10 +1684,13 @@
final SoundTriggerInternal.Session mSession;
private IHotwordRecognitionStatusCallback mSessionExternalCallback;
private IRecognitionStatusCallback mSessionInternalCallback;
+ private final Identity mVoiceInteractorIdentity;
SoundTriggerSession(
- SoundTriggerInternal.Session session) {
+ SoundTriggerInternal.Session session,
+ Identity voiceInteractorIdentity) {
mSession = session;
+ mVoiceInteractorIdentity = voiceInteractorIdentity;
}
@Override
@@ -1731,7 +1744,8 @@
if (mSessionExternalCallback == null
|| mSessionInternalCallback == null
|| callback.asBinder() != mSessionExternalCallback.asBinder()) {
- mSessionInternalCallback = createSoundTriggerCallbackLocked(callback);
+ mSessionInternalCallback = createSoundTriggerCallbackLocked(callback,
+ mVoiceInteractorIdentity);
mSessionExternalCallback = callback;
}
}
@@ -1752,7 +1766,8 @@
if (mSessionExternalCallback == null
|| mSessionInternalCallback == null
|| callback.asBinder() != mSessionExternalCallback.asBinder()) {
- soundTriggerCallback = createSoundTriggerCallbackLocked(callback);
+ soundTriggerCallback = createSoundTriggerCallbackLocked(callback,
+ mVoiceInteractorIdentity);
Slog.w(TAG, "stopRecognition() called with a different callback than"
+ "startRecognition()");
} else {
@@ -2090,6 +2105,7 @@
pw.println(" mTemporarilyDisabled: " + mTemporarilyDisabled);
pw.println(" mCurUser: " + mCurUser);
pw.println(" mCurUserSupported: " + mCurUserSupported);
+ pw.println(" mIsHdsRequired: " + IS_HDS_REQUIRED);
dumpSupportedUsers(pw, " ");
mDbHelper.dump(pw);
if (mImpl == null) {
@@ -2165,11 +2181,13 @@
}
private IRecognitionStatusCallback createSoundTriggerCallbackLocked(
- IHotwordRecognitionStatusCallback callback) {
+ IHotwordRecognitionStatusCallback callback,
+ Identity voiceInteractorIdentity) {
if (mImpl == null) {
return null;
}
- return mImpl.createSoundTriggerCallbackLocked(callback);
+ return mImpl.createSoundTriggerCallbackLocked(mContext, callback,
+ voiceInteractorIdentity);
}
class RoleObserver implements OnRoleHoldersChangedListener {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 0ad86c1..5d88a65 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -877,12 +877,13 @@
}
public IRecognitionStatusCallback createSoundTriggerCallbackLocked(
- IHotwordRecognitionStatusCallback callback) {
+ Context context, IHotwordRecognitionStatusCallback callback,
+ Identity voiceInteractorIdentity) {
if (DEBUG) {
Slog.d(TAG, "createSoundTriggerCallbackLocked");
}
- return new HotwordDetectionConnection.SoundTriggerCallback(callback,
- mHotwordDetectionConnection, mInfo.getServiceInfo().applicationInfo.uid);
+ return new HotwordDetectionConnection.SoundTriggerCallback(context, callback,
+ mHotwordDetectionConnection, voiceInteractorIdentity);
}
private static ServiceInfo getServiceInfoLocked(@NonNull ComponentName componentName,
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index efcbd31..f1ee76e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5268,7 +5268,11 @@
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getMergedImsisFromGroup(getSubId(), getOpPackageName());
+ String[] mergedImsisFromGroup =
+ telephony.getMergedImsisFromGroup(getSubId(), getOpPackageName());
+ if (mergedImsisFromGroup != null) {
+ return mergedImsisFromGroup;
+ }
}
} catch (RemoteException ex) {
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
index 0f406fd..845e649 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
@@ -70,6 +70,9 @@
@Ignore("Not applicable to this CUJ.") override fun navBarWindowIsVisibleAtStartAndEnd() {}
+ @FlakyTest(bugId = 291575593)
+ override fun entireScreenCovered() {}
+
@Ignore("Not applicable to this CUJ.") override fun statusBarWindowIsAlwaysVisible() {}
@Ignore("Not applicable to this CUJ.") override fun statusBarLayerPositionAtStartAndEnd() {}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
index 144a7312..6722cc8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
@@ -62,14 +62,6 @@
}
}
- /** Assert the background animation layer is never visible during bounds change transition. */
- @Presubmit
- @Test
- fun backgroundLayerNeverVisible() {
- val backgroundColorLayer = ComponentNameMatcher("", "Animation Background")
- flicker.assertLayers { isInvisible(backgroundColorLayer) }
- }
-
/** Trampoline activity should finish itself before the end of this test. */
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
new file mode 100644
index 0000000..0417f9d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.activityembedding
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.Rect
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher.Companion.TRANSITION_SNAPSHOT
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching a secondary Activity into Picture-In-Picture mode.
+ *
+ * Setup: Start from a split A|B.
+ * Transition: B enters PIP, observe the window shrink to the bottom right corner on screen.
+ *
+ * To run this test: `atest FlickerTests:SecondaryActivityEnterPipTest`
+ *
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class SecondaryActivityEnterPipTest (flicker: LegacyFlickerTest) :
+ ActivityEmbeddingTestBase(flicker) {
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+ testApp.launchViaIntent(wmHelper)
+ testApp.launchSecondaryActivity(wmHelper)
+ startDisplayBounds =
+ wmHelper.currentState.layerState.physicalDisplayBounds
+ ?: error("Can't get display bounds")
+ }
+ transitions {
+ testApp.secondaryActivityEnterPip(wmHelper)
+ }
+ teardown {
+ tapl.goHome()
+ testApp.exit(wmHelper)
+ }
+ }
+
+ /**
+ * Main and secondary activity start from a split each taking half of the screen.
+ */
+ @Presubmit
+ @Test
+ fun layersStartFromEqualSplit() {
+ flicker.assertLayersStart {
+ val leftLayerRegion =
+ visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ val rightLayerRegion =
+ visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ // Compare dimensions of two splits, given we're using default split attributes,
+ // both activities take up the same visible size on the display.
+ check { "height" }
+ .that(leftLayerRegion.region.height).isEqual(rightLayerRegion.region.height)
+ check { "width" }
+ .that(leftLayerRegion.region.width).isEqual(rightLayerRegion.region.width)
+ leftLayerRegion.notOverlaps(rightLayerRegion.region)
+ leftLayerRegion.plus(rightLayerRegion.region).coversExactly(startDisplayBounds)
+ }
+ flicker.assertLayersEnd {
+ visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Main Activity is visible throughout the transition and becomes fullscreen.
+ */
+ @Presubmit
+ @Test
+ fun mainActivityWindowBecomesFullScreen() {
+ flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) }
+ flicker.assertWmEnd {
+ visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Main Activity is visible throughout the transition and becomes fullscreen.
+ */
+ @Presubmit
+ @Test
+ fun mainActivityLayerBecomesFullScreen() {
+ flicker.assertLayers {
+ isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .then()
+ .isVisible(TRANSITION_SNAPSHOT)
+ .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .then()
+ .isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ }
+ flicker.assertLayersEnd {
+ visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Secondary Activity is visible throughout the transition and shrinks to the bottom right
+ * corner.
+ */
+ @Presubmit
+ @Test
+ fun secondaryWindowShrinks() {
+ flicker.assertWm {
+ isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ }
+ flicker.assertWmEnd {
+ val pipWindowRegion =
+ visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ check{"height"}
+ .that(pipWindowRegion.region.height)
+ .isLower(startDisplayBounds.height / 2)
+ check{"width"}
+ .that(pipWindowRegion.region.width).isLower(startDisplayBounds.width)
+ }
+ }
+
+ /**
+ * During the transition Secondary Activity shrinks to the bottom right corner.
+ */
+ @Presubmit
+ @Test
+ fun secondaryLayerShrinks() {
+ flicker.assertLayers {
+ val pipLayerList = layers {
+ ComponentNameMatcher.PIP_CONTENT_OVERLAY.layerMatchesAnyOf(it) && it.isVisible
+ }
+ pipLayerList.zipWithNext { previous, current ->
+ // TODO(b/290987990): Add checks for visibleRegion.
+ current.screenBounds.isToTheRightBottom(previous.screenBounds.region, 3)
+ current.screenBounds.notBiggerThan(previous.screenBounds.region)
+ }
+ }
+ flicker.assertLayersEnd {
+ val pipRegion = visibleRegion(
+ ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ check { "height" }
+ .that(pipRegion.region.height)
+ .isLower(startDisplayBounds.height / 2)
+ check { "width" }
+ .that(pipRegion.region.width).isLower(startDisplayBounds.width)
+ }
+ }
+
+ companion object {
+ /** {@inheritDoc} */
+ private var startDisplayBounds = Rect.EMPTY
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
index a72ec1e..2d3bc2d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
@@ -113,6 +113,21 @@
.waitForAndVerify()
}
+ fun secondaryActivityEnterPip(wmHelper: WindowManagerStateHelper) {
+ val pipButton =
+ uiDevice.wait(
+ Until.findObject(By.res(getPackage(), "secondary_enter_pip_button")),
+ FIND_TIMEOUT
+ )
+ require(pipButton != null) { "Can't find enter pip button on screen." }
+ pipButton.click()
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withPipShown()
+ .waitForAndVerify()
+ }
+
/**
* Clicks the button to launch a secondary activity with alwaysExpand enabled, which will launch
* a fullscreen window on top of the visible region.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index 24e231c..82de646 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -383,8 +383,11 @@
it.wmState.visibleWindows.firstOrNull { window ->
this.windowMatchesAnyOf(window)
}
- ?: return@add false
+ Log.d(TAG, "window " + pipAppWindow)
+ if (pipAppWindow == null) return@add false
val pipRegion = pipAppWindow.frameRegion
+ Log.d(TAG, "region " + pipRegion +
+ " covers " + windowRect.coversMoreThan(pipRegion))
return@add windowRect.coversMoreThan(pipRegion)
}
.waitForAndVerify()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
index c1db423..ea7c7c4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
@@ -25,6 +25,7 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
import com.android.server.wm.flicker.helpers.setRotation
@@ -78,6 +79,7 @@
flicker.snapshotStartingWindowLayerCoversExactlyOnApp(imeTestApp)
}
+ @FlakyTest(bugId = 290767483)
@Postsubmit
@Test
fun imeLayerAlphaOneAfterSnapshotStartingWindowRemoval() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index 7a16060..94b090f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -200,7 +200,7 @@
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
- @FlakyTest(bugId = 251217585)
+ @FlakyTest(bugId = 285980483)
@Test
override fun focusChanges() {
super.focusChanges()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 4164c0d..df9780e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -21,6 +21,7 @@
import android.content.res.Resources
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.flicker.subject.layers.LayersTraceSubject.Companion.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.ComponentNameMatcher.Companion.SPLASH_SCREEN
import android.tools.common.traces.component.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER
@@ -190,6 +191,16 @@
}
}
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + listOf(launchNewTaskApp)
+ )
+ }
+ }
+
companion object {
private fun getWallpaperPackage(instrumentation: Instrumentation): IComponentMatcher {
val wallpaperManager = WallpaperManager.getInstance(instrumentation.targetContext)
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 68ae806..ff9799a 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -102,6 +102,24 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".LaunchTransparentActivity"
+ android:resizeableActivity="false"
+ android:screenOrientation="portrait"
+ android:theme="@android:style/Theme"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchTransparentActivity"
+ android:label="LaunchTransparentActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".TransparentActivity"
+ android:theme="@style/TransparentTheme"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.TransparentActivity"
+ android:label="TransparentActivity"
+ android:exported="false">
+ </activity>
<activity android:name=".LaunchNewActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewActivity"
android:theme="@style/CutoutShortEdges"
@@ -206,6 +224,7 @@
android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:supportsPictureInPicture="true"
android:exported="false"/>
<activity
android:name=".ActivityEmbeddingThirdActivity"
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
index 6731446..135140a 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
@@ -35,4 +35,10 @@
android:onClick="launchThirdActivity"
android:text="Launch a third activity" />
+ <Button
+ android:id="@+id/secondary_enter_pip_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:text="Enter pip" />
+
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml
similarity index 72%
rename from packages/SystemUI/res/values/dimens_tv.xml
rename to tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml
index 3dbd990..0730ded 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2020 The Android Open Source Project
+ ~ Copyright (C) 2023 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,7 +14,8 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<resources>
- <dimen name="tv_notification_panel_width">360dp</dimen>
- <dimen name="tv_notification_blur_radius">31dp</dimen>
-</resources>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+</FrameLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml
new file mode 100644
index 0000000..ff4ead9
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:color/black">
+
+ <Button
+ android:id="@+id/button_launch_transparent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:text="Launch Transparent" />
+ <Button
+ android:id="@+id/button_request_permission"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:text="Request Permission" />
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
index 1d21fd5..e51ed29 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -43,6 +43,13 @@
<item name="android:windowSoftInputMode">stateUnchanged</item>
</style>
+ <style name="TransparentTheme" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ </style>
+
<style name="no_starting_window" parent="@android:style/Theme.DeviceDefault">
<item name="android:windowDisablePreview">true</item>
</style>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
index dc21027..ee087ef 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.content.Intent;
+import android.app.PictureInPictureParams;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
@@ -40,6 +41,16 @@
finish();
}
});
+ findViewById(R.id.secondary_enter_pip_button).setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ PictureInPictureParams.Builder picInPicParamsBuilder =
+ new PictureInPictureParams.Builder();
+ enterPictureInPictureMode(picInPicParamsBuilder.build());
+ }
+ }
+ );
}
public void launchThirdActivity(View view) {
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 95c86ac..2795a6c 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -73,6 +73,18 @@
FLICKER_APP_PACKAGE + ".NonResizeablePortraitActivity");
}
+ public static class TransparentActivity {
+ public static final String LABEL = "TransparentActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".TransparentActivity");
+ }
+
+ public static class LaunchTransparentActivity {
+ public static final String LABEL = "LaunchTransparentActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".LaunchTransparentActivity");
+ }
+
public static class DialogThemedActivity {
public static final String LABEL = "DialogThemedActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchTransparentActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchTransparentActivity.java
new file mode 100644
index 0000000..7c161fd
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchTransparentActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class LaunchTransparentActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.activity_transparent_launch);
+ findViewById(R.id.button_launch_transparent)
+ .setOnClickListener(v -> launchTransparentActivity());
+ }
+
+ private void launchTransparentActivity() {
+ startActivity(new Intent(this, TransparentActivity.class));
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/TransparentActivity.java
similarity index 60%
rename from packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
rename to tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/TransparentActivity.java
index 23f37ec..1bac8bd 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/TransparentActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,15 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.tv;
+package com.android.server.wm.flicker.testapp;
-import com.android.systemui.dagger.GlobalRootComponent;
+import android.app.Activity;
+import android.os.Bundle;
-import dagger.Binds;
-import dagger.Module;
+public class TransparentActivity extends Activity {
-@Module
-interface TvSystemUIBinder {
- @Binds
- GlobalRootComponent bindGlobalRootComponent(TvGlobalRootComponent globalRootComponent);
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.activity_transparent);
+ }
}
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
index 44da69c..5719273 100644
--- a/tests/Input/src/com/android/test/input/AnrTest.kt
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -100,6 +100,7 @@
private fun clickCloseAppOnAnrDialog() {
// Find anr dialog and kill app
+ val timestamp = System.currentTimeMillis()
val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
val closeAppButton: UiObject2? =
uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000)
@@ -107,7 +108,6 @@
fail("Could not find anr dialog")
return
}
- val initialReasons = getExitReasons()
closeAppButton.click()
/**
* We must wait for the app to be fully closed before exiting this test. This is because
@@ -116,7 +116,7 @@
* the killing logic will apply to the newly launched 'am start' instance, and the second
* test will fail because the unresponsive activity will never be launched.
*/
- waitForNewExitReason(initialReasons[0].timestamp)
+ waitForNewExitReasonAfter(timestamp)
}
private fun clickWaitOnAnrDialog() {
@@ -140,12 +140,13 @@
return infos
}
- private fun waitForNewExitReason(previousExitTimestamp: Long) {
+ private fun waitForNewExitReasonAfter(timestamp: Long) {
PollingCheck.waitFor {
- getExitReasons()[0].timestamp > previousExitTimestamp
+ val reasons = getExitReasons()
+ !reasons.isEmpty() && reasons[0].timestamp >= timestamp
}
val reasons = getExitReasons()
- assertTrue(reasons[0].timestamp > previousExitTimestamp)
+ assertTrue(reasons[0].timestamp > timestamp)
assertEquals(ApplicationExitInfo.REASON_ANR, reasons[0].reason)
}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java
index 0c267b2..320daee 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java
@@ -17,8 +17,9 @@
package com.android.inputmethod.stresstest;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
+import static com.android.compatibility.common.util.SystemUtil.eventually;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.REQUEST_FOCUS_ON_CREATE;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.TestActivity.createIntent;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.callOnMainSync;
@@ -26,11 +27,16 @@
import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsHidden;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsShown;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import android.content.Intent;
import android.platform.test.annotations.RootPermissionTest;
import android.platform.test.rule.UnlockScreenRule;
+import android.support.test.uiautomator.UiDevice;
import android.widget.EditText;
+import androidx.test.platform.app.InstrumentationRegistry;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,6 +45,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.TimeUnit;
/**
* Test IME visibility by using system default IME to ensure the behavior is consistent
@@ -59,8 +66,12 @@
public ScreenCaptureRule mScreenCaptureRule =
new ScreenCaptureRule("/sdcard/InputMethodStressTest");
+ private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(3);
+
private static final int NUM_TEST_ITERATIONS = 10;
+ private final boolean mIsPortrait;
+
@Parameterized.Parameters(name = "isPortrait={0}")
public static List<Boolean> isPortraitCases() {
// Test in both portrait and landscape mode.
@@ -68,6 +79,7 @@
}
public DefaultImeVisibilityTest(boolean isPortrait) {
+ mIsPortrait = isPortrait;
mImeStressTestRule.setIsPortrait(isPortrait);
}
@@ -75,14 +87,26 @@
public void showHideDefaultIme() {
Intent intent =
createIntent(
- 0x0, /* No window focus flags */
- SOFT_INPUT_STATE_UNSPECIFIED | SOFT_INPUT_ADJUST_RESIZE,
+ 0x0 /* No window focus flags */,
+ SOFT_INPUT_STATE_HIDDEN | SOFT_INPUT_ADJUST_RESIZE,
Collections.singletonList(REQUEST_FOCUS_ON_CREATE));
ImeStressTestUtil.TestActivity activity = ImeStressTestUtil.TestActivity.start(intent);
EditText editText = activity.getEditText();
+
+ UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ eventually(
+ () ->
+ assertWithMessage("Display rotation should be updated.")
+ .that(uiDevice.getDisplayRotation())
+ .isEqualTo(mIsPortrait ? 0 : 1),
+ TIMEOUT);
+
for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
- callOnMainSync(activity::showImeWithInputMethodManager);
+ // TODO(b/291752364): Remove the explicit focus request once the issue with view focus
+ // change between fullscreen IME and actual editText is fixed.
+ callOnMainSync(editText::requestFocus);
verifyWindowAndViewFocus(editText, true, true);
+ callOnMainSync(activity::showImeWithInputMethodManager);
waitOnMainUntilImeIsShown(editText);
callOnMainSync(activity::hideImeWithInputMethodManager);
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java
index 12104b2..c746321 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java
@@ -20,9 +20,9 @@
import android.os.RemoteException;
import android.support.test.uiautomator.UiDevice;
+import androidx.annotation.NonNull;
import androidx.test.platform.app.InstrumentationRegistry;
-import org.checkerframework.checker.nullness.qual.NonNull;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
@@ -35,8 +35,6 @@
public class ImeStressTestRule extends TestWatcher {
private static final String LOCK_SCREEN_OFF_COMMAND = "locksettings set-disabled true";
private static final String LOCK_SCREEN_ON_COMMAND = "locksettings set-disabled false";
- private static final String SET_PORTRAIT_MODE_COMMAND = "settings put system user_rotation 0";
- private static final String SET_LANDSCAPE_MODE_COMMAND = "settings put system user_rotation 1";
private static final String SIMPLE_IME_ID =
"com.android.apps.inputmethod.simpleime/.SimpleInputMethodService";
private static final String ENABLE_IME_COMMAND = "ime enable " + SIMPLE_IME_ID;
@@ -44,8 +42,10 @@
private static final String DISABLE_IME_COMMAND = "ime disable " + SIMPLE_IME_ID;
private static final String RESET_IME_COMMAND = "ime reset";
- @NonNull private final Instrumentation mInstrumentation;
- @NonNull private final UiDevice mUiDevice;
+ @NonNull
+ private final Instrumentation mInstrumentation;
+ @NonNull
+ private final UiDevice mUiDevice;
// Whether the screen orientation is set to portrait.
private boolean mIsPortrait;
// Whether to use a simple test Ime or system default Ime for test.
@@ -105,12 +105,13 @@
private void setOrientation() {
try {
mUiDevice.freezeRotation();
- executeShellCommand(
- mIsPortrait ? SET_PORTRAIT_MODE_COMMAND : SET_LANDSCAPE_MODE_COMMAND);
- } catch (IOException e) {
- throw new RuntimeException("Could not set screen orientation.", e);
+ if (mIsPortrait) {
+ mUiDevice.setOrientationNatural();
+ } else {
+ mUiDevice.setOrientationLeft();
+ }
} catch (RemoteException e) {
- throw new RuntimeException("Could not freeze rotation.", e);
+ throw new RuntimeException("Could not freeze rotation or set screen orientation.", e);
}
}
@@ -147,7 +148,8 @@
}
}
- private @NonNull String executeShellCommand(@NonNull String cmd) throws IOException {
+ @NonNull
+ private String executeShellCommand(@NonNull String cmd) throws IOException {
return mUiDevice.executeShellCommand(cmd);
}
}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
index f3c8194..c9b5c96 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
@@ -392,7 +392,7 @@
public boolean showImeWithInputMethodManager() {
boolean showResult =
getInputMethodManager()
- .showSoftInput(mEditText, InputMethodManager.SHOW_IMPLICIT);
+ .showSoftInput(mEditText, 0 /* flags */);
if (showResult) {
Log.i(TAG, "IMM#showSoftInput successfully");
} else {
@@ -404,7 +404,8 @@
/** Hide IME with InputMethodManager. */
public boolean hideImeWithInputMethodManager() {
boolean hideResult =
- getInputMethodManager().hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
+ getInputMethodManager()
+ .hideSoftInputFromWindow(mEditText.getWindowToken(), 0 /* flags */);
if (hideResult) {
Log.i(TAG, "IMM#hideSoftInput successfully");
} else {
diff --git a/tests/permission/OWNERS b/tests/permission/OWNERS
index 999ea0e..2fe78c5 100644
--- a/tests/permission/OWNERS
+++ b/tests/permission/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
include /core/java/android/permission/OWNERS
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt
index e03d92ab..f1727b7 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt
@@ -30,6 +30,7 @@
const val IINTERFACE_INTERFACE = "android.os.IInterface"
const val AIDL_PERMISSION_HELPER_SUFFIX = "_enforcePermission"
+const val PERMISSION_PREFIX_LITERAL = "android.permission."
/**
* If a non java (e.g. c++) backend is enabled, the @EnforcePermission
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
index 0a66cd5..3a95df9 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
@@ -95,7 +95,7 @@
val v1 = ConstantEvaluator.evaluate(context, value1)
val v2 = ConstantEvaluator.evaluate(context, value2)
if (v1 != null && v2 != null) {
- if (v1 != v2) {
+ if (v1 != v2 && !isOneShortPermissionOfOther(v1, v2)) {
return false
}
} else {
@@ -107,7 +107,7 @@
for (j in children1.indices) {
val c1 = ConstantEvaluator.evaluate(context, children1[j])
val c2 = ConstantEvaluator.evaluate(context, children2[j])
- if (c1 != c2) {
+ if (c1 != c2 && !isOneShortPermissionOfOther(c1, c2)) {
return false
}
}
@@ -116,6 +116,12 @@
return true
}
+ private fun isOneShortPermissionOfOther(
+ permission1: Any?,
+ permission2: Any?
+ ): Boolean = permission1 == (permission2 as? String)?.removePrefix(PERMISSION_PREFIX_LITERAL) ||
+ permission2 == (permission1 as? String)?.removePrefix(PERMISSION_PREFIX_LITERAL)
+
private fun compareMethods(
context: JavaContext,
element: UElement,
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
index 75b0073..b3dacbd 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
@@ -316,20 +316,55 @@
overrides the method Stub.testMethod which is annotated with @EnforcePermission. The same annotation must be used on Default.testMethod [MissingEnforcePermissionAnnotation]
public void testMethod() {}
~~~~~~~~~~
- 1 errors, 0 warnings
+ 1 errors, 0 warnings
""".addLineContinuation()
)
}
- fun testDoesDetectIssuesShortStringsNotAllowed() {
+ fun testDoesNotDetectIssuesShortStringsAllowedInChildAndParent() {
lint().files(java(
"""
package test.pkg;
import android.annotation.EnforcePermission;
public class TestClass121 extends IFooMethod.Stub {
@Override
+ @EnforcePermission("READ_PHONE_STATE")
+ public void testMethod() {}
+ @Override
+ @EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethodParentShortPermission() {}
+ @Override
@EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"})
public void testMethodAnyLiteral() {}
+ @Override
+ @EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
+ public void testMethodAnyLiteralParentsShortPermission() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testDoesDetectIssuesWrongShortStringsInChildAndParent() {
+ lint().files(java(
+ """
+ package test.pkg;
+ import android.annotation.EnforcePermission;
+ public class TestClass121 extends IFooMethod.Stub {
+ @Override
+ @EnforcePermission("READ_WRONG_PHONE_STATE")
+ public void testMethod() {}
+ @Override
+ @EnforcePermission(android.Manifest.permission.READ_WRONG_PHONE_STATE)
+ public void testMethodParentShortPermission() {}
+ @Override
+ @EnforcePermission(anyOf={"WRONG_INTERNET", "READ_PHONE_STATE"})
+ public void testMethodAnyLiteral() {}
+ @Override
+ @EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_WRONG_PHONE_STATE})
+ public void testMethodAnyLiteralParentsShortPermission() {}
}
""").indented(),
*stubs
@@ -337,14 +372,19 @@
.run()
.expect(
"""
- src/test/pkg/TestClass121.java:6: Error: The method \
- TestClass121.testMethodAnyLiteral is annotated with @EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"}) \
- which differs from the overridden method Stub.testMethodAnyLiteral: \
- @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"}). \
- The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
- public void testMethodAnyLiteral() {}
- ~~~~~~~~~~~~~~~~~~~~
- 1 errors, 0 warnings
+ src/test/pkg/TestClass121.java:6: Error: The method TestClass121.testMethod is annotated with @EnforcePermission("READ_WRONG_PHONE_STATE") which differs from the overridden method Stub.testMethod: @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethod() {}
+ ~~~~~~~~~~
+ src/test/pkg/TestClass121.java:9: Error: The method TestClass121.testMethodParentShortPermission is annotated with @EnforcePermission(android.Manifest.permission.READ_WRONG_PHONE_STATE) which differs from the overridden method Stub.testMethodParentShortPermission: @android.annotation.EnforcePermission("READ_PHONE_STATE"). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethodParentShortPermission() {}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass121.java:12: Error: The method TestClass121.testMethodAnyLiteral is annotated with @EnforcePermission(anyOf={"WRONG_INTERNET", "READ_PHONE_STATE"}) which differs from the overridden method Stub.testMethodAnyLiteral: @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"}). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethodAnyLiteral() {}
+ ~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass121.java:15: Error: The method TestClass121.testMethodAnyLiteralParentsShortPermission is annotated with @EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_WRONG_PHONE_STATE}) which differs from the overridden method Stub.testMethodAnyLiteralParentsShortPermission: @android.annotation.EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"}). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethodAnyLiteralParentsShortPermission() {}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 4 errors, 0 warnings
""".addLineContinuation()
)
}
@@ -360,12 +400,18 @@
@android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
public void testMethod() {}
@Override
+ @android.annotation.EnforcePermission("READ_PHONE_STATE")
+ public void testMethodParentShortPermission() {}
+ @Override
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
public void testMethodAny() {}
@Override
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"})
public void testMethodAnyLiteral() {}
@Override
+ @android.annotation.EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"})
+ public void testMethodAnyLiteralParentsShortPermission() {}
+ @Override
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
public void testMethodAll() {}
@Override
@@ -374,10 +420,14 @@
}
@android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
public void testMethod();
+ @android.annotation.EnforcePermission("READ_PHONE_STATE")
+ public void testMethodParentShortPermission();
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
public void testMethodAny() {}
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"})
public void testMethodAnyLiteral() {}
+ @android.annotation.EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"})
+ public void testMethodAnyLiteralParentsShortPermission() {}
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
public void testMethodAll() {}
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"})
@@ -404,6 +454,7 @@
package android.Manifest;
class permission {
public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
+ public static final String READ_WRONG_PHONE_STATE = "android.permission.READ_WRONG_PHONE_STATE";
public static final String NFC = "android.permission.NFC";
public static final String INTERNET = "android.permission.INTERNET";
}