Merge "Add output of hasCustomAudioInputSupport() to dumpsys" into main
diff --git a/apex/jobscheduler/service/aconfig/Android.bp b/apex/jobscheduler/service/aconfig/Android.bp
index 4db39dc..859c67a 100644
--- a/apex/jobscheduler/service/aconfig/Android.bp
+++ b/apex/jobscheduler/service/aconfig/Android.bp
@@ -2,6 +2,7 @@
aconfig_declarations {
name: "service-deviceidle.flags-aconfig",
package: "com.android.server.deviceidle",
+ container: "system",
srcs: [
"device_idle.aconfig",
],
@@ -17,6 +18,7 @@
aconfig_declarations {
name: "service-job.flags-aconfig",
package: "com.android.server.job",
+ container: "system",
srcs: [
"job.aconfig",
],
@@ -32,6 +34,7 @@
aconfig_declarations {
name: "alarm_flags",
package: "com.android.server.alarm",
+ container: "system",
srcs: ["alarm.aconfig"],
}
diff --git a/apex/jobscheduler/service/aconfig/alarm.aconfig b/apex/jobscheduler/service/aconfig/alarm.aconfig
index bb0f3cb..d3068d7 100644
--- a/apex/jobscheduler/service/aconfig/alarm.aconfig
+++ b/apex/jobscheduler/service/aconfig/alarm.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.alarm"
+container: "system"
flag {
name: "use_frozen_state_to_drop_listener_alarms"
diff --git a/apex/jobscheduler/service/aconfig/device_idle.aconfig b/apex/jobscheduler/service/aconfig/device_idle.aconfig
index e4cb5ad..e8c99b1 100644
--- a/apex/jobscheduler/service/aconfig/device_idle.aconfig
+++ b/apex/jobscheduler/service/aconfig/device_idle.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.deviceidle"
+container: "system"
flag {
name: "disable_wakelocks_in_light_idle"
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index 5e6d377..75e2efd2 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -1,4 +1,5 @@
package: "com.android.server.job"
+container: "system"
flag {
name: "batch_active_bucket_jobs"
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 42e82f6..ea6f45e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -10284,6 +10284,16 @@
* get the list of app restrictions set by each admin via
* {@link android.content.RestrictionsManager#getApplicationRestrictionsPerAdmin}.
*
+ * <p>Starting from Android Version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM},
+ * the device policy management role holder can also set app restrictions on any applications
+ * in the calling user, as well as the parent user of an organization-owned managed profile via
+ * the {@link DevicePolicyManager} instance returned by
+ * {@link #getParentProfileInstance(ComponentName)}. App restrictions set by the device policy
+ * management role holder are not returned by
+ * {@link UserManager#getApplicationRestrictions(String)}. The target application should use
+ * {@link android.content.RestrictionsManager#getApplicationRestrictionsPerAdmin} to retrieve
+ * them, alongside any app restrictions the profile or device owner might have set.
+ *
* <p>NOTE: The method performs disk I/O and shouldn't be called on the main thread
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
@@ -10299,11 +10309,14 @@
@WorkerThread
public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName,
Bundle settings) {
- throwIfParentInstance("setApplicationRestrictions");
+ if (!Flags.dmrhCanSetAppRestriction()) {
+ throwIfParentInstance("setApplicationRestrictions");
+ }
+
if (mService != null) {
try {
mService.setApplicationRestrictions(admin, mContext.getPackageName(), packageName,
- settings);
+ settings, mParentInstance);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -11704,11 +11717,14 @@
@WorkerThread
public @NonNull Bundle getApplicationRestrictions(
@Nullable ComponentName admin, String packageName) {
- throwIfParentInstance("getApplicationRestrictions");
+ if (!Flags.dmrhCanSetAppRestriction()) {
+ throwIfParentInstance("getApplicationRestrictions");
+ }
+
if (mService != null) {
try {
return mService.getApplicationRestrictions(admin, mContext.getPackageName(),
- packageName);
+ packageName, mParentInstance);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -13986,8 +14002,15 @@
public @NonNull DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
throwIfParentInstance("getParentProfileInstance");
try {
- if (!mService.isManagedProfile(admin)) {
- throw new SecurityException("The current user does not have a parent profile.");
+ if (Flags.dmrhCanSetAppRestriction()) {
+ UserManager um = mContext.getSystemService(UserManager.class);
+ if (!um.isManagedProfile()) {
+ throw new SecurityException("The current user does not have a parent profile.");
+ }
+ } else {
+ if (!mService.isManagedProfile(admin)) {
+ throw new SecurityException("The current user does not have a parent profile.");
+ }
}
return new DevicePolicyManager(mContext, mService, true);
} catch (RemoteException e) {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index d4589dc..2002326 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -244,8 +244,8 @@
void setDefaultSmsApplication(in ComponentName admin, String callerPackageName, String packageName, boolean parent);
void setDefaultDialerApplication(String packageName);
- void setApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName, in Bundle settings);
- Bundle getApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName);
+ void setApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName, in Bundle settings, in boolean parent);
+ Bundle getApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName, in boolean parent);
boolean setApplicationRestrictionsManagingPackage(in ComponentName admin, in String packageName);
String getApplicationRestrictionsManagingPackage(in ComponentName admin);
boolean isCallerApplicationRestrictionsManagingPackage(in String callerPackage);
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 6b2baa7..56fb4aa 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -204,6 +204,13 @@
}
flag {
+ name: "dmrh_can_set_app_restriction"
+ namespace: "enterprise"
+ description: "Allow DMRH to set application restrictions (both on the profile and the parent)"
+ bug: "328758346"
+}
+
+flag {
name: "allow_screen_brightness_control_on_cope"
namespace: "enterprise"
description: "Allow COPE admin to control screen brightness and timeout."
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 7548562..508077e 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -2655,13 +2655,12 @@
);
}
GetCredentialRequest getCredentialRequest = node.getPendingCredentialRequest();
- if (getCredentialRequest == null) {
- Log.i(TAG, prefix + " No Credential Manager Request");
- } else {
- Log.i(TAG, prefix + " GetCredentialRequest: no. of options= "
- + getCredentialRequest.getCredentialOptions().size()
- );
- }
+ Log.i(TAG, prefix + " Credential Manager info:"
+ + " hasCredentialManagerRequest=" + (getCredentialRequest != null)
+ + (getCredentialRequest != null
+ ? ", sizeOfOptions=" + getCredentialRequest.getCredentialOptions().size()
+ : "")
+ );
final int NCHILDREN = node.getChildCount();
if (NCHILDREN > 0) {
diff --git a/core/java/android/permission/TEST_MAPPING b/core/java/android/permission/TEST_MAPPING
index 69113ef..a15d9bc 100644
--- a/core/java/android/permission/TEST_MAPPING
+++ b/core/java/android/permission/TEST_MAPPING
@@ -11,5 +11,29 @@
}
]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsVirtualDevicesAudioTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "include-filter": "android.virtualdevice.cts.audio.VirtualAudioPermissionTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsVirtualDevicesAppLaunchTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "include-filter": "android.virtualdevice.cts.applaunch.VirtualDevicePermissionTest"
+ }
+ ]
+ }
]
}
\ No newline at end of file
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index cfdf8fa..1cd7d34 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -1272,7 +1272,7 @@
* surface has no buffer or crop, the surface is boundless and only constrained
* by the size of its parent bounds.
*
- * @param session The surface session, must not be null.
+ * @param session The surface session.
* @param name The surface name, must not be null.
* @param w The surface initial width.
* @param h The surface initial height.
diff --git a/core/jni/android_tracing_PerfettoDataSource.cpp b/core/jni/android_tracing_PerfettoDataSource.cpp
index 1eff5ce..25ff853 100644
--- a/core/jni/android_tracing_PerfettoDataSource.cpp
+++ b/core/jni/android_tracing_PerfettoDataSource.cpp
@@ -213,7 +213,7 @@
PerfettoDataSource::~PerfettoDataSource() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
- env->DeleteWeakGlobalRef(mJavaDataSource);
+ env->DeleteGlobalRef(mJavaDataSource);
}
jlong nativeCreate(JNIEnv* env, jclass clazz, jobject javaDataSource, jstring name) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index f790d2a..d0879434 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -425,7 +425,7 @@
}
boolean shouldResizeListenerHandleEvent(MotionEvent e, Point offset) {
- return mDragResizeListener.shouldHandleEvent(e, offset);
+ return mDragResizeListener != null && mDragResizeListener.shouldHandleEvent(e, offset);
}
boolean isHandlingDragResize() {
@@ -795,7 +795,7 @@
*/
private Region getGlobalExclusionRegion() {
Region exclusionRegion;
- if (mTaskInfo.isResizeable) {
+ if (mDragResizeListener != null && mTaskInfo.isResizeable) {
exclusionRegion = mDragResizeListener.getCornersRegion();
} else {
exclusionRegion = new Region();
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index c1be6b5..50fdb8a 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -121,3 +121,13 @@
description: "Enables apps owning a MediaBrowserService to disconnect all connected browsers."
bug: "185136506"
}
+
+flag {
+ name: "enable_prevention_of_manager_scans_when_no_apps_scan"
+ namespace: "media_solutions"
+ description: "Prevents waking up route providers when no apps are scanning, even if SysUI or Settings are scanning."
+ bug: "319604673"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java
index 7ed67dc..2a0648d 100644
--- a/media/java/android/media/projection/MediaProjectionManager.java
+++ b/media/java/android/media/projection/MediaProjectionManager.java
@@ -23,9 +23,6 @@
import android.annotation.TestApi;
import android.app.Activity;
import android.app.ActivityOptions.LaunchCookie;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
-import android.compat.annotation.Overridable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -69,18 +66,6 @@
private static final String TAG = "MediaProjectionManager";
/**
- * This change id ensures that users are presented with a choice of capturing a single app
- * or the entire screen when initiating a MediaProjection session, overriding the usage of
- * MediaProjectionConfig#createConfigForDefaultDisplay.
- *
- * @hide
- */
- @ChangeId
- @Overridable
- @Disabled
- public static final long OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION = 316897322L;
-
- /**
* Intent extra to customize the permission dialog based on the host app's preferences.
* @hide
*/
diff --git a/packages/CrashRecovery/services/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
index 7093ba4..f86eb61 100644
--- a/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
@@ -139,7 +139,7 @@
static final String NAMESPACE_TO_PACKAGE_MAPPING_FLAG =
"namespace_to_package_mapping";
@VisibleForTesting
- static final long DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN = 10;
+ static final long DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN = 1440;
private static final String NAME = "rescue-party-observer";
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 0b36757..d6cbf2a 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -88,6 +88,7 @@
aconfig_declarations {
name: "settingslib_media_flags",
package: "com.android.settingslib.media.flags",
+ container: "system",
srcs: [
"aconfig/settingslib_media_flag_declarations.aconfig",
],
@@ -101,6 +102,7 @@
aconfig_declarations {
name: "settingslib_flags",
package: "com.android.settingslib.flags",
+ container: "system",
srcs: [
"aconfig/settingslib.aconfig",
],
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 54c5a14..e09ab00 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -1,4 +1,5 @@
package: "com.android.settingslib.flags"
+container: "system"
flag {
name: "new_status_bar_icons"
diff --git a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
index f3e537b..4d70aec 100644
--- a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
@@ -1,4 +1,5 @@
package: "com.android.settingslib.media.flags"
+container: "system"
flag {
name: "use_media_router2_for_info_media_manager"
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java b/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java
index 6b833cc..0282f03 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java
@@ -16,6 +16,7 @@
package com.android.settingslib.applications;
+import android.annotation.NonNull;
import android.app.usage.StorageStats;
import android.app.usage.StorageStatsManager;
import android.content.Context;
@@ -25,6 +26,7 @@
import androidx.annotation.VisibleForTesting;
import java.io.IOException;
+import java.util.UUID;
/**
* StorageStatsSource wraps the StorageStatsManager for testability purposes.
@@ -59,6 +61,10 @@
return mStorageStatsManager.getCacheQuotaBytes(volumeUuid, uid);
}
+ public long getTotalBytes(@NonNull UUID storageUuid) throws IOException {
+ return mStorageStatsManager.getTotalBytes(storageUuid);
+ }
+
/**
* Static class that provides methods for querying the amount of external storage available as
* well as breaking it up into several media types.
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index e34c50e..4e6d3cb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -174,6 +174,7 @@
public void startScan() {
mMediaDevices.clear();
+ registerRouter();
startScanOnRouter();
updateRouteListingPreference();
refreshDevices();
@@ -188,10 +189,19 @@
}
}
- public abstract void stopScan();
+ public final void stopScan() {
+ stopScanOnRouter();
+ unregisterRouter();
+ }
+
+ protected abstract void stopScanOnRouter();
protected abstract void startScanOnRouter();
+ protected abstract void registerRouter();
+
+ protected abstract void unregisterRouter();
+
protected abstract void transferToRoute(@NonNull MediaRoute2Info route);
protected abstract void selectRoute(
@@ -514,17 +524,20 @@
// MediaRoute2Info.getType was made public on API 34, but exists since API 30.
@SuppressWarnings("NewApi")
private synchronized void buildAvailableRoutes() {
- for (MediaRoute2Info route : getAvailableRoutes()) {
+ RoutingSessionInfo activeSession = getActiveRoutingSession();
+
+ for (MediaRoute2Info route : getAvailableRoutes(activeSession)) {
if (DEBUG) {
Log.d(TAG, "buildAvailableRoutes() route : " + route.getName() + ", volume : "
+ route.getVolume() + ", type : " + route.getType());
}
- addMediaDevice(route);
+ addMediaDevice(route, activeSession);
}
}
- private synchronized List<MediaRoute2Info> getAvailableRoutes() {
+
+ private synchronized List<MediaRoute2Info> getAvailableRoutes(
+ RoutingSessionInfo activeSession) {
List<MediaRoute2Info> availableRoutes = new ArrayList<>();
- RoutingSessionInfo activeSession = getActiveRoutingSession();
List<MediaRoute2Info> selectedRoutes = getSelectedRoutes(activeSession);
availableRoutes.addAll(selectedRoutes);
@@ -562,7 +575,7 @@
// MediaRoute2Info.getType was made public on API 34, but exists since API 30.
@SuppressWarnings("NewApi")
@VisibleForTesting
- void addMediaDevice(MediaRoute2Info route) {
+ void addMediaDevice(MediaRoute2Info route, RoutingSessionInfo activeSession) {
final int deviceType = route.getType();
MediaDevice mediaDevice = null;
switch (deviceType) {
@@ -627,14 +640,13 @@
break;
}
- if (mediaDevice != null
- && getActiveRoutingSession().getSelectedRoutes().contains(route.getId())) {
- mediaDevice.setState(STATE_SELECTED);
- if (mCurrentConnectedDevice == null) {
- mCurrentConnectedDevice = mediaDevice;
- }
- }
if (mediaDevice != null) {
+ if (activeSession.getSelectedRoutes().contains(route.getId())) {
+ mediaDevice.setState(STATE_SELECTED);
+ if (mCurrentConnectedDevice == null) {
+ mCurrentConnectedDevice = mediaDevice;
+ }
+ }
mMediaDevices.add(mediaDevice);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
index c4fac35..23063da 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
@@ -62,22 +62,30 @@
@Override
protected void startScanOnRouter() {
if (!mIsScanning) {
- mRouterManager.registerCallback(mExecutor, mMediaRouterCallback);
mRouterManager.registerScanRequest();
mIsScanning = true;
}
}
@Override
- public void stopScan() {
+ protected void registerRouter() {
+ mRouterManager.registerCallback(mExecutor, mMediaRouterCallback);
+ }
+
+ @Override
+ protected void stopScanOnRouter() {
if (mIsScanning) {
- mRouterManager.unregisterCallback(mMediaRouterCallback);
mRouterManager.unregisterScanRequest();
mIsScanning = false;
}
}
@Override
+ protected void unregisterRouter() {
+ mRouterManager.unregisterCallback(mMediaRouterCallback);
+ }
+
+ @Override
protected void transferToRoute(@NonNull MediaRoute2Info route) {
// TODO: b/279555229 - provide real user handle of a caller.
mRouterManager.transfer(mPackageName, route, android.os.Process.myUserHandle());
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
index 2b8c2dd..cf11c6d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
@@ -63,12 +63,22 @@
}
@Override
- public void stopScan() {
+ protected void startScanOnRouter() {
// Do nothing.
}
@Override
- protected void startScanOnRouter() {
+ protected void registerRouter() {
+ // Do nothing.
+ }
+
+ @Override
+ protected void stopScanOnRouter() {
+ // Do nothing.
+ }
+
+ @Override
+ protected void unregisterRouter() {
// Do nothing.
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
index 9c82cb1..0dceeba 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
@@ -97,11 +97,6 @@
@Override
protected void startScanOnRouter() {
- mRouter.registerRouteCallback(mExecutor, mRouteCallback, RouteDiscoveryPreference.EMPTY);
- mRouter.registerRouteListingPreferenceUpdatedCallback(
- mExecutor, mRouteListingPreferenceCallback);
- mRouter.registerTransferCallback(mExecutor, mTransferCallback);
- mRouter.registerControllerCallback(mExecutor, mControllerCallback);
if (Flags.enableScreenOffScanning()) {
MediaRouter2.ScanRequest request = new MediaRouter2.ScanRequest.Builder().build();
mScanToken.compareAndSet(null, mRouter.requestScan(request));
@@ -111,7 +106,16 @@
}
@Override
- public void stopScan() {
+ protected void registerRouter() {
+ mRouter.registerRouteCallback(mExecutor, mRouteCallback, RouteDiscoveryPreference.EMPTY);
+ mRouter.registerRouteListingPreferenceUpdatedCallback(
+ mExecutor, mRouteListingPreferenceCallback);
+ mRouter.registerTransferCallback(mExecutor, mTransferCallback);
+ mRouter.registerControllerCallback(mExecutor, mControllerCallback);
+ }
+
+ @Override
+ protected void stopScanOnRouter() {
if (Flags.enableScreenOffScanning()) {
MediaRouter2.ScanToken token = mScanToken.getAndSet(null);
if (token != null) {
@@ -120,6 +124,10 @@
} else {
mRouter.stopScan();
}
+ }
+
+ @Override
+ protected void unregisterRouter() {
mRouter.unregisterControllerCallback(mControllerCallback);
mRouter.unregisterTransferCallback(mTransferCallback);
mRouter.unregisterRouteListingPreferenceUpdatedCallback(mRouteListingPreferenceCallback);
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 d85d253..f8dcec7 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
@@ -86,6 +86,30 @@
private static final String TEST_DUPLICATED_ID_2 = "test_duplicated_id_2";
private static final String TEST_DUPLICATED_ID_3 = "test_duplicated_id_3";
+ private static final String TEST_SYSTEM_ROUTE_ID = "TEST_SYSTEM_ROUTE_ID";
+ private static final String TEST_BLUETOOTH_ROUTE_ID = "TEST_BT_ROUTE_ID";
+
+ private static final RoutingSessionInfo TEST_SYSTEM_ROUTING_SESSION =
+ new RoutingSessionInfo.Builder("FAKE_SYSTEM_ROUTING_SESSION_ID", TEST_PACKAGE_NAME)
+ .addSelectedRoute(TEST_SYSTEM_ROUTE_ID)
+ .addTransferableRoute(TEST_BLUETOOTH_ROUTE_ID)
+ .setSystemSession(true)
+ .build();
+
+ private static final MediaRoute2Info TEST_SELECTED_SYSTEM_ROUTE =
+ new MediaRoute2Info.Builder(TEST_SYSTEM_ROUTE_ID, "SELECTED_SYSTEM_ROUTE")
+ .setSystemRoute(true)
+ .addFeature(MediaRoute2Info.FEATURE_LIVE_AUDIO)
+ .build();
+
+ private static final MediaRoute2Info TEST_BLUETOOTH_ROUTE =
+ new MediaRoute2Info.Builder(TEST_BLUETOOTH_ROUTE_ID, "BLUETOOTH_ROUTE")
+ .setSystemRoute(true)
+ .addFeature(MediaRoute2Info.FEATURE_LIVE_AUDIO)
+ .setType(TYPE_BLUETOOTH_A2DP)
+ .setAddress("00:00:00:00:00:00")
+ .build();
+
@Mock
private MediaRouter2Manager mRouterManager;
@Mock
@@ -795,19 +819,19 @@
when(route2Info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
when(route2Info.getId()).thenReturn(TEST_ID);
- mInfoMediaManager.addMediaDevice(route2Info);
+ mInfoMediaManager.addMediaDevice(route2Info, TEST_SYSTEM_ROUTING_SESSION);
assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof InfoMediaDevice).isTrue();
when(route2Info.getType()).thenReturn(TYPE_USB_DEVICE);
when(route2Info.getId()).thenReturn(TEST_ID);
mInfoMediaManager.mMediaDevices.clear();
- mInfoMediaManager.addMediaDevice(route2Info);
+ mInfoMediaManager.addMediaDevice(route2Info, TEST_SYSTEM_ROUTING_SESSION);
assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof PhoneMediaDevice).isTrue();
when(route2Info.getType()).thenReturn(TYPE_WIRED_HEADSET);
when(route2Info.getId()).thenReturn(TEST_ID);
mInfoMediaManager.mMediaDevices.clear();
- mInfoMediaManager.addMediaDevice(route2Info);
+ mInfoMediaManager.addMediaDevice(route2Info, TEST_SYSTEM_ROUTING_SESSION);
assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof PhoneMediaDevice).isTrue();
when(route2Info.getType()).thenReturn(TYPE_BLUETOOTH_A2DP);
@@ -818,12 +842,12 @@
when(cachedBluetoothDeviceManager.findDevice(any(BluetoothDevice.class)))
.thenReturn(cachedDevice);
mInfoMediaManager.mMediaDevices.clear();
- mInfoMediaManager.addMediaDevice(route2Info);
+ mInfoMediaManager.addMediaDevice(route2Info, TEST_SYSTEM_ROUTING_SESSION);
assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof BluetoothMediaDevice).isTrue();
when(route2Info.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
mInfoMediaManager.mMediaDevices.clear();
- mInfoMediaManager.addMediaDevice(route2Info);
+ mInfoMediaManager.addMediaDevice(route2Info, TEST_SYSTEM_ROUTING_SESSION);
assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof PhoneMediaDevice).isTrue();
}
@@ -841,26 +865,27 @@
.thenReturn(null);
mInfoMediaManager.mMediaDevices.clear();
- mInfoMediaManager.addMediaDevice(route2Info);
+ mInfoMediaManager.addMediaDevice(route2Info, TEST_SYSTEM_ROUTING_SESSION);
assertThat(mInfoMediaManager.mMediaDevices.size()).isEqualTo(0);
}
@Test
public void addMediaDevice_deviceIncludedInSelectedDevices_shouldSetAsCurrentConnected() {
- final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
final CachedBluetoothDeviceManager cachedBluetoothDeviceManager =
mock(CachedBluetoothDeviceManager.class);
- final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
- final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
- final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
- routingSessionInfos.add(sessionInfo);
- when(mRouterManager.getRoutingSessions(TEST_PACKAGE_NAME)).thenReturn(routingSessionInfos);
- when(sessionInfo.getSelectedRoutes()).thenReturn(ImmutableList.of(TEST_ID));
- when(route2Info.getType()).thenReturn(TYPE_BLUETOOTH_A2DP);
- when(route2Info.getAddress()).thenReturn("00:00:00:00:00:00");
- when(route2Info.getId()).thenReturn(TEST_ID);
+ final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
+ RoutingSessionInfo selectedBtSession =
+ new RoutingSessionInfo.Builder(TEST_SYSTEM_ROUTING_SESSION)
+ .clearSelectedRoutes()
+ .clearTransferableRoutes()
+ .addSelectedRoute(TEST_BLUETOOTH_ROUTE_ID)
+ .addTransferableRoute(TEST_SYSTEM_ROUTE_ID)
+ .build();
+
+ when(mRouterManager.getRoutingSessions(TEST_PACKAGE_NAME))
+ .thenReturn(List.of(selectedBtSession));
when(mLocalBluetoothManager.getCachedDeviceManager())
.thenReturn(cachedBluetoothDeviceManager);
when(cachedBluetoothDeviceManager.findDevice(any(BluetoothDevice.class)))
@@ -868,7 +893,7 @@
mInfoMediaManager.mRouterManager = mRouterManager;
mInfoMediaManager.mMediaDevices.clear();
- mInfoMediaManager.addMediaDevice(route2Info);
+ mInfoMediaManager.addMediaDevice(TEST_BLUETOOTH_ROUTE, selectedBtSession);
MediaDevice device = mInfoMediaManager.mMediaDevices.get(0);
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
index bf4f60d..e9c2672 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -32,6 +32,7 @@
"unsupportedappusage",
],
static_libs: [
+ "aconfig_demo_flags_java_lib",
"device_config_service_flags_java",
"libaconfig_java_proto_lite",
"SettingsLibDeviceStateRotationLock",
@@ -87,6 +88,7 @@
aconfig_declarations {
name: "device_config_service_flags",
package: "com.android.providers.settings",
+ container: "system",
srcs: [
"src/com/android/providers/settings/device_config_service.aconfig",
],
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 4e4c22f..a28cfeb 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -60,16 +60,17 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
-import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFileAttributes;
+import java.nio.file.attribute.PosixFilePermission;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -165,8 +166,8 @@
private static final String STORAGE_MIGRATION_FLAG =
"core_experiments_team_internal/com.android.providers.settings.storage_test_mission_1";
- private static final String STORAGE_MIGRATION_LOG =
- "/metadata/aconfig/flags/storage_migration.log";
+ private static final String STORAGE_MIGRATION_MARKER_FILE =
+ "/metadata/aconfig/storage_test_mission_1";
/**
* This tag is applied to all aconfig default value-loaded flags.
@@ -1467,16 +1468,29 @@
}
}
- if (name != null && name.equals(STORAGE_MIGRATION_FLAG) && value.equals("true")) {
- File file = new File(STORAGE_MIGRATION_LOG);
- if (!file.exists()) {
- try (BufferedWriter writer =
- new BufferedWriter(new FileWriter(STORAGE_MIGRATION_LOG))) {
- final long timestamp = System.currentTimeMillis();
- String entry = String.format("%d | Log init", timestamp);
- writer.write(entry);
- } catch (IOException e) {
- Slog.e(LOG_TAG, "failed to write storage migration file", e);
+ if (isConfigSettingsKey(mKey) && name != null
+ && name.equals(STORAGE_MIGRATION_FLAG)) {
+ if (value.equals("true")) {
+ Path path = Paths.get(STORAGE_MIGRATION_MARKER_FILE);
+ if (!Files.exists(path)) {
+ Files.createFile(path);
+ }
+
+ Set<PosixFilePermission> perms =
+ Files.readAttributes(path, PosixFileAttributes.class).permissions();
+ perms.add(PosixFilePermission.OWNER_WRITE);
+ perms.add(PosixFilePermission.OWNER_READ);
+ perms.add(PosixFilePermission.GROUP_READ);
+ perms.add(PosixFilePermission.OTHERS_READ);
+ try {
+ Files.setPosixFilePermissions(path, perms);
+ } catch (Exception e) {
+ Slog.e(LOG_TAG, "failed to set permissions on migration marker", e);
+ }
+ } else {
+ java.nio.file.Path path = Paths.get(STORAGE_MIGRATION_MARKER_FILE);
+ if (Files.exists(path)) {
+ Files.delete(path);
}
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
index c572bdb..d20fbf5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
+++ b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
@@ -1,4 +1,5 @@
package: "com.android.providers.settings"
+container: "system"
flag {
name: "support_overrides"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp
index f74e59a..0ff856e 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp
@@ -5,6 +5,7 @@
aconfig_declarations {
name: "com_android_a11y_menu_flags",
package: "com.android.systemui.accessibility.accessibilitymenu",
+ container: "system",
srcs: [
"accessibility.aconfig",
],
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
index f5db6a4..d868d5c 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
@@ -1,4 +1,5 @@
package: "com.android.systemui.accessibility.accessibilitymenu"
+container: "system"
# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
diff --git a/packages/SystemUI/aconfig/Android.bp b/packages/SystemUI/aconfig/Android.bp
index 15c2c17..2a32b58 100644
--- a/packages/SystemUI/aconfig/Android.bp
+++ b/packages/SystemUI/aconfig/Android.bp
@@ -36,6 +36,7 @@
aconfig_declarations {
name: "com_android_systemui_flags",
package: "com.android.systemui",
+ container: "system",
srcs: [
"*.aconfig",
],
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 866aa89..8137e40 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -1,4 +1,5 @@
package: "com.android.systemui"
+container: "system"
# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
diff --git a/packages/SystemUI/aconfig/biometrics_framework.aconfig b/packages/SystemUI/aconfig/biometrics_framework.aconfig
index 7cc0c83..bd1a442 100644
--- a/packages/SystemUI/aconfig/biometrics_framework.aconfig
+++ b/packages/SystemUI/aconfig/biometrics_framework.aconfig
@@ -1,4 +1,5 @@
package: "com.android.systemui"
+container: "system"
# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
@@ -14,4 +15,4 @@
namespace: "biometrics_framework"
description: "Refactors Biometric Prompt to use a ConstraintLayout"
bug: "288175072"
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/aconfig/communal.aconfig b/packages/SystemUI/aconfig/communal.aconfig
index 2c6ff97..2e9af7e 100644
--- a/packages/SystemUI/aconfig/communal.aconfig
+++ b/packages/SystemUI/aconfig/communal.aconfig
@@ -1,4 +1,5 @@
package: "com.android.systemui"
+container: "system"
flag {
name: "communal_hub"
diff --git a/packages/SystemUI/aconfig/cross_device_control.aconfig b/packages/SystemUI/aconfig/cross_device_control.aconfig
index d3f14c1..5f9a4f4 100644
--- a/packages/SystemUI/aconfig/cross_device_control.aconfig
+++ b/packages/SystemUI/aconfig/cross_device_control.aconfig
@@ -1,4 +1,5 @@
package: "com.android.systemui"
+container: "system"
flag {
name: "legacy_le_audio_sharing"
diff --git a/packages/SystemUI/aconfig/predictive_back.aconfig b/packages/SystemUI/aconfig/predictive_back.aconfig
index 7bbe82c..46eb9e1 100644
--- a/packages/SystemUI/aconfig/predictive_back.aconfig
+++ b/packages/SystemUI/aconfig/predictive_back.aconfig
@@ -1,4 +1,5 @@
package: "com.android.systemui"
+container: "system"
flag {
name: "predictive_back_sysui"
@@ -26,4 +27,4 @@
namespace: "systemui"
description: "Enable Predictive Back Animation for SysUI dialogs"
bug: "327721544"
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 4bfc629..112d964 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1,4 +1,5 @@
package: "com.android.systemui"
+container: "system"
flag {
name: "example_flag"
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
index 7a73c58..8129e41 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
@@ -71,7 +71,6 @@
return remember(clock, topInset, topmostTop) {
BurnInParameters(
- clockControllerProvider = { clock },
topInset = topInset,
minViewY = topmostTop,
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index fa0a1c4..a31b533 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -33,7 +33,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.notifications.ui.composable.NotificationStack
+import com.android.systemui.notifications.ui.composable.ConstrainedNotificationStack
import com.android.systemui.res.R
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
@@ -94,7 +94,7 @@
return
}
- NotificationStack(
+ ConstrainedNotificationStack(
viewModel = viewModel,
modifier =
modifier.fillMaxWidth().thenIf(shouldUseSplitNotificationShade) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index cda4347..579e837 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -36,12 +36,14 @@
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
@@ -60,7 +62,6 @@
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
-import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp
import com.android.compose.animation.scene.ElementKey
@@ -68,12 +69,12 @@
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.height
import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
-import com.android.systemui.notifications.ui.composable.Notifications.Form
import com.android.systemui.notifications.ui.composable.Notifications.TransitionThresholds.EXPANSION_FOR_MAX_CORNER_RADIUS
import com.android.systemui.notifications.ui.composable.Notifications.TransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.ui.composable.ShadeHeader
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import kotlin.math.roundToInt
@@ -81,7 +82,8 @@
object Notifications {
object Elements {
val NotificationScrim = ElementKey("NotificationScrim")
- val NotificationPlaceholder = ElementKey("NotificationPlaceholder")
+ val NotificationStackPlaceholder = ElementKey("NotificationStackPlaceholder")
+ val HeadsUpNotificationPlaceholder = ElementKey("HeadsUpNotificationPlaceholder")
val ShelfSpace = ElementKey("ShelfSpace")
}
@@ -91,12 +93,6 @@
const val EXPANSION_FOR_MAX_CORNER_RADIUS = 0.1f
const val EXPANSION_FOR_MAX_SCRIM_ALPHA = 0.3f
}
-
- enum class Form {
- HunFromTop,
- Stack,
- HunFromBottom,
- }
}
/**
@@ -109,24 +105,48 @@
modifier: Modifier = Modifier,
isPeekFromBottom: Boolean = false,
) {
- NotificationPlaceholder(
- viewModel = viewModel,
- form = if (isPeekFromBottom) Form.HunFromBottom else Form.HunFromTop,
- modifier = modifier,
- )
+ val headsUpHeight = viewModel.headsUpHeight.collectAsState()
+
+ Element(
+ Notifications.Elements.HeadsUpNotificationPlaceholder,
+ modifier =
+ modifier
+ .height { headsUpHeight.value.roundToInt() }
+ .fillMaxWidth()
+ .debugBackground(viewModel, DEBUG_HUN_COLOR)
+ .onGloballyPositioned { coordinates: LayoutCoordinates ->
+ val boundsInWindow = coordinates.boundsInWindow()
+ debugLog(viewModel) {
+ "HUNS onGloballyPositioned:" +
+ " size=${coordinates.size}" +
+ " bounds=$boundsInWindow"
+ }
+ viewModel.onHeadsUpTopChanged(boundsInWindow.top)
+ }
+ ) {
+ content {}
+ }
}
/** Adds the space where notification stack should appear in the scene. */
@Composable
-fun SceneScope.NotificationStack(
+fun SceneScope.ConstrainedNotificationStack(
viewModel: NotificationsPlaceholderViewModel,
modifier: Modifier = Modifier,
) {
- NotificationPlaceholder(
- viewModel = viewModel,
- form = Form.Stack,
- modifier = modifier,
- )
+ Box(
+ modifier =
+ modifier.onSizeChanged { viewModel.onConstrainedAvailableSpaceChanged(it.height) }
+ ) {
+ NotificationPlaceholder(
+ viewModel = viewModel,
+ modifier = Modifier.fillMaxSize(),
+ )
+ HeadsUpNotificationSpace(
+ viewModel = viewModel,
+ modifier = Modifier.align(Alignment.TopCenter),
+ )
+ }
}
/**
@@ -169,6 +189,9 @@
// entire height of the scrim is visible on screen.
val scrimOffset = remember { mutableStateOf(0f) }
+ // set the bounds to null when the scrim disappears
+ DisposableEffect(Unit) { onDispose { viewModel.onScrimBoundsChanged(null) } }
+
val minScrimTop = with(density) { ShadeHeader.Dimensions.CollapsedHeight.toPx() }
// The minimum offset for the scrim. The scrim is considered fully expanded when it
@@ -235,6 +258,22 @@
.let { scrimRounding.value.toRoundedCornerShape(it) }
clip = true
}
+ .onGloballyPositioned { coordinates ->
+ val boundsInWindow = coordinates.boundsInWindow()
+ debugLog(viewModel) {
+ "SCRIM onGloballyPositioned:" +
+ " size=${coordinates.size}" +
+ " bounds=$boundsInWindow"
+ }
+ viewModel.onScrimBoundsChanged(
+ ShadeScrimBounds(
+ left = boundsInWindow.left,
+ top = boundsInWindow.top,
+ right = boundsInWindow.right,
+ bottom = boundsInWindow.bottom,
+ )
+ )
+ }
) {
// Creates a cutout in the background scrim in the shape of the notifications scrim.
// Only visible when notif scrim alpha < 1, during shade expansion.
@@ -254,11 +293,10 @@
} else 1f
}
.background(MaterialTheme.colorScheme.surface)
- .debugBackground(viewModel, Color(0.5f, 0.5f, 0f, 0.2f))
+ .debugBackground(viewModel, DEBUG_BOX_COLOR)
) {
NotificationPlaceholder(
viewModel = viewModel,
- form = Form.Stack,
modifier =
Modifier.verticalNestedScrollToScene(
topBehavior = NestedScrollBehavior.EdgeWithPreview,
@@ -284,6 +322,7 @@
.height { (stackHeight.value + navBarHeight).roundToInt() },
)
}
+ HeadsUpNotificationSpace(viewModel = viewModel)
}
}
@@ -304,14 +343,10 @@
modifier
.element(key = Notifications.Elements.ShelfSpace)
.fillMaxWidth()
- .onSizeChanged { size: IntSize ->
- debugLog(viewModel) { "SHELF onSizeChanged: size=$size" }
- }
.onPlaced { coordinates: LayoutCoordinates ->
debugLog(viewModel) {
("SHELF onPlaced:" +
" size=${coordinates.size}" +
- " position=${coordinates.positionInWindow()}" +
" bounds=${coordinates.boundsInWindow()}")
}
}
@@ -326,32 +361,26 @@
@Composable
private fun SceneScope.NotificationPlaceholder(
viewModel: NotificationsPlaceholderViewModel,
- form: Form,
modifier: Modifier = Modifier,
) {
- val elementKey = Notifications.Elements.NotificationPlaceholder
Element(
- elementKey,
+ Notifications.Elements.NotificationStackPlaceholder,
modifier =
modifier
- .debugBackground(viewModel)
- .onSizeChanged { size: IntSize ->
- debugLog(viewModel) { "STACK onSizeChanged: size=$size" }
- }
+ .debugBackground(viewModel, DEBUG_STACK_COLOR)
+ .onSizeChanged { size -> debugLog(viewModel) { "STACK onSizeChanged: size=$size" } }
.onGloballyPositioned { coordinates: LayoutCoordinates ->
- viewModel.onContentTopChanged(coordinates.positionInWindow().y)
+ val positionInWindow = coordinates.positionInWindow()
debugLog(viewModel) {
"STACK onGloballyPositioned:" +
" size=${coordinates.size}" +
- " position=${coordinates.positionInWindow()}" +
+ " position=$positionInWindow" +
" bounds=${coordinates.boundsInWindow()}"
}
- val boundsInWindow = coordinates.boundsInWindow()
- viewModel.onBoundsChanged(
- left = boundsInWindow.left,
- top = boundsInWindow.top,
- right = boundsInWindow.right,
- bottom = boundsInWindow.bottom,
+ // NOTE: positionInWindow.y scrolls off screen, but boundsInWindow.top will not
+ viewModel.onStackBoundsChanged(
+ top = positionInWindow.y,
+ bottom = positionInWindow.y + coordinates.size.height,
)
}
) {
@@ -388,7 +417,7 @@
private fun Modifier.debugBackground(
viewModel: NotificationsPlaceholderViewModel,
- color: Color = DEBUG_COLOR,
+ color: Color,
): Modifier =
if (viewModel.isVisualDebuggingEnabled) {
background(color)
@@ -397,8 +426,8 @@
}
fun ShadeScrimRounding.toRoundedCornerShape(radius: Dp): RoundedCornerShape {
- val topRadius = if (roundTop) radius else 0.dp
- val bottomRadius = if (roundBottom) radius else 0.dp
+ val topRadius = if (isTopRounded) radius else 0.dp
+ val bottomRadius = if (isBottomRounded) radius else 0.dp
return RoundedCornerShape(
topStart = topRadius,
topEnd = topRadius,
@@ -408,4 +437,6 @@
}
private const val TAG = "FlexiNotifs"
-private val DEBUG_COLOR = Color(1f, 0f, 0f, 0.2f)
+private val DEBUG_STACK_COLOR = Color(1f, 0f, 0f, 0.2f)
+private val DEBUG_HUN_COLOR = Color(0f, 0f, 1f, 0.2f)
+private val DEBUG_BOX_COLOR = Color(0f, 1f, 0f, 0.2f)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
index f517cec..31337a6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -23,6 +23,7 @@
import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.burnInInteractor
@@ -60,10 +61,7 @@
private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
private lateinit var underTest: AodBurnInViewModel
- private var burnInParameters =
- BurnInParameters(
- clockControllerProvider = { clockController },
- )
+ private var burnInParameters = BurnInParameters()
private val burnInFlow = MutableStateFlow(BurnInModel())
@Before
@@ -76,6 +74,7 @@
whenever(goneToAodTransitionViewModel.enterFromTopTranslationY(anyInt()))
.thenReturn(emptyFlow())
kosmos.goneToAodTransitionViewModel = goneToAodTransitionViewModel
+ kosmos.fakeKeyguardClockRepository.setCurrentClock(clockController)
underTest = kosmos.aodBurnInViewModel
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
index 53522e2..3c28c0e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
@@ -31,7 +31,9 @@
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
-import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationStackAppearanceViewModel
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
+import com.android.systemui.statusbar.notification.stack.shared.model.ViewPosition
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationScrollViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -57,27 +59,44 @@
}
private val testScope = kosmos.testScope
private val placeholderViewModel by lazy { kosmos.notificationsPlaceholderViewModel }
- private val appearanceViewModel by lazy { kosmos.notificationStackAppearanceViewModel }
+ private val appearanceViewModel by lazy { kosmos.notificationScrollViewModel }
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val fakeSceneDataSource by lazy { kosmos.fakeSceneDataSource }
@Test
fun updateBounds() =
testScope.runTest {
- val clipping by collectLastValue(appearanceViewModel.shadeScrimClipping)
+ val radius = MutableStateFlow(32)
+ val viewPosition = MutableStateFlow(ViewPosition(0, 0))
+ val shape by collectLastValue(appearanceViewModel.shadeScrimShape(radius, viewPosition))
- val top = 200f
- val left = 0f
- val bottom = 550f
- val right = 100f
- placeholderViewModel.onBoundsChanged(
- left = left,
- top = top,
- right = right,
- bottom = bottom
+ placeholderViewModel.onScrimBoundsChanged(
+ ShadeScrimBounds(left = 0f, top = 200f, right = 100f, bottom = 550f)
)
- assertThat(clipping?.bounds)
- .isEqualTo(ShadeScrimBounds(left = left, top = top, right = right, bottom = bottom))
+ assertThat(shape)
+ .isEqualTo(
+ ShadeScrimShape(
+ bounds =
+ ShadeScrimBounds(left = 0f, top = 200f, right = 100f, bottom = 550f),
+ topRadius = 32,
+ bottomRadius = 0
+ )
+ )
+
+ viewPosition.value = ViewPosition(200, 15)
+ radius.value = 24
+ placeholderViewModel.onScrimBoundsChanged(
+ ShadeScrimBounds(left = 210f, top = 200f, right = 300f, bottom = 550f)
+ )
+ assertThat(shape)
+ .isEqualTo(
+ ShadeScrimShape(
+ bounds =
+ ShadeScrimBounds(left = 10f, top = 185f, right = 100f, bottom = 535f),
+ topRadius = 24,
+ bottomRadius = 0
+ )
+ )
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt
index dc928c4..50b77dc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt
@@ -68,11 +68,11 @@
kosmos.shadeRepository.setShadeMode(ShadeMode.Single)
assertThat(stackRounding)
- .isEqualTo(ShadeScrimRounding(roundTop = true, roundBottom = false))
+ .isEqualTo(ShadeScrimRounding(isTopRounded = true, isBottomRounded = false))
kosmos.shadeRepository.setShadeMode(ShadeMode.Split)
assertThat(stackRounding)
- .isEqualTo(ShadeScrimRounding(roundTop = true, roundBottom = true))
+ .isEqualTo(ShadeScrimRounding(isTopRounded = true, isBottomRounded = true))
}
@Test(expected = IllegalStateException::class)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
index d4a7049..1f0812d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
@@ -16,14 +16,10 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
-import android.platform.test.annotations.DisableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
@@ -40,23 +36,21 @@
private val underTest = kosmos.notificationsPlaceholderViewModel
@Test
- @DisableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun onBoundsChanged_setsNotificationContainerBounds() =
+ fun onBoundsChanged() =
kosmos.testScope.runTest {
- underTest.onBoundsChanged(left = 5f, top = 5f, right = 5f, bottom = 5f)
- val containerBounds by
- collectLastValue(kosmos.keyguardInteractor.notificationContainerBounds)
+ val bounds = ShadeScrimBounds(left = 5f, top = 15f, right = 25f, bottom = 35f)
+ underTest.onScrimBoundsChanged(bounds)
val stackBounds by
collectLastValue(kosmos.notificationStackAppearanceInteractor.shadeScrimBounds)
- assertThat(containerBounds)
- .isEqualTo(NotificationContainerBounds(top = 5f, bottom = 5f))
- assertThat(stackBounds)
- .isEqualTo(ShadeScrimBounds(left = 5f, top = 5f, right = 5f, bottom = 5f))
+ assertThat(stackBounds).isEqualTo(bounds)
}
@Test
- fun onContentTopChanged_setsContentTop() {
- underTest.onContentTopChanged(padding = 5f)
- assertThat(kosmos.notificationStackAppearanceInteractor.stackTop.value).isEqualTo(5f)
- }
+ fun onStackBoundsChanged() =
+ kosmos.testScope.runTest {
+ underTest.onStackBoundsChanged(top = 5f, bottom = 500f)
+ assertThat(kosmos.notificationStackAppearanceInteractor.stackTop.value).isEqualTo(5f)
+ assertThat(kosmos.notificationStackAppearanceInteractor.stackBottom.value)
+ .isEqualTo(500f)
+ }
}
diff --git a/packages/SystemUI/res/anim/slide_in_up.xml b/packages/SystemUI/res/anim/slide_in_up.xml
new file mode 100644
index 0000000..6089a28
--- /dev/null
+++ b/packages/SystemUI/res/anim/slide_in_up.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<translate
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromYDelta="100%p"
+ android:toYDelta="0"
+ android:duration="@android:integer/config_shortAnimTime" />
diff --git a/packages/SystemUI/res/anim/slide_out_down.xml b/packages/SystemUI/res/anim/slide_out_down.xml
new file mode 100644
index 0000000..5a7b591
--- /dev/null
+++ b/packages/SystemUI/res/anim/slide_out_down.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<translate
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromYDelta="0"
+ android:toYDelta="100%p"
+ android:duration="@android:integer/config_shortAnimTime" />
diff --git a/packages/SystemUI/res/drawable/ic_shortcutlist_search.xml b/packages/SystemUI/res/drawable/ic_shortcutlist_search.xml
index 1b12e74..0406f0e 100644
--- a/packages/SystemUI/res/drawable/ic_shortcutlist_search.xml
+++ b/packages/SystemUI/res/drawable/ic_shortcutlist_search.xml
@@ -14,12 +14,15 @@
limitations under the License
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="48"
android:viewportHeight="48"
- android:tint="?android:attr/textColorSecondary">
+ android:tint="?androidprv:attr/materialColorOnSurfaceVariant">
<path
android:fillColor="@android:color/white"
+ android:strokeColor="@android:color/white"
+ android:strokeWidth="2"
android:pathData="M39.8,41.95 L26.65,28.8Q25.15,30.1 23.15,30.825Q21.15,31.55 18.9,31.55Q13.5,31.55 9.75,27.8Q6,24.05 6,18.75Q6,13.45 9.75,9.7Q13.5,5.95 18.85,5.95Q24.15,5.95 27.875,9.7Q31.6,13.45 31.6,18.75Q31.6,20.9 30.9,22.9Q30.2,24.9 28.8,26.65L42,39.75ZM18.85,28.55Q22.9,28.55 25.75,25.675Q28.6,22.8 28.6,18.75Q28.6,14.7 25.75,11.825Q22.9,8.95 18.85,8.95Q14.75,8.95 11.875,11.825Q9,14.7 9,18.75Q9,22.8 11.875,25.675Q14.75,28.55 18.85,28.55Z"/>
-</vector>
\ No newline at end of file
+</vector>
diff --git a/packages/SystemUI/res/drawable/shortcut_button_colored.xml b/packages/SystemUI/res/drawable/shortcut_button_colored.xml
index bf90853..2e2d9b9 100644
--- a/packages/SystemUI/res/drawable/shortcut_button_colored.xml
+++ b/packages/SystemUI/res/drawable/shortcut_button_colored.xml
@@ -21,7 +21,7 @@
android:color="?android:attr/colorControlHighlight">
<item>
<shape android:shape="rectangle">
- <corners android:radius="16dp"/>
+ <corners android:radius="@dimen/ksh_button_corner_radius"/>
<solid android:color="?androidprv:attr/materialColorSurfaceBright"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/shortcut_button_focus_colored.xml b/packages/SystemUI/res/drawable/shortcut_button_focus_colored.xml
index f692ed97..5b88bb9 100644
--- a/packages/SystemUI/res/drawable/shortcut_button_focus_colored.xml
+++ b/packages/SystemUI/res/drawable/shortcut_button_focus_colored.xml
@@ -21,7 +21,7 @@
android:color="?android:attr/colorControlHighlight">
<item>
<shape android:shape="rectangle">
- <corners android:radius="16dp"/>
+ <corners android:radius="@dimen/ksh_button_corner_radius"/>
<solid android:color="?androidprv:attr/materialColorPrimary"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/shortcut_dialog_bg.xml b/packages/SystemUI/res/drawable/shortcut_dialog_bg.xml
index 6ce3eae..aa0b268 100644
--- a/packages/SystemUI/res/drawable/shortcut_dialog_bg.xml
+++ b/packages/SystemUI/res/drawable/shortcut_dialog_bg.xml
@@ -17,8 +17,8 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="?android:attr/colorBackground"/>
- <corners android:topLeftRadius="16dp"
- android:topRightRadius="16dp"
+ <corners android:topLeftRadius="@dimen/ksh_dialog_top_corner_radius"
+ android:topRightRadius="@dimen/ksh_dialog_top_corner_radius"
android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp"/>
-</shape>
\ No newline at end of file
+</shape>
diff --git a/packages/SystemUI/res/drawable/shortcut_search_background.xml b/packages/SystemUI/res/drawable/shortcut_search_background.xml
index 66fc191..d6847f0 100644
--- a/packages/SystemUI/res/drawable/shortcut_search_background.xml
+++ b/packages/SystemUI/res/drawable/shortcut_search_background.xml
@@ -19,8 +19,8 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item>
<shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners android:radius="24dp" />
+ <solid android:color="?androidprv:attr/materialColorSurfaceBright" />
+ <corners android:radius="@dimen/ksh_search_box_corner_radius" />
</shape>
</item>
-</layer-list>
\ No newline at end of file
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/shortcut_search_cancel_button.xml b/packages/SystemUI/res/drawable/shortcut_search_cancel_button.xml
index 6c4d4fb..2675906 100644
--- a/packages/SystemUI/res/drawable/shortcut_search_cancel_button.xml
+++ b/packages/SystemUI/res/drawable/shortcut_search_cancel_button.xml
@@ -20,7 +20,7 @@
<shape android:shape="oval">
<size android:width="24dp"
android:height="24dp" />
- <solid android:color="?androidprv:attr/colorSurface"/>
+ <solid android:color="?androidprv:attr/materialColorSurfaceBright"/>
</shape>
</item>
</ripple>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
index a005100..5ab2327 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
@@ -15,13 +15,14 @@
~ limitations under the License
-->
<com.android.systemui.statusbar.KeyboardShortcutAppItemLayout
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:background="@drawable/list_item_background"
android:focusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="48dp"
+ android:minHeight="@dimen/ksh_app_item_minimum_height"
android:paddingBottom="8dp">
<ImageView
android:id="@+id/keyboard_shortcuts_icon"
@@ -39,7 +40,8 @@
android:layout_height="wrap_content"
android:paddingEnd="12dp"
android:paddingBottom="4dp"
- android:textColor="?android:attr/textColorPrimary"
+ android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="16sp"
android:maxLines="5"
android:singleLine="false"
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_short_separator.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_short_separator.xml
index 530e46e..76e5b12 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_category_short_separator.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_short_separator.xml
@@ -15,6 +15,6 @@
limitations under the License
-->
<View xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_marginTop="8dp"
- android:layout_marginBottom="0dp"
+ android:layout_marginTop="@dimen/ksh_category_separator_margin"
+ android:layout_marginBottom="@dimen/ksh_category_separator_margin"
style="@style/ShortcutHorizontalDivider" />
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
index 4f100f6..6e7fde6 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
@@ -16,10 +16,12 @@
~ limitations under the License
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="14sp"
- android:fontFamily="sans-serif-medium"
+ android:textColor="?androidprv:attr/materialColorPrimary"
android:importantForAccessibility="yes"
android:paddingTop="20dp"
android:paddingBottom="10dp"/>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
index f6042e4..2cfd644 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
@@ -16,15 +16,21 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:background="@drawable/shortcut_dialog_bg"
android:layout_width="@dimen/ksh_layout_width"
android:layout_height="wrap_content"
android:orientation="vertical">
+
+ <com.google.android.material.bottomsheet.BottomSheetDragHandleView
+ android:id="@+id/drag_handle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
<TextView
android:id="@+id/shortcut_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="40dp"
android:layout_gravity="center_horizontal"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="?android:attr/textColorPrimary"
@@ -39,44 +45,47 @@
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="24dp"
- android:layout_marginStart="49dp"
- android:layout_marginEnd="49dp"
+ android:layout_marginStart="@dimen/ksh_container_horizontal_margin"
+ android:layout_marginEnd="@dimen/ksh_container_horizontal_margin"
android:padding="16dp"
android:background="@drawable/shortcut_search_background"
android:drawableStart="@drawable/ic_shortcutlist_search"
android:drawablePadding="15dp"
android:singleLine="true"
- android:textColor="?android:attr/textColorPrimary"
+ android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
android:inputType="text"
android:textDirection="locale"
android:textAlignment="viewStart"
android:hint="@string/keyboard_shortcut_search_list_hint"
- android:textColorHint="?android:attr/textColorTertiary" />
+ android:textAppearance="@android:style/TextAppearance.Material"
+ android:textSize="16sp"
+ android:textColorHint="?androidprv:attr/materialColorOutline" />
<ImageButton
android:id="@+id/keyboard_shortcuts_search_cancel"
android:layout_gravity="center_vertical|end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginEnd="49dp"
+ android:layout_marginEnd="@dimen/ksh_container_horizontal_margin"
android:padding="16dp"
android:contentDescription="@string/keyboard_shortcut_clear_text"
android:src="@drawable/ic_shortcutlist_search_button_cancel"
android:background="@drawable/shortcut_search_cancel_button"
style="@android:style/Widget.Material.Button.Borderless.Small"
- android:pointerIcon="arrow" />
+ android:pointerIcon="arrow"
+ android:visibility="gone" />
</FrameLayout>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginStart="49dp"
+ android:layout_marginStart="@dimen/ksh_container_horizontal_margin"
android:layout_marginEnd="0dp"
android:scrollbars="none">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="center_vertical"
+ android:layout_gravity="center_vertical"
android:orientation="horizontal">
<Button
android:id="@+id/shortcut_system"
@@ -113,29 +122,29 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
- android:layout_marginStart="49dp"
- android:layout_marginEnd="49dp"
+ android:layout_marginStart="@dimen/ksh_container_horizontal_margin"
+ android:layout_marginEnd="@dimen/ksh_container_horizontal_margin"
android:layout_gravity="center_horizontal"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/keyboard_shortcut_search_list_no_result"/>
- <ScrollView
+ <androidx.core.widget.NestedScrollView
android:id="@+id/keyboard_shortcuts_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
- android:layout_marginStart="49dp"
- android:layout_marginEnd="49dp"
+ android:layout_marginStart="@dimen/ksh_container_horizontal_margin"
+ android:layout_marginEnd="@dimen/ksh_container_horizontal_margin"
android:overScrollMode="never"
- android:layout_marginBottom="16dp"
+ android:clipToPadding="false"
android:scrollbars="none">
<LinearLayout
android:id="@+id/keyboard_shortcuts_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
- </ScrollView>
+ </androidx.core.widget.NestedScrollView>
<!-- Required for stretching to full available height when the items in the scroll view
occupy less space then the full height -->
<View
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 55606aa..56ebc06 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -94,4 +94,7 @@
<dimen name="keyguard_indication_margin_bottom">8dp</dimen>
<dimen name="lock_icon_margin_bottom">24dp</dimen>
+
+ <!-- Keyboard shortcuts helper -->
+ <dimen name="ksh_container_horizontal_margin">48dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f2288a4..b95ee56 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -978,6 +978,7 @@
<dimen name="assist_disclosure_shadow_thickness">1.5dp</dimen>
<!-- Keyboard shortcuts helper -->
+ <dimen name="ksh_container_horizontal_margin">32dp</dimen>
<dimen name="ksh_layout_width">@dimen/match_parent</dimen>
<dimen name="ksh_item_text_size">14sp</dimen>
<dimen name="ksh_item_padding">0dp</dimen>
@@ -985,6 +986,11 @@
<dimen name="ksh_icon_scaled_size">18dp</dimen>
<dimen name="ksh_key_view_padding_vertical">4dp</dimen>
<dimen name="ksh_key_view_padding_horizontal">12dp</dimen>
+ <dimen name="ksh_button_corner_radius">12dp</dimen>
+ <dimen name="ksh_dialog_top_corner_radius">28dp</dimen>
+ <dimen name="ksh_search_box_corner_radius">100dp</dimen>
+ <dimen name="ksh_app_item_minimum_height">64dp</dimen>
+ <dimen name="ksh_category_separator_margin">16dp</dimen>
<!-- The size of corner radius of the arrow in the onboarding toast. -->
<dimen name="recents_onboarding_toast_arrow_corner_radius">2dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e825f32..9da4f79 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -366,6 +366,21 @@
<item name="android:layout_height">wrap_content</item>
</style>
+ <style name="KeyboardShortcutHelper" parent="@android:style/Theme.DeviceDefault.Settings">
+ <!-- Needed to be able to use BottomSheetDragHandleView -->
+ <item name="android:windowActionBar">false</item>
+ <item name="bottomSheetDragHandleStyle">@style/KeyboardShortcutHelper.BottomSheet.DragHandle</item>
+ </style>
+
+ <style name="KeyboardShortcutHelper.BottomSheet.DragHandle" parent="Widget.Material3.BottomSheet.DragHandle">
+ <item name="tint">?androidprv:attr/materialColorOutlineVariant</item>
+ </style>
+
+ <style name="KeyboardShortcutHelper.BottomSheetDialogAnimation">
+ <item name="android:windowEnterAnimation">@anim/slide_in_up</item>
+ <item name="android:windowExitAnimation">@anim/slide_out_down</item>
+ </style>
+
<style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer" />
<style name="Animation" />
@@ -1598,14 +1613,15 @@
<item name="android:layout_marginEnd">12dp</item>
<item name="android:paddingLeft">24dp</item>
<item name="android:paddingRight">24dp</item>
- <item name="android:minHeight">40dp</item>
+ <item name="android:minHeight">36dp</item>
+ <item name="android:minWidth">120dp</item>
<item name="android:stateListAnimator">@*android:anim/flat_button_state_list_anim_material</item>
<item name="android:pointerIcon">arrow</item>
</style>
<style name="ShortcutHorizontalDivider">
- <item name="android:layout_width">120dp</item>
- <item name="android:layout_height">1dp</item>
+ <item name="android:layout_width">132dp</item>
+ <item name="android:layout_height">2dp</item>
<item name="android:layout_gravity">center_horizontal</item>
<item name="android:background">?android:attr/dividerHorizontal</item>
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 4632914..dcc1440 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -155,5 +155,10 @@
*/
oneway void animateNavBarLongPress(boolean isTouchDown, boolean shrink, long durationMs) = 54;
- // Next id = 55
+ /**
+ * Set the override value for home button long press duration in ms and slop multiplier.
+ */
+ oneway void setOverrideHomeButtonLongPress(long duration, float slopMultiplier) = 55;
+
+ // Next id = 56
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 9d79e87..b25c3da 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -403,6 +403,8 @@
if (DeviceEntryUdfpsRefactor.isEnabled()) {
if (mOverlay != null && mOverlay.getRequestReason() == REASON_AUTH_KEYGUARD) {
mOverlay.updateOverlayParams(mOverlayParams);
+ } else {
+ redrawOverlay();
}
} else {
final boolean wasShowingAlternateBouncer =
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/ViewExt.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/ViewExt.kt
index ce798ba..7dd883b 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/ViewExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/ViewExt.kt
@@ -17,6 +17,7 @@
package com.android.systemui.common.ui.view
import android.view.View
+import kotlinx.coroutines.DisposableHandle
/**
* Set this view's [View#importantForAccessibility] to [View#IMPORTANT_FOR_ACCESSIBILITY_YES] or
@@ -43,3 +44,10 @@
}
return null
}
+
+/** Adds a [View.OnLayoutChangeListener] and provides a [DisposableHandle] for teardown. */
+fun View.onLayoutChanged(onLayoutChanged: (v: View) -> Unit): DisposableHandle {
+ val listener = View.OnLayoutChangeListener { v, _, _, _, _, _, _, _, _ -> onLayoutChanged(v) }
+ addOnLayoutChangeListener(listener)
+ return DisposableHandle { removeOnLayoutChangeListener(listener) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 9f7e0d4..e6e6ff6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -208,7 +208,7 @@
chipbarCoordinator,
screenOffAnimationController,
shadeInteractor,
- { keyguardStatusViewController!!.getClockController() },
+ clockInteractor,
interactionJankMonitor,
deviceEntryHapticsInteractor,
vibratorHelper,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
index 99b691e..d551c9b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
@@ -17,7 +17,9 @@
package com.android.systemui.keyguard.domain.interactor
+import android.util.Log
import com.android.keyguard.ClockEventController
+import com.android.keyguard.KeyguardClockSwitch
import com.android.keyguard.KeyguardClockSwitch.ClockSize
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardClockRepository
@@ -54,6 +56,15 @@
keyguardClockRepository.setClockSize(size)
}
+ val renderedClockId: ClockId
+ get() {
+ return clock?.let { clock -> clock.config.id }
+ ?: run {
+ Log.e(TAG, "No clock is available")
+ KeyguardClockSwitch.MISSING_CLOCK_ID
+ }
+ }
+
fun animateFoldToAod(foldFraction: Float) {
clock?.let { clock ->
clock.smallClock.animations.fold(foldFraction)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index 68ea5d0..141cca3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -21,6 +21,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.core.LogLevel.VERBOSE
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import javax.inject.Inject
@@ -69,9 +70,11 @@
}
}
- scope.launch {
- sharedNotificationContainerViewModel.bounds.collect {
- logger.log(TAG, VERBOSE, "Notif: bounds", it)
+ if (!SceneContainerFlag.isEnabled) {
+ scope.launch {
+ sharedNotificationContainerViewModel.bounds.collect {
+ logger.log(TAG, VERBOSE, "Notif: bounds", it)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 5f50f7e..0249abd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -36,7 +36,6 @@
import com.android.app.tracing.coroutines.launch
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
-import com.android.keyguard.KeyguardClockSwitch.MISSING_CLOCK_ID
import com.android.systemui.Flags.newAodTransition
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
@@ -47,6 +46,7 @@
import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.MigrateClocksToBlueprint
+import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
@@ -55,7 +55,6 @@
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.CrossFadeHelper
@@ -69,7 +68,6 @@
import com.android.systemui.util.ui.isAnimating
import com.android.systemui.util.ui.stopAnimating
import com.android.systemui.util.ui.value
-import javax.inject.Provider
import kotlin.math.min
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
@@ -93,7 +91,7 @@
chipbarCoordinator: ChipbarCoordinator,
screenOffAnimationController: ScreenOffAnimationController,
shadeInteractor: ShadeInteractor,
- clockControllerProvider: Provider<ClockController>?,
+ clockInteractor: KeyguardClockInteractor,
interactionJankMonitor: InteractionJankMonitor?,
deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor?,
vibratorHelper: VibratorHelper?,
@@ -281,14 +279,11 @@
viewModel.goneToAodTransition.collect {
when (it.transitionState) {
TransitionState.STARTED -> {
- val clockId =
- clockControllerProvider?.get()?.config?.id
- ?: MISSING_CLOCK_ID
+ val clockId = clockInteractor.renderedClockId
val builder =
InteractionJankMonitor.Configuration.Builder
.withView(CUJ_SCREEN_OFF_SHOW_AOD, view)
.setTag(clockId)
-
jankMonitor.begin(builder)
}
TransitionState.CANCELED ->
@@ -345,12 +340,6 @@
}
}
- if (!MigrateClocksToBlueprint.isEnabled) {
- burnInParams.update { current ->
- current.copy(clockControllerProvider = clockControllerProvider)
- }
- }
-
if (MigrateClocksToBlueprint.isEnabled) {
burnInParams.update { current ->
current.copy(translationY = { childViews[burnInLayerId]?.translationY })
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 9195b4f..39df4c3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -60,6 +60,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
import com.android.systemui.keyguard.MigrateClocksToBlueprint
+import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
@@ -140,6 +141,7 @@
private val secureSettings: SecureSettings,
private val communalTutorialViewModel: CommunalTutorialIndicatorViewModel,
private val defaultShortcutsSection: DefaultShortcutsSection,
+ private val keyguardClockInteractor: KeyguardClockInteractor,
) {
val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
private val width: Int = bundle.getInt(KEY_VIEW_WIDTH)
@@ -364,6 +366,7 @@
),
)
}
+
@OptIn(ExperimentalCoroutinesApi::class)
private fun setupKeyguardRootView(previewContext: Context, rootView: FrameLayout) {
val keyguardRootView = KeyguardRootView(previewContext, null)
@@ -377,7 +380,7 @@
chipbarCoordinator,
screenOffAnimationController,
shadeInteractor,
- null, // clock provider only needed for burn in
+ keyguardClockInteractor,
null, // jank monitor not required for preview mode
null, // device entry haptics not required preview mode
null, // device entry haptics not required for preview mode
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index 2054932..4ddd5711 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -33,10 +33,8 @@
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.ui.StateToValue
-import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.res.R
import javax.inject.Inject
-import javax.inject.Provider
import kotlin.math.max
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -128,12 +126,12 @@
yDimenResourceId = R.dimen.burn_in_prevention_offset_y
),
) { interpolated, burnIn ->
+ val useAltAod =
+ keyguardClockViewModel.currentClock.value?.let { clock ->
+ clock.config.useAlternateSmartspaceAODTransition
+ } == true
val useScaleOnly =
- (clockController(params.clockControllerProvider)
- ?.get()
- ?.config
- ?.useAlternateSmartspaceAODTransition
- ?: false) && keyguardClockViewModel.clockSize.value == KeyguardClockSwitch.LARGE
+ useAltAod && keyguardClockViewModel.clockSize.value == KeyguardClockSwitch.LARGE
if (useScaleOnly) {
BurnInModel(
@@ -164,21 +162,10 @@
}
}
}
-
- private fun clockController(
- provider: Provider<ClockController>?,
- ): Provider<ClockController>? {
- return if (MigrateClocksToBlueprint.isEnabled) {
- Provider { keyguardClockViewModel.currentClock.value }
- } else {
- provider
- }
- }
}
/** UI-sourced parameters to pass into the various methods of [AodBurnInViewModel]. */
data class BurnInParameters(
- val clockControllerProvider: Provider<ClockController>? = null,
/** System insets that keyguard needs to stay out of */
val topInset: Int = 0,
/** The min y-value of the visible elements on lockscreen */
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index e861ddf..da9e00d 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -16,11 +16,8 @@
package com.android.systemui.mediaprojection.permission;
-import static android.Manifest.permission.LOG_COMPAT_CHANGE;
-import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
import static android.media.projection.IMediaProjectionManager.EXTRA_PACKAGE_REUSING_GRANTED_CONSENT;
import static android.media.projection.IMediaProjectionManager.EXTRA_USER_REVIEW_GRANTED_CONSENT;
-import static android.media.projection.MediaProjectionManager.OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
@@ -29,13 +26,11 @@
import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.SINGLE_APP;
import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions.LaunchCookie;
import android.app.AlertDialog;
import android.app.StatusBarManager;
-import android.app.compat.CompatChanges;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -113,7 +108,6 @@
}
@Override
- @RequiresPermission(allOf = {READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE})
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -241,10 +235,6 @@
// the correct screen width when in split screen.
Context dialogContext = getApplicationContext();
if (isPartialScreenSharingEnabled()) {
- final boolean overrideDisableSingleAppOption =
- CompatChanges.isChangeEnabled(
- OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION,
- mPackageName, getHostUserHandle());
MediaProjectionPermissionDialogDelegate delegate =
new MediaProjectionPermissionDialogDelegate(
dialogContext,
@@ -256,7 +246,6 @@
},
() -> finish(RECORD_CANCEL, /* projection= */ null),
appName,
- overrideDisableSingleAppOption,
mUid,
mMediaProjectionMetricsLogger);
mDialog =
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
index 8858041a..0f54e93 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
@@ -30,12 +30,11 @@
private val onStartRecordingClicked: Consumer<MediaProjectionPermissionDialogDelegate>,
private val onCancelClicked: Runnable,
private val appName: String?,
- private val forceShowPartialScreenshare: Boolean,
hostUid: Int,
mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
) :
BaseMediaProjectionPermissionDialogDelegate<AlertDialog>(
- createOptionList(context, appName, mediaProjectionConfig, forceShowPartialScreenshare),
+ createOptionList(context, appName, mediaProjectionConfig),
appName,
hostUid,
mediaProjectionMetricsLogger
@@ -66,8 +65,7 @@
private fun createOptionList(
context: Context,
appName: String?,
- mediaProjectionConfig: MediaProjectionConfig?,
- overrideDisableSingleAppOption: Boolean = false,
+ mediaProjectionConfig: MediaProjectionConfig?
): List<ScreenShareOption> {
val singleAppWarningText =
if (appName == null) {
@@ -82,13 +80,8 @@
R.string.media_projection_entry_app_permission_dialog_warning_entire_screen
}
- // The single app option should only be disabled if there is an app name provided,
- // the client has setup a MediaProjection with
- // MediaProjectionConfig#createConfigForDefaultDisplay, AND it hasn't been overridden by
- // the OVERRIDE_DISABLE_SINGLE_APP_OPTION per-app override.
val singleAppOptionDisabled =
appName != null &&
- !overrideDisableSingleAppOption &&
mediaProjectionConfig?.regionToCapture ==
MediaProjectionConfig.CAPTURE_REGION_FIXED_DISPLAY
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 4fe3a11..ade56c4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -85,6 +85,7 @@
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.InternalInsetsInfo;
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
@@ -186,6 +187,7 @@
/** Allow some time inbetween the long press for back and recents. */
private static final int LOCK_TO_APP_GESTURE_TOLERANCE = 200;
private static final long AUTODIM_TIMEOUT_MS = 2250;
+ private static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 3f;
private final Context mContext;
private final Bundle mSavedState;
@@ -223,6 +225,7 @@
private final int mNavColorSampleMargin;
private EdgeBackGestureHandler mEdgeBackGestureHandler;
private NavigationBarFrame mFrame;
+ private MotionEvent mCurrentDownEvent;
private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
@@ -238,6 +241,8 @@
private int mLayoutDirection;
private Optional<Long> mHomeButtonLongPressDurationMs;
+ private Optional<Long> mOverrideHomeButtonLongPressDurationMs = Optional.empty();
+ private Optional<Float> mOverrideHomeButtonLongPressSlopMultiplier = Optional.empty();
/** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
private @Appearance int mAppearance;
@@ -405,6 +410,25 @@
}
@Override
+ public void setOverrideHomeButtonLongPress(long duration, float slopMultiplier) {
+ mOverrideHomeButtonLongPressDurationMs = Optional.of(duration)
+ .filter(value -> value > 0);
+ mOverrideHomeButtonLongPressSlopMultiplier = Optional.of(slopMultiplier)
+ .filter(value -> value > 0);
+ if (mOverrideHomeButtonLongPressDurationMs.isPresent()) {
+ Log.d(TAG, "Receive duration override: "
+ + mOverrideHomeButtonLongPressDurationMs.get());
+ }
+ if (mOverrideHomeButtonLongPressSlopMultiplier.isPresent()) {
+ Log.d(TAG, "Receive slop multiplier override: "
+ + mOverrideHomeButtonLongPressSlopMultiplier.get());
+ }
+ if (mView != null) {
+ reconfigureHomeLongClick();
+ }
+ }
+
+ @Override
public void onHomeRotationEnabled(boolean enabled) {
mView.getRotationButtonController().setHomeRotationEnabled(enabled);
}
@@ -1016,7 +1040,10 @@
if (mView.getHomeButton().getCurrentView() == null) {
return;
}
- if (mHomeButtonLongPressDurationMs.isPresent() || !mLongPressHomeEnabled) {
+ if (mHomeButtonLongPressDurationMs.isPresent()
+ || mOverrideHomeButtonLongPressDurationMs.isPresent()
+ || mOverrideHomeButtonLongPressSlopMultiplier.isPresent()
+ || !mLongPressHomeEnabled) {
mView.getHomeButton().getCurrentView().setLongClickable(false);
mView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false);
mView.getHomeButton().setOnLongClickListener(null);
@@ -1038,6 +1065,10 @@
pw.println(" mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation);
pw.println(" mCurrentRotation=" + mCurrentRotation);
pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs);
+ pw.println(" mOverrideHomeButtonLongPressDurationMs="
+ + mOverrideHomeButtonLongPressDurationMs);
+ pw.println(" mOverrideHomeButtonLongPressSlopMultiplier="
+ + mOverrideHomeButtonLongPressSlopMultiplier);
pw.println(" mLongPressHomeEnabled=" + mLongPressHomeEnabled);
pw.println(" mNavigationBarWindowState="
+ windowStateToString(mNavigationBarWindowState));
@@ -1331,6 +1362,10 @@
final Optional<CentralSurfaces> centralSurfacesOptional = mCentralSurfacesOptionalLazy.get();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
+ if (mCurrentDownEvent != null) {
+ mCurrentDownEvent.recycle();
+ }
+ mCurrentDownEvent = MotionEvent.obtain(event);
mHomeBlockedThisTouch = false;
if (mTelecomManagerOptional.isPresent()
&& mTelecomManagerOptional.get().isRinging()) {
@@ -1342,9 +1377,45 @@
}
}
if (mLongPressHomeEnabled) {
- mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> {
- mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration);
- });
+ if (mOverrideHomeButtonLongPressDurationMs.isPresent()) {
+ Log.d(TAG, "ACTION_DOWN Launcher override duration: "
+ + mOverrideHomeButtonLongPressDurationMs.get());
+ mHandler.postDelayed(mOnVariableDurationHomeLongClick,
+ mOverrideHomeButtonLongPressDurationMs.get());
+ } else if (mOverrideHomeButtonLongPressSlopMultiplier.isPresent()) {
+ // If override timeout doesn't exist but override touch slop exists, we use
+ // system default long press duration
+ Log.d(TAG, "ACTION_DOWN default duration: "
+ + ViewConfiguration.getLongPressTimeout());
+ mHandler.postDelayed(mOnVariableDurationHomeLongClick,
+ ViewConfiguration.getLongPressTimeout());
+ } else {
+ mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> {
+ Log.d(TAG, "ACTION_DOWN original duration: " + longPressDuration);
+ mHandler.postDelayed(mOnVariableDurationHomeLongClick,
+ longPressDuration);
+ });
+ }
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (!mHandler.hasCallbacks(mOnVariableDurationHomeLongClick)) {
+ Log.w(TAG, "No callback. Don't handle touch slop.");
+ break;
+ }
+ float customSlopMultiplier = mOverrideHomeButtonLongPressSlopMultiplier.orElse(1f);
+ float touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ float calculatedTouchSlop =
+ customSlopMultiplier * QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON * touchSlop;
+ float touchSlopSquared = calculatedTouchSlop * calculatedTouchSlop;
+
+ float dx = event.getX() - mCurrentDownEvent.getX();
+ float dy = event.getY() - mCurrentDownEvent.getY();
+ double distanceSquared = (dx * dx) + (dy * dy);
+ if (distanceSquared > touchSlopSquared) {
+ Log.i(TAG, "Touch slop passed. Abort.");
+ mView.abortCurrentGesture();
+ mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
}
break;
case MotionEvent.ACTION_UP:
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 7c1a2c0..f621f11 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -259,6 +259,12 @@
}
@Override
+ public void setOverrideHomeButtonLongPress(long duration, float slopMultiplier) {
+ verifyCallerAndClearCallingIdentityPostMain("setOverrideHomeButtonLongPress",
+ () -> notifySetOverrideHomeButtonLongPress(duration, slopMultiplier));
+ }
+
+ @Override
public void onBackPressed() {
verifyCallerAndClearCallingIdentityPostMain("onBackPressed", () -> {
sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
@@ -947,6 +953,12 @@
}
}
+ private void notifySetOverrideHomeButtonLongPress(long duration, float slopMultiplier) {
+ for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+ mConnectionCallbacks.get(i).setOverrideHomeButtonLongPress(duration, slopMultiplier);
+ }
+ }
+
public void notifyAssistantVisibilityChanged(float visibility) {
try {
if (mOverviewProxy != null) {
@@ -1104,6 +1116,8 @@
default void startAssistant(Bundle bundle) {}
default void setAssistantOverridesRequested(int[] invocationTypes) {}
default void animateNavBarLongPress(boolean isTouchDown, boolean shrink, long durationMs) {}
+ /** Set override of home button long press duration and touch slop multiplier. */
+ default void setOverrideHomeButtonLongPress(long override, float slopMultiplier) {}
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
index c42fdf8..b24edd9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
@@ -25,6 +25,7 @@
import android.graphics.drawable.Drawable;
import com.android.keyguard.LockIconViewController;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import java.util.HashSet;
@@ -80,12 +81,14 @@
mNotificationPanelViewController.getClockPositionResult()
.stackScrollerPadding),
Color.YELLOW, "calculatePanelHeightShade()");
- drawDebugInfo(canvas,
- (int) mQsController.calculateNotificationsTopPadding(
- mNotificationPanelViewController.isExpandingOrCollapsing(),
- mNotificationPanelViewController.getKeyguardNotificationStaticPadding(),
- mNotificationPanelViewController.getExpandedFraction()),
- Color.MAGENTA, "calculateNotificationsTopPadding()");
+ if (!SceneContainerFlag.isEnabled()) {
+ drawDebugInfo(canvas,
+ (int) mQsController.calculateNotificationsTopPadding(
+ mNotificationPanelViewController.isExpandingOrCollapsing(),
+ mNotificationPanelViewController.getKeyguardNotificationStaticPadding(),
+ mNotificationPanelViewController.getExpandedFraction()),
+ Color.MAGENTA, "calculateNotificationsTopPadding()");
+ }
drawDebugInfo(canvas, mNotificationPanelViewController.getClockPositionResult().clockY,
Color.GRAY, "mClockPositionResult.clockY");
drawDebugInfo(canvas, (int) mLockIconViewController.getTop(), Color.GRAY,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 343f377..4660831 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1588,7 +1588,8 @@
* @param forceClockUpdate Should the clock be updated even when not on keyguard
*/
private void positionClockAndNotifications(boolean forceClockUpdate) {
- boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
+ boolean animate = !SceneContainerFlag.isEnabled()
+ && mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
int stackScrollerPadding;
boolean onKeyguard = isKeyguardShowing();
@@ -1675,7 +1676,8 @@
mClockPositionResult.clockX, mClockPositionResult.clockY);
}
- boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
+ boolean animate = !SceneContainerFlag.isEnabled()
+ && mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
boolean animateClock = (animate || mAnimateNextPositionUpdate) && shouldAnimateClockChange;
if (!MigrateClocksToBlueprint.isEnabled()) {
@@ -2483,6 +2485,7 @@
/** Returns the topPadding of notifications when on keyguard not respecting QS expansion. */
int getKeyguardNotificationStaticPadding() {
+ SceneContainerFlag.assertInLegacyMode();
if (!isKeyguardShowing()) {
return 0;
}
@@ -2524,12 +2527,14 @@
}
void requestScrollerTopPaddingUpdate(boolean animate) {
- float padding = mQsController.calculateNotificationsTopPadding(mIsExpandingOrCollapsing,
- getKeyguardNotificationStaticPadding(), mExpandedFraction);
- if (MigrateClocksToBlueprint.isEnabled()) {
- mSharedNotificationContainerInteractor.setTopPosition(padding);
- } else {
- mNotificationStackScrollLayoutController.updateTopPadding(padding, animate);
+ if (!SceneContainerFlag.isEnabled()) {
+ float padding = mQsController.calculateNotificationsTopPadding(mIsExpandingOrCollapsing,
+ getKeyguardNotificationStaticPadding(), mExpandedFraction);
+ if (MigrateClocksToBlueprint.isEnabled()) {
+ mSharedNotificationContainerInteractor.setTopPosition(padding);
+ } else {
+ mNotificationStackScrollLayoutController.updateTopPadding(padding, animate);
+ }
}
if (isKeyguardShowing()
@@ -3174,6 +3179,7 @@
}
notifyExpandingFinished();
}
+ // TODO(b/332732878): replace this call when scene container is enabled
mNotificationStackScrollLayoutController.setAnimationsEnabled(!disabled);
}
@@ -3963,7 +3969,9 @@
mShadeRepository.setLegacyShadeExpansion(mExpandedFraction);
mQsController.setShadeExpansion(mExpandedHeight, mExpandedFraction);
mExpansionDragDownAmountPx = h;
- mAmbientState.setExpansionFraction(mExpandedFraction);
+ if (!SceneContainerFlag.isEnabled()) {
+ mAmbientState.setExpansionFraction(mExpandedFraction);
+ }
onHeightUpdated(mExpandedHeight);
updateExpansionAndVisibility();
});
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index 35b4059..2507507 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -1371,6 +1371,7 @@
@Override
public float calculateNotificationsTopPadding(boolean isShadeExpanding,
int keyguardNotificationStaticPadding, float expandedFraction) {
+ SceneContainerFlag.assertInLegacyMode();
float topPadding;
boolean keyguardShowing = mBarState == KEYGUARD;
if (mSplitShadeEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurfaceImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurfaceImpl.kt
index adb2928..ec4018c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurfaceImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurfaceImpl.kt
@@ -46,7 +46,7 @@
}
override fun setTouchAndAnimationDisabled(disabled: Boolean) {
- // TODO(b/322197941): determine if still needed
+ // TODO(b/332732878): determine if still needed
}
override fun setWillPlayDelayedDozeAmountAnimation(willPlay: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 2cb9f9a..29cdf0a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -43,6 +43,7 @@
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.NotificationInsetsController
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
import com.android.systemui.statusbar.phone.StatusBarLocation
@@ -51,6 +52,7 @@
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 javax.inject.Named
@@ -59,6 +61,14 @@
/** Module for providing views related to the shade. */
@Module
abstract class ShadeViewProviderModule {
+
+ @Binds
+ @SysUISingleton
+ // TODO(b/277762009): Only allow this view's binder to inject the view.
+ abstract fun bindsNotificationScrollView(
+ notificationStackScrollLayout: NotificationStackScrollLayout
+ ): NotificationScrollView
+
companion object {
const val SHADE_HEADER = "large_screen_shade_header"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index d6858ca..78e108d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -40,6 +40,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.text.Editable;
+import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.util.Pair;
@@ -57,6 +58,7 @@
import android.view.View.AccessibilityDelegate;
import android.view.ViewGroup;
import android.view.Window;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Button;
@@ -104,6 +106,7 @@
private WindowManager mWindowManager;
private EditText mSearchEditText;
+ private ImageButton mEditTextCancel;
private String mQueryString;
private int mCurrentCategoryIndex = 0;
private Map<Integer, Boolean> mKeySearchResultMap = new HashMap<>();
@@ -143,7 +146,7 @@
@VisibleForTesting
KeyboardShortcutListSearch(Context context, WindowManager windowManager) {
this.mContext = new ContextThemeWrapper(
- context, android.R.style.Theme_DeviceDefault_Settings);
+ context, R.style.KeyboardShortcutHelper);
this.mPackageManager = AppGlobals.getPackageManager();
if (windowManager != null) {
this.mWindowManager = windowManager;
@@ -853,13 +856,14 @@
List<List<KeyboardShortcutMultiMappingGroup>> keyboardShortcutMultiMappingGroupList) {
mQueryString = null;
LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class);
- mKeyboardShortcutsBottomSheetDialog =
- new BottomSheetDialog(mContext);
+ mKeyboardShortcutsBottomSheetDialog = new BottomSheetDialog(mContext);
final View keyboardShortcutsView = inflater.inflate(
R.layout.keyboard_shortcuts_search_view, null);
LinearLayout shortcutsContainer = keyboardShortcutsView.findViewById(
R.id.keyboard_shortcuts_container);
mNoSearchResults = keyboardShortcutsView.findViewById(R.id.shortcut_search_no_result);
+ Window keyboardShortcutsWindow = mKeyboardShortcutsBottomSheetDialog.getWindow();
+ setWindowProperties(keyboardShortcutsWindow);
mKeyboardShortcutsBottomSheetDialog.setContentView(keyboardShortcutsView);
setButtonsDefaultStatus(keyboardShortcutsView);
populateCurrentAppButton();
@@ -874,25 +878,11 @@
}
BottomSheetBehavior<FrameLayout> behavior = BottomSheetBehavior.from(bottomSheet);
+ behavior.setDraggable(true);
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
behavior.setSkipCollapsed(true);
- behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
- @Override
- public void onStateChanged(@NonNull View bottomSheet, int newState) {
- if (newState == BottomSheetBehavior.STATE_DRAGGING) {
- behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
- }
- }
- @Override
- public void onSlide(@NonNull View bottomSheet, float slideOffset) {
- // Do nothing.
- }
- });
- mKeyboardShortcutsBottomSheetDialog.setCanceledOnTouchOutside(true);
- Window keyboardShortcutsWindow = mKeyboardShortcutsBottomSheetDialog.getWindow();
- keyboardShortcutsWindow.setType(TYPE_SYSTEM_DIALOG);
synchronized (sLock) {
// show KeyboardShortcutsBottomSheetDialog only if it has not been dismissed already
if (sInstance != null) {
@@ -908,6 +898,8 @@
}
}
mSearchEditText = keyboardShortcutsView.findViewById(R.id.keyboard_shortcuts_search);
+ mEditTextCancel = keyboardShortcutsView.findViewById(
+ R.id.keyboard_shortcuts_search_cancel);
mSearchEditText.addTextChangedListener(
new TextWatcher() {
@Override
@@ -921,6 +913,8 @@
shortcutsContainer.setAccessibilityPaneTitle(mContext.getString(
R.string.keyboard_shortcut_a11y_show_search_results));
}
+ mEditTextCancel.setVisibility(
+ TextUtils.isEmpty(mQueryString) ? View.GONE : View.VISIBLE);
}
@Override
@@ -933,9 +927,28 @@
// Do nothing.
}
});
- ImageButton editTextCancel = keyboardShortcutsView.findViewById(
- R.id.keyboard_shortcuts_search_cancel);
- editTextCancel.setOnClickListener(v -> mSearchEditText.setText(null));
+
+ mEditTextCancel.setOnClickListener(v -> mSearchEditText.setText(null));
+ }
+
+ private static void setWindowProperties(Window keyboardShortcutsWindow) {
+ keyboardShortcutsWindow.setType(TYPE_SYSTEM_DIALOG);
+ WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+ params.copyFrom(keyboardShortcutsWindow.getAttributes());
+ // Allows the bottom sheet dialog to render all the way to the bottom of the screen,
+ // behind the gesture navigation bar.
+ params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+ params.setFitInsetsTypes(WindowInsets.Type.statusBars());
+ keyboardShortcutsWindow.setAttributes(params);
+ keyboardShortcutsWindow.getDecorView().setOnApplyWindowInsetsListener((v, insets) -> {
+ int bottom = insets.getInsets(WindowInsets.Type.navigationBars()).bottom;
+ View container = v.findViewById(R.id.keyboard_shortcuts_container);
+ container.setPadding(container.getPaddingLeft(), container.getPaddingTop(),
+ container.getPaddingRight(), bottom);
+ return WindowInsets.CONSUMED;
+ });
+ keyboardShortcutsWindow.setWindowAnimations(
+ R.style.KeyboardShortcutHelper_BottomSheetDialogAnimation);
}
private void populateKeyboardShortcutSearchList(LinearLayout keyboardShortcutsLayout) {
@@ -1256,10 +1269,10 @@
if (mContext.getResources().getConfiguration().orientation
== Configuration.ORIENTATION_PORTRAIT) {
lp.width = (int) (display.getWidth() * 0.8);
- lp.height = (int) (display.getHeight() * 0.7);
+ lp.height = (int) (display.getHeight() * 0.8);
} else {
lp.width = (int) (display.getWidth() * 0.7);
- lp.height = (int) (display.getHeight() * 0.8);
+ lp.height = (int) (display.getHeight() * 0.95);
}
window.setGravity(Gravity.BOTTOM);
window.setAttributes(lp);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 8ea29dd6..aa6bec1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -49,7 +49,6 @@
import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus;
import com.android.systemui.keyguard.MigrateClocksToBlueprint;
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
-import com.android.systemui.plugins.clocks.ClockController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
@@ -469,13 +468,7 @@
/** Returns the id of the currently rendering clock */
public String getClockId() {
if (MigrateClocksToBlueprint.isEnabled()) {
- ClockController clock = mKeyguardClockInteractorLazy.get()
- .getCurrentClock().getValue();
- if (clock == null) {
- Log.e(TAG, "No clock is available");
- return KeyguardClockSwitch.MISSING_CLOCK_ID;
- }
- return clock.getConfig().getId();
+ return mKeyguardClockInteractorLazy.get().getRenderedClockId();
}
if (mClockSwitchView == null) {
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 5b9eb21..0bb871b 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
@@ -742,7 +742,7 @@
pw.println("mHideSensitive=" + mHideSensitive);
pw.println("mShadeExpanded=" + mShadeExpanded);
pw.println("mClearAllInProgress=" + mClearAllInProgress);
- pw.println("mStatusBarState=" + mStatusBarState);
+ pw.println("mStatusBarState=" + StatusBarState.toString(mStatusBarState));
pw.println("mExpansionChanging=" + mExpansionChanging);
pw.println("mPanelFullWidth=" + mIsSmallScreen);
pw.println("mPulsing=" + mPulsing);
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 3367dc4..fedb88d 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
@@ -113,6 +113,9 @@
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds;
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape;
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
@@ -135,6 +138,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.function.BiConsumer;
@@ -143,7 +147,9 @@
/**
* A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
*/
-public class NotificationStackScrollLayout extends ViewGroup implements Dumpable {
+public class NotificationStackScrollLayout
+ extends ViewGroup
+ implements Dumpable, NotificationScrollView {
public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
private static final String TAG = "StackScroller";
@@ -218,6 +224,7 @@
*/
private final StackScrollAlgorithm mStackScrollAlgorithm;
private final AmbientState mAmbientState;
+ private final ScrollViewFields mScrollViewFields = new ScrollViewFields();
private final GroupMembershipManager mGroupMembershipManager;
private final GroupExpansionManager mGroupExpansionManager;
@@ -230,7 +237,8 @@
private final ArrayList<View> mSwipedOutViews = new ArrayList<>();
private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
private final StackStateAnimator mStateAnimator;
- private boolean mAnimationsEnabled;
+ // TODO(b/332732878): call setAnimationsEnabled with scene container enabled, then remove this
+ private boolean mAnimationsEnabled = SceneContainerFlag.isEnabled();
private boolean mChangePositionInProgress;
private boolean mChildTransferInProgress;
@@ -589,7 +597,7 @@
@Override
public boolean isScrolledToTop() {
if (SceneContainerFlag.isEnabled()) {
- return mController.isPlaceholderScrolledToTop();
+ return mScrollViewFields.isScrolledToTop();
} else {
return mOwnScrollY == 0;
}
@@ -854,7 +862,8 @@
drawDebugInfo(canvas, y, Color.MAGENTA,
/* label= */ "mContentHeight = " + y);
- drawDebugInfo(canvas, mRoundedRectClippingBottom, Color.DKGRAY,
+ y = mRoundedRectClippingBottom;
+ drawDebugInfo(canvas, y, Color.DKGRAY,
/* label= */ "mRoundedRectClippingBottom) = " + y);
}
@@ -1130,6 +1139,48 @@
}
}
+ @Override
+ public View asView() {
+ return this;
+ }
+
+ @Override
+ public void setScrolledToTop(boolean scrolledToTop) {
+ mScrollViewFields.setScrolledToTop(scrolledToTop);
+ }
+
+ @Override
+ public void setStackTop(float stackTop) {
+ mScrollViewFields.setStackTop(stackTop);
+ // TODO(b/332574413): replace the following with using stackTop
+ updateTopPadding(stackTop, isAddOrRemoveAnimationPending());
+ }
+
+ @Override
+ public void setStackBottom(float stackBottom) {
+ mScrollViewFields.setStackBottom(stackBottom);
+ }
+
+ @Override
+ public void setHeadsUpTop(float headsUpTop) {
+ mScrollViewFields.setHeadsUpTop(headsUpTop);
+ }
+
+ @Override
+ public void setSyntheticScrollConsumer(@Nullable Consumer<Float> consumer) {
+ mScrollViewFields.setSyntheticScrollConsumer(consumer);
+ }
+
+ @Override
+ public void setStackHeightConsumer(@Nullable Consumer<Float> consumer) {
+ mScrollViewFields.setStackHeightConsumer(consumer);
+ }
+
+ @Override
+ public void setHeadsUpHeightConsumer(@Nullable Consumer<Float> consumer) {
+ mScrollViewFields.setHeadsUpHeightConsumer(consumer);
+ }
+
/**
* @param listener to be notified after the location of Notification children might have
* changed.
@@ -1380,6 +1431,31 @@
mOnStackYChanged = onStackYChanged;
}
+ @Override
+ public void setExpandFraction(float expandFraction) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+ final float oldFraction = mAmbientState.getExpansionFraction();
+ final boolean wasExpanding = oldFraction != 0f && oldFraction != 1f;
+ final boolean nowExpanding = expandFraction != 0f && expandFraction != 1f;
+
+ // need to enter 'expanding' state before handling the new expand fraction, and then
+ if (nowExpanding && !wasExpanding) {
+ onExpansionStarted();
+ mController.checkSnoozeLeavebehind();
+ }
+
+ // Update the expand progress between started/stopped events
+ mAmbientState.setExpansionFraction(expandFraction);
+ // TODO(b/332577544): don't convert to height which then converts to the fraction again
+ setExpandedHeight(expandFraction * getHeight());
+
+ // expansion stopped event requires that the expandFraction has already been updated
+ if (!nowExpanding && wasExpanding) {
+ setCheckForLeaveBehind(false);
+ onExpansionStopped();
+ }
+ }
+
/**
* Update the height of the panel.
*
@@ -2314,7 +2390,7 @@
/* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications,
shelfIntrinsicHeight);
mIntrinsicContentHeight = height;
- mController.setIntrinsicContentHeight(mIntrinsicContentHeight);
+ mScrollViewFields.sendStackHeight(height);
// The topPadding can be bigger than the regular padding when qs is expanded, in that
// state the maxPanelHeight and the contentHeight should be bigger
@@ -2904,6 +2980,7 @@
}
public void setAnimationsEnabled(boolean animationsEnabled) {
+ // TODO(b/332732878): remove the initial value of this field once the setter is called
mAnimationsEnabled = animationsEnabled;
updateNotificationAnimationStates();
if (!animationsEnabled) {
@@ -3553,7 +3630,7 @@
protected boolean isInsideQsHeader(MotionEvent ev) {
if (SceneContainerFlag.isEnabled()) {
- return ev.getY() < mController.getPlaceholderTop();
+ return ev.getY() < mScrollViewFields.getScrimClippingShape().getBounds().getTop();
}
mQsHeader.getBoundsOnScreen(mQsHeaderBound);
@@ -4086,7 +4163,7 @@
// to it so that it can scroll the stack and scrim accordingly.
if (SceneContainerFlag.isEnabled()) {
float diff = endPosition - layoutEnd;
- mController.sendSyntheticScrollToSceneFramework(diff);
+ mScrollViewFields.sendSyntheticScroll(diff);
}
setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
mDisallowScrollingInThisMotion = true;
@@ -4969,6 +5046,7 @@
elapsedRealtime - mLastUpdateSidePaddingElapsedRealtime);
println(pw, "isSmallLandscapeLockscreenEnabled", mIsSmallLandscapeLockscreenEnabled);
mNotificationStackSizeCalculator.dump(pw, args);
+ mScrollViewFields.dump(pw);
});
pw.println();
pw.println("Contents:");
@@ -5509,8 +5587,40 @@
/**
* Set rounded rect clipping bounds on this view.
*/
+ @Override
+ public void setScrimClippingShape(@Nullable ShadeScrimShape shape) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+ if (Objects.equals(mScrollViewFields.getScrimClippingShape(), shape)) return;
+ mScrollViewFields.setScrimClippingShape(shape);
+ mShouldUseRoundedRectClipping = shape != null;
+ mRoundedClipPath.reset();
+ if (shape != null) {
+ ShadeScrimBounds bounds = shape.getBounds();
+ mRoundedRectClippingLeft = (int) bounds.getLeft();
+ mRoundedRectClippingTop = (int) bounds.getTop();
+ mRoundedRectClippingRight = (int) bounds.getRight();
+ mRoundedRectClippingBottom = (int) bounds.getBottom();
+ mBgCornerRadii[0] = shape.getTopRadius();
+ mBgCornerRadii[1] = shape.getTopRadius();
+ mBgCornerRadii[2] = shape.getTopRadius();
+ mBgCornerRadii[3] = shape.getTopRadius();
+ mBgCornerRadii[4] = shape.getBottomRadius();
+ mBgCornerRadii[5] = shape.getBottomRadius();
+ mBgCornerRadii[6] = shape.getBottomRadius();
+ mBgCornerRadii[7] = shape.getBottomRadius();
+ mRoundedClipPath.addRoundRect(
+ bounds.getLeft(), bounds.getTop(), bounds.getRight(), bounds.getBottom(),
+ mBgCornerRadii, Path.Direction.CW);
+ }
+ invalidate();
+ }
+
+ /**
+ * Set rounded rect clipping bounds on this view.
+ */
public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
int bottomRadius) {
+ SceneContainerFlag.assertInLegacyMode();
if (mRoundedRectClippingLeft == left && mRoundedRectClippingRight == right
&& mRoundedRectClippingBottom == bottom && mRoundedRectClippingTop == top
&& mBgCornerRadii[0] == topRadius && mBgCornerRadii[5] == bottomRadius) {
@@ -5588,6 +5698,7 @@
* Should we use rounded rect clipping
*/
private void updateUseRoundedRectClipping() {
+ if (SceneContainerFlag.isEnabled()) return;
// We don't want to clip notifications when QS is expanded, because incoming heads up on
// the bottom would be clipped otherwise
boolean qsAllowsClipping = mQsExpansionFraction < 0.5f || mShouldUseSplitNotificationShade;
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 59901ac..06479e5 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
@@ -83,6 +83,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.scene.ui.view.WindowRootView;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeViewController;
@@ -125,7 +126,6 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationSnooze;
import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
-import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor;
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
@@ -189,7 +189,6 @@
private final VisibilityLocationProviderDelegator mVisibilityLocationProviderDelegator;
private final ShadeController mShadeController;
private final Provider<WindowRootView> mWindowRootView;
- private final NotificationStackAppearanceInteractor mStackAppearanceInteractor;
private final KeyguardMediaController mKeyguardMediaController;
private final SysuiStatusBarStateController mStatusBarStateController;
private final KeyguardBypassController mKeyguardBypassController;
@@ -742,7 +741,6 @@
NotificationListViewBinder viewBinder,
ShadeController shadeController,
Provider<WindowRootView> windowRootView,
- NotificationStackAppearanceInteractor stackAppearanceInteractor,
InteractionJankMonitor jankMonitor,
StackStateLogger stackLogger,
NotificationStackScrollLogger logger,
@@ -795,7 +793,6 @@
mSeenNotificationsInteractor = seenNotificationsInteractor;
mShadeController = shadeController;
mWindowRootView = windowRootView;
- mStackAppearanceInteractor = stackAppearanceInteractor;
mFeatureFlags = featureFlags;
mNotificationTargetsHelper = notificationTargetsHelper;
mSecureSettings = secureSettings;
@@ -1124,6 +1121,7 @@
}
public boolean isAddOrRemoveAnimationPending() {
+ SceneContainerFlag.assertInLegacyMode();
return mView != null && mView.isAddOrRemoveAnimationPending();
}
@@ -1163,29 +1161,6 @@
}
}
- /** Send internal notification expansion to the scene container framework. */
- public void sendSyntheticScrollToSceneFramework(Float delta) {
- mStackAppearanceInteractor.setSyntheticScroll(delta);
- }
-
- /** Get the y-coordinate of the top bound of the stack. */
- public float getPlaceholderTop() {
- return mStackAppearanceInteractor.getShadeScrimBounds().getValue().getTop();
- }
-
- /**
- * Returns whether the notification stack is scrolled to the top; i.e., it cannot be scrolled
- * down any further.
- */
- public boolean isPlaceholderScrolledToTop() {
- return mStackAppearanceInteractor.getScrolledToTop().getValue();
- }
-
- /** Set the intrinsic height of the stack content without additional padding. */
- public void setIntrinsicContentHeight(float intrinsicContentHeight) {
- mStackAppearanceInteractor.setStackHeight(intrinsicContentHeight);
- }
-
public void setIntrinsicPadding(int intrinsicPadding) {
mView.setIntrinsicPadding(intrinsicPadding);
}
@@ -1264,6 +1239,7 @@
}
public void setScrollingEnabled(boolean enabled) {
+ SceneContainerFlag.assertInLegacyMode();
mView.setScrollingEnabled(enabled);
}
@@ -1284,6 +1260,7 @@
}
public void updateTopPadding(float qsHeight, boolean animate) {
+ SceneContainerFlag.assertInLegacyMode();
mView.updateTopPadding(qsHeight, animate);
}
@@ -1386,11 +1363,13 @@
}
public void onExpansionStarted() {
+ SceneContainerFlag.assertInLegacyMode();
mView.onExpansionStarted();
checkSnoozeLeavebehind();
}
public void onExpansionStopped() {
+ SceneContainerFlag.assertInLegacyMode();
mView.setCheckForLeaveBehind(false);
mView.onExpansionStopped();
}
@@ -1519,6 +1498,7 @@
}
public void setExpandedHeight(float expandedHeight) {
+ SceneContainerFlag.assertInLegacyMode();
mView.setExpandedHeight(expandedHeight);
}
@@ -1794,6 +1774,7 @@
*/
public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
int bottomRadius) {
+ SceneContainerFlag.assertInLegacyMode();
mView.setRoundedClippingBounds(left, top, right, bottom, topRadius, bottomRadius);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
new file mode 100644
index 0000000..edac5ed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack
+
+import android.util.IndentingPrintWriter
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
+import com.android.systemui.util.printSection
+import com.android.systemui.util.println
+import java.util.function.Consumer
+
+/**
+ * This is a state holder object used by [NSSL][NotificationStackScrollLayout] to contain states
+ * provided by the `NotificationScrollViewBinder` to the `NotificationScrollView`.
+ *
+ * Unlike AmbientState, no class other than NSSL should ever have access to this class in any way.
+ * These fields are effectively NSSL's private fields.
+ */
+class ScrollViewFields {
+ /** Used to produce the clipping path */
+ var scrimClippingShape: ShadeScrimShape? = null
+ /** Y coordinate in view pixels of the top of the notification stack */
+ var stackTop: Float = 0f
+ /**
+ * Y coordinate in view pixels above which the bottom of the notification stack / shelf / footer
+ * must be.
+ */
+ var stackBottom: Float = 0f
+ /** Y coordinate in view pixels of the top of the HUN */
+ var headsUpTop: Float = 0f
+ /** Whether the notifications are scrolled all the way to the top (i.e. when freshly opened) */
+ var isScrolledToTop: Boolean = true
+
+ /**
+ * When internal NSSL expansion requires the stack to be scrolled (e.g. to keep an expanding
+ * notification in view), that scroll amount can be sent here and it will be handled by the
+ * placeholder
+ */
+ var syntheticScrollConsumer: Consumer<Float>? = null
+ /**
+ * Any time the stack height is recalculated, it should be updated here to be used by the
+ * placeholder
+ */
+ var stackHeightConsumer: Consumer<Float>? = null
+ /**
+ * Any time the heads up height is recalculated, it should be updated here to be used by the
+ * placeholder
+ */
+ var headsUpHeightConsumer: Consumer<Float>? = null
+
+ /** send the [syntheticScroll] to the [syntheticScrollConsumer], if present. */
+ fun sendSyntheticScroll(syntheticScroll: Float) =
+ syntheticScrollConsumer?.accept(syntheticScroll)
+ /** send the [stackHeight] to the [stackHeightConsumer], if present. */
+ fun sendStackHeight(stackHeight: Float) = stackHeightConsumer?.accept(stackHeight)
+ /** send the [headsUpHeight] to the [headsUpHeightConsumer], if present. */
+ fun sendHeadsUpHeight(headsUpHeight: Float) = headsUpHeightConsumer?.accept(headsUpHeight)
+
+ fun dump(pw: IndentingPrintWriter) {
+ pw.printSection("StackViewStates") {
+ pw.println("scrimClippingShape", scrimClippingShape)
+ pw.println("stackTop", stackTop)
+ pw.println("stackBottom", stackBottom)
+ pw.println("headsUpTop", headsUpTop)
+ pw.println("isScrolledToTop", isScrolledToTop)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
index 462547e..b047379 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
@@ -27,8 +27,12 @@
*/
@SysUISingleton
class NotificationPlaceholderRepository @Inject constructor() {
- /** The bounds of the notification shade scrim / container in the current scene. */
- val shadeScrimBounds = MutableStateFlow(ShadeScrimBounds())
+ /**
+ * The bounds of the notification shade scrim / container in the current scene.
+ *
+ * When `null`, clipping should not be applied to notifications.
+ */
+ val shadeScrimBounds = MutableStateFlow<ShadeScrimBounds?>(null)
/**
* The y-coordinate in px of top of the contents of the notification stack. This value can be
@@ -44,7 +48,7 @@
val headsUpTop = MutableStateFlow(0f)
/** height made available to the notifications in the size-constrained mode of lock screen. */
- val constrainedAvailableSpace = MutableStateFlow(0f)
+ val constrainedAvailableSpace = MutableStateFlow(0)
/**
* Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
index 938e43e..8a9da69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
@@ -35,7 +35,7 @@
val stackHeight = MutableStateFlow(0f)
/** The height in px of the current heads up notification. */
- val activeHeadsUpRowHeight = MutableStateFlow(0f)
+ val headsUpHeight = MutableStateFlow(0f)
/**
* The amount in px that the notification stack should scroll due to internal expansion. This
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
index 32562f1..a5b4f5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -42,7 +42,7 @@
shadeInteractor: ShadeInteractor,
) {
/** The bounds of the notification stack in the current scene. */
- val shadeScrimBounds: StateFlow<ShadeScrimBounds> =
+ val shadeScrimBounds: StateFlow<ShadeScrimBounds?> =
placeholderRepository.shadeScrimBounds.asStateFlow()
/**
@@ -59,8 +59,8 @@
isExpandingFromHeadsUp,
) { shadeMode, isExpandingFromHeadsUp ->
ShadeScrimRounding(
- roundTop = !(shadeMode == ShadeMode.Split && isExpandingFromHeadsUp),
- roundBottom = shadeMode != ShadeMode.Single,
+ isTopRounded = !(shadeMode == ShadeMode.Split && isExpandingFromHeadsUp),
+ isBottomRounded = shadeMode != ShadeMode.Single,
)
}
.distinctUntilChanged()
@@ -72,15 +72,28 @@
*/
val stackHeight: StateFlow<Float> = viewHeightRepository.stackHeight.asStateFlow()
+ /** The height in px of the contents of the HUN. */
+ val headsUpHeight: StateFlow<Float> = viewHeightRepository.headsUpHeight.asStateFlow()
+
/** The y-coordinate in px of top of the contents of the notification stack. */
val stackTop: StateFlow<Float> = placeholderRepository.stackTop.asStateFlow()
+ /** The y-coordinate in px of bottom of the contents of the notification stack. */
+ val stackBottom: StateFlow<Float> = placeholderRepository.stackBottom.asStateFlow()
+
+ /** The height of the keyguard's available space bounds */
+ val constrainedAvailableSpace: StateFlow<Int> =
+ placeholderRepository.constrainedAvailableSpace.asStateFlow()
+
/**
* Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any
* further.
*/
val scrolledToTop: StateFlow<Boolean> = placeholderRepository.scrolledToTop.asStateFlow()
+ /** The y-coordinate in px of bottom of the contents of the HUN. */
+ val headsUpTop: StateFlow<Float> = placeholderRepository.headsUpTop.asStateFlow()
+
/**
* The amount in px that the notification stack should scroll due to internal expansion. This
* should only happen when a notification expansion hits the bottom of the screen, so it is
@@ -89,8 +102,8 @@
val syntheticScroll: Flow<Float> = viewHeightRepository.syntheticScroll.asStateFlow()
/** Sets the position of the notification stack in the current scene. */
- fun setShadeScrimBounds(bounds: ShadeScrimBounds) {
- check(bounds.top <= bounds.bottom) { "Invalid bounds: $bounds" }
+ fun setShadeScrimBounds(bounds: ShadeScrimBounds?) {
+ check(bounds == null || bounds.top <= bounds.bottom) { "Invalid bounds: $bounds" }
placeholderRepository.shadeScrimBounds.value = bounds
}
@@ -99,9 +112,19 @@
viewHeightRepository.stackHeight.value = height
}
+ /** Sets the height of heads up notification. */
+ fun setHeadsUpHeight(height: Float) {
+ viewHeightRepository.headsUpHeight.value = height
+ }
+
/** Sets the y-coord in px of the top of the contents of the notification stack. */
- fun setStackTop(startY: Float) {
- placeholderRepository.stackTop.value = startY
+ fun setStackTop(stackTop: Float) {
+ placeholderRepository.stackTop.value = stackTop
+ }
+
+ /** Sets the y-coord in px of the bottom of the contents of the notification stack. */
+ fun setStackBottom(stackBottom: Float) {
+ placeholderRepository.stackBottom.value = stackBottom
}
/** Sets whether the notification stack is scrolled to the top. */
@@ -113,4 +136,12 @@
fun setSyntheticScroll(delta: Float) {
viewHeightRepository.syntheticScroll.value = delta
}
+
+ fun setConstrainedAvailableSpace(height: Int) {
+ placeholderRepository.constrainedAvailableSpace.value = height
+ }
+
+ fun setHeadsUpTop(headsUpTop: Float) {
+ placeholderRepository.headsUpTop.value = headsUpTop
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimBounds.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimBounds.kt
index 448127a..7e3e724 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimBounds.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimBounds.kt
@@ -29,4 +29,12 @@
) {
/** The current height of the notification container. */
val height: Float = bottom - top
+
+ operator fun minus(position: ViewPosition) =
+ ShadeScrimBounds(
+ left = left - position.left,
+ top = top - position.top,
+ right = right - position.left,
+ bottom = bottom - position.top,
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimClipping.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimClipping.kt
index 621dd0c..e86fd1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimClipping.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimClipping.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.stack.shared.model
-/** Models the clipping rounded rectangle of the notification stack */
+/** Models the clipping rounded rectangle of the notification stack, where the rounding is binary */
data class ShadeScrimClipping(
val bounds: ShadeScrimBounds = ShadeScrimBounds(),
val rounding: ShadeScrimRounding = ShadeScrimRounding()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimRounding.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimRounding.kt
index 2fe265f..9d4231c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimRounding.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimRounding.kt
@@ -19,7 +19,7 @@
/** Models the corner rounds of the notification stack. */
data class ShadeScrimRounding(
/** Whether the top corners of the notification stack should be rounded. */
- val roundTop: Boolean = false,
+ val isTopRounded: Boolean = false,
/** Whether the bottom corners of the notification stack should be rounded. */
- val roundBottom: Boolean = false,
+ val isBottomRounded: Boolean = false,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimShape.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimShape.kt
new file mode 100644
index 0000000..e6f0647
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrimShape.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.shared.model
+
+/** Models the clipping rounded rectangle of the notification stack, with corner radii in px */
+data class ShadeScrimShape(
+ val bounds: ShadeScrimBounds = ShadeScrimBounds(),
+ /** radius in px of the top corners */
+ val topRadius: Int,
+ /** radius in px of the bottom corners */
+ val bottomRadius: Int,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ViewPosition.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ViewPosition.kt
new file mode 100644
index 0000000..5d2b0ad2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ViewPosition.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.shared.model
+
+/** An offset of view, used to adjust bounds. */
+data class ViewPosition(val left: Int = 0, val top: Int = 0)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
new file mode 100644
index 0000000..ac00d3b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.ui.view
+
+import android.view.View
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
+import java.util.function.Consumer
+
+/**
+ * This view (interface) is the view which scrolls and positions the heads up notification and
+ * notification stack, but is otherwise agnostic to the content.
+ */
+interface NotificationScrollView {
+ /**
+ * Since this is an interface rather than a literal View, this provides cast-like access to the
+ * underlying view.
+ */
+ fun asView(): View
+
+ /** Set the clipping bounds used when drawing */
+ fun setScrimClippingShape(shape: ShadeScrimShape?)
+
+ /** set the y position in px of the top of the stack in this view's coordinates */
+ fun setStackTop(stackTop: Float)
+
+ /** set the y position in px of the bottom of the stack in this view's coordinates */
+ fun setStackBottom(stackBottom: Float)
+
+ /** set the y position in px of the top of the HUN in this view's coordinates */
+ fun setHeadsUpTop(headsUpTop: Float)
+
+ /** set whether the view has been scrolled all the way to the top */
+ fun setScrolledToTop(scrolledToTop: Boolean)
+
+ /** Set a consumer for synthetic scroll events */
+ fun setSyntheticScrollConsumer(consumer: Consumer<Float>?)
+ /** Set a consumer for stack height changed events */
+ fun setStackHeightConsumer(consumer: Consumer<Float>?)
+ /** Set a consumer for heads up height changed events */
+ fun setHeadsUpHeightConsumer(consumer: Consumer<Float>?)
+
+ /** sets that scrolling is allowed */
+ fun setScrollingEnabled(enabled: Boolean)
+
+ /** sets the current expand fraction */
+ fun setExpandFraction(expandFraction: Float)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
new file mode 100644
index 0000000..a98717a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.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.systemui.statusbar.notification.stack.ui.viewbinder
+
+import android.view.View
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.common.ui.view.onLayoutChanged
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.stack.shared.model.ViewPosition
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationScrollViewModel
+import com.android.systemui.util.kotlin.FlowDumperImpl
+import com.android.systemui.util.kotlin.launchAndDispose
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+
+/** Binds the [NotificationScrollView]. */
+@SysUISingleton
+class NotificationScrollViewBinder
+@Inject
+constructor(
+ dumpManager: DumpManager,
+ @Main private val mainImmediateDispatcher: CoroutineDispatcher,
+ private val view: NotificationScrollView,
+ private val viewModel: NotificationScrollViewModel,
+ private val configuration: ConfigurationState,
+) : FlowDumperImpl(dumpManager) {
+
+ private val viewPosition = MutableStateFlow(ViewPosition()).dumpValue("viewPosition")
+ private val viewTopOffset = viewPosition.map { it.top }.distinctUntilChanged()
+
+ fun bindWhileAttached(): DisposableHandle {
+ return view.asView().repeatWhenAttached(mainImmediateDispatcher) {
+ repeatOnLifecycle(Lifecycle.State.CREATED) { bind() }
+ }
+ }
+
+ suspend fun bind() = coroutineScope {
+ launchAndDispose {
+ viewPosition.value = view.asView().position
+ view.asView().onLayoutChanged { viewPosition.value = it.position }
+ }
+
+ launch {
+ viewModel.shadeScrimShape(scrimRadius, viewPosition).collect {
+ view.setScrimClippingShape(it)
+ }
+ }
+
+ launch { viewModel.stackTop.minusTopOffset().collect { view.setStackTop(it) } }
+ launch { viewModel.stackBottom.minusTopOffset().collect { view.setStackBottom(it) } }
+ launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
+ launch { viewModel.headsUpTop.minusTopOffset().collect { view.setHeadsUpTop(it) } }
+ launch { viewModel.expandFraction.collect { view.setExpandFraction(it) } }
+ launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } }
+
+ launchAndDispose {
+ view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
+ view.setStackHeightConsumer(viewModel.stackHeightConsumer)
+ view.setHeadsUpHeightConsumer(viewModel.headsUpHeightConsumer)
+ DisposableHandle {
+ view.setSyntheticScrollConsumer(null)
+ view.setStackHeightConsumer(null)
+ view.setHeadsUpHeightConsumer(null)
+ }
+ }
+ }
+
+ /** Combine with the topOffset flow and subtract that value from this flow's value */
+ private fun Flow<Float>.minusTopOffset() =
+ combine(viewTopOffset) { y, topOffset -> y - topOffset }
+
+ /** flow of the scrim clipping radius */
+ private val scrimRadius: Flow<Int>
+ get() = configuration.getDimensionPixelOffset(R.dimen.notification_scrim_corner_radius)
+
+ /** Construct a [ViewPosition] from this view using [View.getLeft] and [View.getTop] */
+ private val View.position
+ get() = ViewPosition(left = left, top = top)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackViewBinder.kt
deleted file mode 100644
index d6d31db..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackViewBinder.kt
+++ /dev/null
@@ -1,103 +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.systemui.statusbar.notification.stack.ui.viewbinder
-
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.notification.stack.AmbientState
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationStackAppearanceViewModel
-import javax.inject.Inject
-import kotlin.math.roundToInt
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.launch
-
-/** Binds the NSSL/Controller/AmbientState to their ViewModel. */
-@SysUISingleton
-class NotificationStackViewBinder
-@Inject
-constructor(
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
- private val ambientState: AmbientState,
- private val view: NotificationStackScrollLayout,
- private val controller: NotificationStackScrollLayoutController,
- private val viewModel: NotificationStackAppearanceViewModel,
- private val configuration: ConfigurationState,
-) {
-
- fun bindWhileAttached(): DisposableHandle {
- return view.repeatWhenAttached(mainImmediateDispatcher) {
- repeatOnLifecycle(Lifecycle.State.CREATED) { bind() }
- }
- }
-
- suspend fun bind() = coroutineScope {
- launch {
- combine(viewModel.shadeScrimClipping, clipRadius, ::Pair).collect {
- (clipping, clipRadius) ->
- val (bounds, rounding) = clipping
- val viewLeft = controller.view.left
- val viewTop = controller.view.top
- controller.setRoundedClippingBounds(
- bounds.left.roundToInt() - viewLeft,
- bounds.top.roundToInt() - viewTop,
- bounds.right.roundToInt() - viewLeft,
- bounds.bottom.roundToInt() - viewTop,
- if (rounding.roundTop) clipRadius else 0,
- if (rounding.roundBottom) clipRadius else 0,
- )
- }
- }
-
- launch {
- viewModel.stackTop.collect {
- controller.updateTopPadding(it, controller.isAddOrRemoveAnimationPending)
- }
- }
-
- launch {
- var wasExpanding = false
- viewModel.expandFraction.collect { expandFraction ->
- val nowExpanding = expandFraction != 0f && expandFraction != 1f
- if (nowExpanding && !wasExpanding) {
- controller.onExpansionStarted()
- }
- ambientState.expansionFraction = expandFraction
- controller.expandedHeight = expandFraction * controller.view.height
- if (!nowExpanding && wasExpanding) {
- controller.onExpansionStopped()
- }
- wasExpanding = nowExpanding
- }
- }
-
- launch { viewModel.isScrollable.collect { controller.setScrollingEnabled(it) } }
- }
-
- private val clipRadius: Flow<Int>
- get() = configuration.getDimensionPixelOffset(R.dimen.notification_scrim_corner_radius)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index 4a096a8..cfd19ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -49,7 +49,7 @@
private val sceneContainerFlags: SceneContainerFlags,
private val controller: NotificationStackScrollLayoutController,
private val notificationStackSizeCalculator: NotificationStackSizeCalculator,
- private val notificationStackViewBinder: NotificationStackViewBinder,
+ private val notificationScrollViewBinder: NotificationScrollViewBinder,
@Main private val mainImmediateDispatcher: CoroutineDispatcher,
) {
@@ -162,7 +162,7 @@
}
if (sceneContainerFlags.isEnabled()) {
- disposables += notificationStackViewBinder.bindWhileAttached()
+ disposables += notificationScrollViewBinder.bindWhileAttached()
}
controller.setOnHeightChangedRunnable { viewModel.notificationStackChanged() }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
similarity index 64%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 071127c..9483f33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -26,17 +26,18 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimClipping
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
+import com.android.systemui.statusbar.notification.stack.shared.model.ViewPosition
import com.android.systemui.util.kotlin.FlowDumperImpl
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
/** ViewModel which represents the state of the NSSL/Controller in the world of flexiglass */
@SysUISingleton
-class NotificationStackAppearanceViewModel
+class NotificationScrollViewModel
@Inject
constructor(
dumpManager: DumpManager,
@@ -80,16 +81,48 @@
.dumpWhileCollecting("expandFraction")
/** The bounds of the notification stack in the current scene. */
- val shadeScrimClipping: Flow<ShadeScrimClipping> =
+ private val shadeScrimClipping: Flow<ShadeScrimClipping?> =
combine(
stackAppearanceInteractor.shadeScrimBounds,
stackAppearanceInteractor.shadeScrimRounding,
- ::ShadeScrimClipping
- )
+ ) { bounds, rounding ->
+ bounds?.let { ShadeScrimClipping(it, rounding) }
+ }
.dumpWhileCollecting("stackClipping")
+ fun shadeScrimShape(
+ cornerRadius: Flow<Int>,
+ viewPosition: Flow<ViewPosition>
+ ): Flow<ShadeScrimShape?> =
+ combine(shadeScrimClipping, cornerRadius, viewPosition) { clipping, radius, position ->
+ if (clipping == null) return@combine null
+ ShadeScrimShape(
+ bounds = clipping.bounds - position,
+ topRadius = radius.takeIf { clipping.rounding.isTopRounded } ?: 0,
+ bottomRadius = radius.takeIf { clipping.rounding.isBottomRounded } ?: 0
+ )
+ }
+ .dumpWhileCollecting("shadeScrimShape")
+
/** The y-coordinate in px of top of the contents of the notification stack. */
- val stackTop: StateFlow<Float> = stackAppearanceInteractor.stackTop.dumpValue("stackTop")
+ val stackTop: Flow<Float> = stackAppearanceInteractor.stackTop.dumpValue("stackTop")
+ /** The y-coordinate in px of bottom of the contents of the notification stack. */
+ val stackBottom: Flow<Float> = stackAppearanceInteractor.stackBottom.dumpValue("stackBottom")
+ /**
+ * Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any
+ * further.
+ */
+ val scrolledToTop: Flow<Boolean> =
+ stackAppearanceInteractor.scrolledToTop.dumpValue("scrolledToTop")
+ /** The y-coordinate in px of bottom of the contents of the HUN. */
+ val headsUpTop: Flow<Float> = stackAppearanceInteractor.headsUpTop.dumpValue("headsUpTop")
+
+ /** Receives the amount (px) that the stack should scroll due to internal expansion. */
+ val syntheticScrollConsumer: (Float) -> Unit = stackAppearanceInteractor::setSyntheticScroll
+ /** Receives the height of the contents of the notification stack. */
+ val stackHeightConsumer: (Float) -> Unit = stackAppearanceInteractor::setStackHeight
+ /** Receives the height of the heads up notification. */
+ val headsUpHeightConsumer: (Float) -> Unit = stackAppearanceInteractor::setHeadsUpHeight
/** Whether the notification stack is scrollable or not. */
val isScrollable: Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index 477f139..b284179 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -18,6 +18,7 @@
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -26,8 +27,10 @@
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding
+import com.android.systemui.util.kotlin.FlowDumperImpl
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
/**
* ViewModel used by the Notification placeholders inside the scene container to update the
@@ -37,67 +40,72 @@
class NotificationsPlaceholderViewModel
@Inject
constructor(
+ dumpManager: DumpManager,
private val interactor: NotificationStackAppearanceInteractor,
shadeInteractor: ShadeInteractor,
flags: SceneContainerFlags,
featureFlags: FeatureFlagsClassic,
private val keyguardInteractor: KeyguardInteractor,
-) {
+) : FlowDumperImpl(dumpManager) {
/** DEBUG: whether the placeholder should be made slightly visible for positional debugging. */
val isVisualDebuggingEnabled: Boolean = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES)
/** DEBUG: whether the debug logging should be output. */
val isDebugLoggingEnabled: Boolean = flags.isEnabled()
- /**
- * Notifies that the bounds of the notification placeholder have changed.
- *
- * @param top The position of the top of the container in its window coordinate system, in
- * pixels.
- * @param bottom The position of the bottom of the container in its window coordinate system, in
- * pixels.
- */
- fun onBoundsChanged(
- left: Float,
+ /** Notifies that the bounds of the notification scrim have changed. */
+ fun onScrimBoundsChanged(bounds: ShadeScrimBounds?) {
+ interactor.setShadeScrimBounds(bounds)
+ }
+
+ /** Notifies that the bounds of the notification placeholder have changed. */
+ fun onStackBoundsChanged(
top: Float,
- right: Float,
bottom: Float,
) {
keyguardInteractor.setNotificationContainerBounds(
NotificationContainerBounds(top = top, bottom = bottom)
)
- interactor.setShadeScrimBounds(
- ShadeScrimBounds(top = top, bottom = bottom, left = left, right = right)
- )
+ interactor.setStackTop(top)
+ interactor.setStackBottom(bottom)
+ }
+
+ /** Sets the available space */
+ fun onConstrainedAvailableSpaceChanged(height: Int) {
+ interactor.setConstrainedAvailableSpace(height)
+ }
+
+ fun onHeadsUpTopChanged(headsUpTop: Float) {
+ interactor.setHeadsUpTop(headsUpTop)
}
/** Corner rounding of the stack */
- val shadeScrimRounding: Flow<ShadeScrimRounding> = interactor.shadeScrimRounding
+ val shadeScrimRounding: Flow<ShadeScrimRounding> =
+ interactor.shadeScrimRounding.dumpWhileCollecting("shadeScrimRounding")
/**
* The height in px of the contents of notification stack. Depending on the number of
* notifications, this can exceed the space available on screen to show notifications, at which
* point the notification stack should become scrollable.
*/
- val stackHeight = interactor.stackHeight
+ val stackHeight: StateFlow<Float> = interactor.stackHeight.dumpValue("stackHeight")
+
+ /** The height in px of the contents of the HUN. */
+ val headsUpHeight: StateFlow<Float> = interactor.headsUpHeight.dumpValue("headsUpHeight")
/**
* The amount [0-1] that the shade has been opened. At 0, the shade is closed; at 1, the shade
* is open.
*/
- val expandFraction: Flow<Float> = shadeInteractor.shadeExpansion
+ val expandFraction: Flow<Float> = shadeInteractor.shadeExpansion.dumpValue("expandFraction")
/**
* The amount in px that the notification stack should scroll due to internal expansion. This
* should only happen when a notification expansion hits the bottom of the screen, so it is
* necessary to scroll up to keep expanding the notification.
*/
- val syntheticScroll: Flow<Float> = interactor.syntheticScroll
-
- /** Sets the y-coord in px of the top of the contents of the notification stack. */
- fun onContentTopChanged(padding: Float) {
- interactor.setStackTop(padding)
- }
+ val syntheticScroll: Flow<Float> =
+ interactor.syntheticScroll.dumpWhileCollecting("syntheticScroll")
/** Sets whether the notification stack is scrolled to the top. */
fun setScrolledToTop(scrolledToTop: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 9f57606..692368d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -60,7 +60,9 @@
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.util.kotlin.BooleanFlowOperators.and
import com.android.systemui.util.kotlin.BooleanFlowOperators.or
@@ -97,6 +99,7 @@
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val shadeInteractor: ShadeInteractor,
+ private val notificationStackAppearanceInteractor: NotificationStackAppearanceInteractor,
private val alternateBouncerToGoneTransitionViewModel:
AlternateBouncerToGoneTransitionViewModel,
private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
@@ -364,6 +367,10 @@
initialValue = NotificationContainerBounds(),
)
.dumpValue("bounds")
+ get() {
+ /* check if */ SceneContainerFlag.isUnexpectedlyInLegacyMode()
+ return field
+ }
/**
* Ensure view is visible when the shade/qs are expanded. Also, as QS is expanding, fade out
@@ -571,8 +578,11 @@
.dumpWhileCollecting("translationX")
private val availableHeight: Flow<Float> =
- bounds
- .map { it.bottom - it.top }
+ if (SceneContainerFlag.isEnabled) {
+ notificationStackAppearanceInteractor.constrainedAvailableSpace.map { it.toFloat() }
+ } else {
+ bounds.map { it.bottom - it.top }
+ }
.distinctUntilChanged()
.dumpWhileCollecting("availableHeight")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index 20a82a4..d99af2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -165,6 +165,8 @@
public void showNotification(@NonNull NotificationEntry entry) {
HeadsUpEntry headsUpEntry = createHeadsUpEntry(entry);
+ mLogger.logShowNotificationRequest(entry);
+
Runnable runnable = () -> {
// TODO(b/315362456) log outside runnable too
mLogger.logShowNotification(entry);
@@ -219,6 +221,8 @@
*/
public void updateNotification(@NonNull String key, boolean shouldHeadsUpAgain) {
HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
+ mLogger.logUpdateNotificationRequest(key, shouldHeadsUpAgain, headsUpEntry != null);
+
Runnable runnable = () -> {
updateNotificationInternal(key, shouldHeadsUpAgain);
};
@@ -378,8 +382,11 @@
*/
protected final void removeEntry(@NonNull String key) {
HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
+ mLogger.logRemoveEntryRequest(key);
Runnable runnable = () -> {
+ mLogger.logRemoveEntry(key);
+
if (headsUpEntry == null) {
return;
}
@@ -566,8 +573,10 @@
public void unpinAll(boolean userUnPinned) {
for (String key : mHeadsUpEntryMap.keySet()) {
HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
-
+ mLogger.logUnpinEntryRequest(key);
Runnable runnable = () -> {
+ mLogger.logUnpinEntry(key);
+
setEntryPinned(headsUpEntry, false /* isPinned */);
// maybe it got un sticky
headsUpEntry.updateEntry(false /* updatePostTime */, "unpinAll");
@@ -886,6 +895,7 @@
* Clear any pending removal runnables.
*/
public void cancelAutoRemovalCallbacks(@Nullable String reason) {
+ mLogger.logAutoRemoveCancelRequest(this.mEntry, reason);
Runnable runnable = () -> {
final boolean removed = cancelAutoRemovalCallbackInternal();
@@ -900,6 +910,7 @@
public void scheduleAutoRemovalCallback(FinishTimeUpdater finishTimeCalculator,
@NonNull String reason) {
+ mLogger.logAutoRemoveRequest(this.mEntry, reason);
Runnable runnable = () -> {
long delayMs = finishTimeCalculator.updateAndGetTimeRemaining();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
index f6154afe..a306606 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
@@ -58,6 +58,14 @@
})
}
+ fun logShowNotificationRequest(entry: NotificationEntry) {
+ buffer.log(TAG, INFO, {
+ str1 = entry.logKey
+ }, {
+ "request: show notification $str1"
+ })
+ }
+
fun logShowNotification(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
str1 = entry.logKey
@@ -76,6 +84,15 @@
})
}
+ fun logAutoRemoveRequest(entry: NotificationEntry, reason: String) {
+ buffer.log(TAG, INFO, {
+ str1 = entry.logKey
+ str2 = reason
+ }, {
+ "request: reschedule auto remove of $str1 reason: $str2"
+ })
+ }
+
fun logAutoRemoveRescheduled(entry: NotificationEntry, delayMillis: Long, reason: String) {
buffer.log(TAG, INFO, {
str1 = entry.logKey
@@ -86,6 +103,15 @@
})
}
+ fun logAutoRemoveCancelRequest(entry: NotificationEntry, reason: String?) {
+ buffer.log(TAG, INFO, {
+ str1 = entry.logKey
+ str2 = reason ?: "unknown"
+ }, {
+ "request: cancel auto remove of $str1 reason: $str2"
+ })
+ }
+
fun logAutoRemoveCanceled(entry: NotificationEntry, reason: String?) {
buffer.log(TAG, INFO, {
str1 = entry.logKey
@@ -95,6 +121,38 @@
})
}
+ fun logRemoveEntryRequest(key: String) {
+ buffer.log(TAG, INFO, {
+ str1 = logKey(key)
+ }, {
+ "request: remove entry $str1"
+ })
+ }
+
+ fun logRemoveEntry(key: String) {
+ buffer.log(TAG, INFO, {
+ str1 = logKey(key)
+ }, {
+ "remove entry $str1"
+ })
+ }
+
+ fun logUnpinEntryRequest(key: String) {
+ buffer.log(TAG, INFO, {
+ str1 = logKey(key)
+ }, {
+ "request: unpin entry $str1"
+ })
+ }
+
+ fun logUnpinEntry(key: String) {
+ buffer.log(TAG, INFO, {
+ str1 = logKey(key)
+ }, {
+ "unpin entry $str1"
+ })
+ }
+
fun logRemoveNotification(key: String, releaseImmediately: Boolean) {
buffer.log(TAG, INFO, {
str1 = logKey(key)
@@ -112,6 +170,16 @@
})
}
+ fun logUpdateNotificationRequest(key: String, alert: Boolean, hasEntry: Boolean) {
+ buffer.log(TAG, INFO, {
+ str1 = logKey(key)
+ bool1 = alert
+ bool2 = hasEntry
+ }, {
+ "request: update notification $str1 alert: $bool1 hasEntry: $bool2 reason: $str2"
+ })
+ }
+
fun logUpdateNotification(key: String, alert: Boolean, hasEntry: Boolean) {
buffer.log(TAG, INFO, {
str1 = logKey(key)
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/DisposableHandleExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/DisposableHandleExt.kt
index 909a18be..97d957d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/DisposableHandleExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/DisposableHandleExt.kt
@@ -17,8 +17,14 @@
package com.android.systemui.util.kotlin
import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.Job
import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.launch
/**
* Suspends to keep getting updates until cancellation. Once cancelled, mark this as eligible for
@@ -42,3 +48,22 @@
dispose()
}
}
+
+/**
+ * This will [launch], run [onLaunch] to get a [DisposableHandle], and finally
+ * [awaitCancellationThenDispose][DisposableHandle.awaitCancellationThenDispose]. This can be used
+ * to structure self-disposing code which attaches listeners, for example in ViewBinders:
+ * ```
+ * suspend fun bind(view: MyView, viewModel: MyViewModel) = coroutineScope {
+ * launchAndDispose {
+ * view.setOnClickListener { viewModel.handleClick() }
+ * DisposableHandle { view.setOnClickListener(null) }
+ * }
+ * }
+ * ```
+ */
+inline fun CoroutineScope.launchAndDispose(
+ context: CoroutineContext = EmptyCoroutineContext,
+ start: CoroutineStart = CoroutineStart.DEFAULT,
+ crossinline onLaunch: () -> DisposableHandle
+): Job = launch(context, start) { onLaunch().awaitCancellationThenDispose() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt
deleted file mode 100644
index e044eec..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt
+++ /dev/null
@@ -1,141 +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.mediaprojection.permission
-
-import android.app.AlertDialog
-import android.media.projection.MediaProjectionConfig
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.WindowManager
-import android.widget.Spinner
-import android.widget.TextView
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
-import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.phone.AlertDialogWithDelegate
-import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.util.mockito.mock
-import junit.framework.Assert.assertEquals
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.`when` as whenever
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class MediaProjectionPermissionDialogDelegateTest : SysuiTestCase() {
-
- private lateinit var dialog: AlertDialog
-
- private val flags = mock<FeatureFlagsClassic>()
- private val onStartRecordingClicked = mock<Runnable>()
- private val mediaProjectionMetricsLogger = mock<MediaProjectionMetricsLogger>()
-
- private val mediaProjectionConfig: MediaProjectionConfig =
- MediaProjectionConfig.createConfigForDefaultDisplay()
- private val appName: String = "testApp"
- private val hostUid: Int = 12345
-
- private val resIdSingleApp = R.string.screen_share_permission_dialog_option_single_app
- private val resIdFullScreen = R.string.screen_share_permission_dialog_option_entire_screen
- private val resIdSingleAppDisabled =
- R.string.media_projection_entry_app_permission_dialog_single_app_disabled
-
- @Before
- fun setUp() {
- whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
- }
-
- @After
- fun teardown() {
- if (::dialog.isInitialized) {
- dialog.dismiss()
- }
- }
-
- @Test
- fun showDialog_forceShowPartialScreenShareFalse() {
- // Set up dialog with MediaProjectionConfig.createConfigForDefaultDisplay() and
- // overrideDisableSingleAppOption = false
- val overrideDisableSingleAppOption = false
- setUpAndShowDialog(overrideDisableSingleAppOption)
-
- val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner)
- val secondOptionText =
- spinner.adapter
- .getDropDownView(1, null, spinner)
- .findViewById<TextView>(android.R.id.text2)
- ?.text
-
- // check that the first option is full screen and enabled
- assertEquals(context.getString(resIdFullScreen), spinner.selectedItem)
-
- // check that the second option is single app and disabled
- assertEquals(context.getString(resIdSingleAppDisabled, appName), secondOptionText)
- }
-
- @Test
- fun showDialog_forceShowPartialScreenShareTrue() {
- // Set up dialog with MediaProjectionConfig.createConfigForDefaultDisplay() and
- // overrideDisableSingleAppOption = true
- val overrideDisableSingleAppOption = true
- setUpAndShowDialog(overrideDisableSingleAppOption)
-
- val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner)
- val secondOptionText =
- spinner.adapter
- .getDropDownView(1, null, spinner)
- .findViewById<TextView>(android.R.id.text1)
- ?.text
-
- // check that the first option is single app and enabled
- assertEquals(context.getString(resIdSingleApp), spinner.selectedItem)
-
- // check that the second option is full screen and enabled
- assertEquals(context.getString(resIdFullScreen), secondOptionText)
- }
-
- private fun setUpAndShowDialog(overrideDisableSingleAppOption: Boolean) {
- val delegate =
- MediaProjectionPermissionDialogDelegate(
- context,
- mediaProjectionConfig,
- {},
- onStartRecordingClicked,
- appName,
- overrideDisableSingleAppOption,
- hostUid,
- mediaProjectionMetricsLogger
- )
-
- dialog = AlertDialogWithDelegate(context, R.style.Theme_SystemUI_Dialog, delegate)
- SystemUIDialog.applyFlags(dialog)
- SystemUIDialog.setDialogSize(dialog)
-
- dialog.window?.addSystemFlags(
- WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS
- )
-
- delegate.onCreate(dialog, savedInstanceState = null)
- dialog.show()
- }
-}
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 cd8be57..912ecb3 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
@@ -94,7 +94,6 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
import com.android.systemui.statusbar.notification.stack.NotificationSwipeHelper.NotificationCallback;
-import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor;
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -161,7 +160,6 @@
@Mock private VisibilityLocationProviderDelegator mVisibilityLocationProviderDelegator;
@Mock private ShadeController mShadeController;
@Mock private Provider<WindowRootView> mWindowRootView;
- @Mock private NotificationStackAppearanceInteractor mNotificationStackAppearanceInteractor;
private final StackStateLogger mStackLogger = new StackStateLogger(logcatLogBuffer(),
logcatLogBuffer());
private final NotificationStackScrollLogger mLogger = new NotificationStackScrollLogger(
@@ -1016,7 +1014,6 @@
mViewBinder,
mShadeController,
mWindowRootView,
- mNotificationStackAppearanceInteractor,
mKosmos.getInteractionJankMonitor(),
mStackLogger,
mLogger,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorKosmos.kt
index d791e94..12165cd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorKosmos.kt
@@ -20,4 +20,4 @@
import com.android.systemui.kosmos.Kosmos
val Kosmos.keyguardClockInteractor by
- Kosmos.Fixture { KeyguardClockInteractor(keyguardClockRepository = keyguardClockRepository) }
+ Kosmos.Fixture { KeyguardClockInteractor(keyguardClockRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModelKosmos.kt
similarity index 92%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModelKosmos.kt
index bada2a6..10cc136 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModelKosmos.kt
@@ -23,8 +23,8 @@
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
-val Kosmos.notificationStackAppearanceViewModel by Fixture {
- NotificationStackAppearanceViewModel(
+val Kosmos.notificationScrollViewModel by Fixture {
+ NotificationScrollViewModel(
dumpManager = dumpManager,
stackAppearanceInteractor = notificationStackAppearanceInteractor,
shadeInteractor = shadeInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
index 29faa58..b249211 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import com.android.systemui.dump.dumpManager
import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
@@ -26,6 +27,7 @@
val Kosmos.notificationsPlaceholderViewModel by Fixture {
NotificationsPlaceholderViewModel(
+ dumpManager = dumpManager,
interactor = notificationStackAppearanceInteractor,
shadeInteractor = shadeInteractor,
flags = sceneContainerFlags,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index de0cc65..d2de835 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -43,6 +43,7 @@
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -55,6 +56,7 @@
keyguardInteractor = keyguardInteractor,
keyguardTransitionInteractor = keyguardTransitionInteractor,
shadeInteractor = shadeInteractor,
+ notificationStackAppearanceInteractor = notificationStackAppearanceInteractor,
alternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel,
aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
aodToOccludedTransitionViewModel = aodToOccludedTransitionViewModel,
diff --git a/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodAndroidApiTest.java b/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodAndroidApiTest.java
new file mode 100644
index 0000000..c11c1bb
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodAndroidApiTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.platform.test.ravenwood.bivalenttest;
+
+import static org.junit.Assert.assertEquals;
+
+import android.util.ArrayMap;
+import android.util.Size;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+
+// Tests for calling simple Android APIs.
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodAndroidApiTest {
+ @Test
+ public void testArrayMapSimple() {
+ final Map<String, String> map = new ArrayMap<>();
+
+ map.put("key1", "value1");
+ assertEquals("value1", map.get("key1"));
+ }
+
+ @Test
+ public void testSizeSimple() {
+ final var size = new Size(1, 2);
+
+ assertEquals(2, size.getHeight());
+ }
+}
diff --git a/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java b/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java
new file mode 100644
index 0000000..6f2465c
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.platform.test.ravenwood.bivalenttest;
+
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.platform.test.ravenwood.RavenwoodClassRule;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@DisabledOnRavenwood
+public class RavenwoodClassRuleDeviceOnlyTest {
+ @ClassRule
+ public static final RavenwoodClassRule sRavenwood = new RavenwoodClassRule();
+
+ @Test
+ public void testDeviceOnly() {
+ Assert.assertFalse(RavenwoodRule.isOnRavenwood());
+ }
+}
diff --git a/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleRavenwoodOnlyTest.java b/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleRavenwoodOnlyTest.java
new file mode 100644
index 0000000..21b31d1
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/platform/test/ravenwood/bivalenttest/RavenwoodClassRuleRavenwoodOnlyTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.platform.test.ravenwood.bivalenttest;
+
+import android.platform.test.ravenwood.RavenwoodClassRule;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+// TODO: atest RavenwoodBivalentTest_device fails with the following message.
+// `RUNNER ERROR: Instrumentation reported numtests=7 but only ran 6`
+// @android.platform.test.annotations.DisabledOnNonRavenwood
+// Figure it out and then make DisabledOnNonRavenwood support TYPEs as well.
+@Ignore
+public class RavenwoodClassRuleRavenwoodOnlyTest {
+ @ClassRule
+ public static final RavenwoodClassRule sRavenwood = new RavenwoodClassRule();
+
+ @Test
+ public void testRavenwoodOnly() {
+ Assert.assertTrue(RavenwoodRule.isOnRavenwood());
+ }
+}
diff --git a/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java b/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java
index 2cd585f..4ee9a9c 100644
--- a/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java
+++ b/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java
@@ -40,7 +40,7 @@
public final RuleChain chain = RuleChain.outerRule(mThrown).around(mRavenwood);
public RavenwoodTestRunnerValidationTest() {
- Assume.assumeTrue(mRavenwood._ravenwood_private$isOptionalValidationEnabled());
+ Assume.assumeTrue(RavenwoodRule._$RavenwoodPrivate.isOptionalValidationEnabled());
// Because RavenwoodRule will throw this error before executing the test method,
// we can't do it in the test method itself.
// So instead, we initialize it here.
diff --git a/ravenwood/junit-src/android/platform/test/annotations/DisabledOnNonRavenwood.java b/ravenwood/junit-src/android/platform/test/annotations/DisabledOnNonRavenwood.java
index 8ca34ba..9d47f3a 100644
--- a/ravenwood/junit-src/android/platform/test/annotations/DisabledOnNonRavenwood.java
+++ b/ravenwood/junit-src/android/platform/test/annotations/DisabledOnNonRavenwood.java
@@ -31,13 +31,17 @@
* which means if a test class has this annotation, you can't negate it in subclasses or
* on a per-method basis.
*
+ * THIS ANNOTATION CANNOT BE ADDED TO CLASSES AT THIS PONINT.
+ * See {@link com.android.platform.test.ravenwood.bivalenttest.RavenwoodClassRuleRavenwoodOnlyTest}
+ * for the reason.
+ *
* The {@code RAVENWOOD_RUN_DISABLED_TESTS} environmental variable won't work because it won't be
* propagated to the device. (We may support it in the future, possibly using a debug. sysprop.)
*
* @hide
*/
@Inherited
-@Target({ElementType.METHOD, ElementType.TYPE})
+@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DisabledOnNonRavenwood {
/**
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java
index 9a4d488..f4b7ec36 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java
@@ -25,6 +25,7 @@
import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.EnabledOnRavenwood;
+import org.junit.Assert;
import org.junit.Assume;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
@@ -41,27 +42,16 @@
public class RavenwoodClassRule implements TestRule {
@Override
public Statement apply(Statement base, Description description) {
- // No special treatment when running outside Ravenwood; run tests as-is
if (!IS_ON_RAVENWOOD) {
- Assume.assumeTrue(shouldEnableOnDevice(description));
- return base;
- }
-
- if (ENABLE_PROBE_IGNORED) {
+ // This should be "Assume", not Assert, but if we use assume here, the device side
+ // test runner would complain.
+ // See the TODO comment in RavenwoodClassRuleRavenwoodOnlyTest.
+ Assert.assertTrue(shouldEnableOnDevice(description));
+ } else if (ENABLE_PROBE_IGNORED) {
Assume.assumeFalse(shouldStillIgnoreInProbeIgnoreMode(description));
- // Pass through to possible underlying RavenwoodRule for both environment
- // configuration and handling method-level annotations
- return base;
} else {
- return new Statement() {
- @Override
- public void evaluate() throws Throwable {
- Assume.assumeTrue(shouldEnableOnRavenwood(description));
- // Pass through to possible underlying RavenwoodRule for both environment
- // configuration and handling method-level annotations
- base.evaluate();
- }
- };
+ Assume.assumeTrue(shouldEnableOnRavenwood(description));
}
+ return base;
}
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index 52ea340..a2e8ec1 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -28,7 +28,6 @@
import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.EnabledOnRavenwood;
import android.platform.test.annotations.IgnoreUnderRavenwood;
-import android.util.ArraySet;
import org.junit.Assume;
import org.junit.rules.TestRule;
@@ -278,6 +277,9 @@
return false;
}
}
+ if (description.getTestClass().getAnnotation(DisabledOnNonRavenwood.class) != null) {
+ return false;
+ }
return true;
}
@@ -413,10 +415,9 @@
};
}
- /**
- * Do not use it outside ravenwood core classes.
- */
- public boolean _ravenwood_private$isOptionalValidationEnabled() {
- return ENABLE_OPTIONAL_VALIDATION;
+ public static class _$RavenwoodPrivate {
+ public static boolean isOptionalValidationEnabled() {
+ return ENABLE_OPTIONAL_VALIDATION;
+ }
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 5a996aa..06431fc 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -1112,10 +1112,10 @@
mParams.dump(fout, indent + indent);
fout.println(indent + "mVirtualDisplayIds: ");
synchronized (mVirtualDeviceLock) {
- fout.println(" mDevicePolicies: " + mDevicePolicies);
for (int i = 0; i < mVirtualDisplays.size(); i++) {
fout.println(indent + " " + mVirtualDisplays.keyAt(i));
}
+ fout.println(" mDevicePolicies: " + mDevicePolicies);
fout.println(indent + "mDefaultShowPointerIcon: " + mDefaultShowPointerIcon);
}
mInputController.dump(fout);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 951f676..656611a 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1820,18 +1820,17 @@
+ "received with null profile proxy: "
+ btInfo)).printLog(TAG));
} else {
- @AudioSystem.AudioFormatNativeEnumForBtCodec final int codec =
+ final Pair<Integer, Boolean> codecAndChanged =
mBtHelper.getCodecWithFallback(btInfo.mDevice,
btInfo.mProfile, btInfo.mIsLeOutput,
"MSG_L_SET_BT_ACTIVE_DEVICE");
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
- mDeviceInventory.onSetBtActiveDevice(btInfo, codec,
- (btInfo.mProfile
- != BluetoothProfile.LE_AUDIO
+ mDeviceInventory.onSetBtActiveDevice(btInfo, codecAndChanged.first,
+ (btInfo.mProfile != BluetoothProfile.LE_AUDIO
|| btInfo.mIsLeOutput)
- ? mAudioService.getBluetoothContextualVolumeStream()
- : AudioSystem.STREAM_DEFAULT);
+ ? mAudioService.getBluetoothContextualVolumeStream()
+ : AudioSystem.STREAM_DEFAULT);
if (btInfo.mProfile == BluetoothProfile.LE_AUDIO
|| btInfo.mProfile
== BluetoothProfile.HEARING_AID) {
@@ -1866,13 +1865,13 @@
break;
case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE: {
final BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj;
- @AudioSystem.AudioFormatNativeEnumForBtCodec final int codec =
- mBtHelper.getCodecWithFallback(btInfo.mDevice,
- btInfo.mProfile, btInfo.mIsLeOutput,
- "MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE");
+ final Pair<Integer, Boolean> codecAndChanged = mBtHelper.getCodecWithFallback(
+ btInfo.mDevice, btInfo.mProfile, btInfo.mIsLeOutput,
+ "MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE");
synchronized (mDeviceStateLock) {
- mDeviceInventory.onBluetoothDeviceConfigChange(
- btInfo, codec, BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
+ mDeviceInventory.onBluetoothDeviceConfigChange(btInfo,
+ codecAndChanged.first, codecAndChanged.second,
+ BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
}
} break;
case MSG_BROADCAST_AUDIO_BECOMING_NOISY:
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 14428c4..ebe9c63 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -868,7 +868,8 @@
@GuardedBy("mDeviceBroker.mDeviceStateLock")
/*package*/ void onBluetoothDeviceConfigChange(
@NonNull AudioDeviceBroker.BtDeviceInfo btInfo,
- @AudioSystem.AudioFormatNativeEnumForBtCodec int codec, int event) {
+ @AudioSystem.AudioFormatNativeEnumForBtCodec int codec,
+ boolean codecChanged, int event) {
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId
+ "onBluetoothDeviceConfigChange")
.set(MediaMetrics.Property.EVENT, BtHelper.deviceEventToString(event));
@@ -916,14 +917,12 @@
if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) {
- boolean codecChange = false;
if (btInfo.mProfile == BluetoothProfile.A2DP
|| btInfo.mProfile == BluetoothProfile.LE_AUDIO
|| btInfo.mProfile == BluetoothProfile.LE_AUDIO_BROADCAST) {
- if (di.mDeviceCodecFormat != codec) {
+ if (codecChanged) {
di.mDeviceCodecFormat = codec;
mConnectedDevices.replace(key, di);
- codecChange = true;
final int res = mAudioSystem.handleDeviceConfigChange(
btInfo.mAudioSystemDevice, address,
BtHelper.getName(btDevice), codec);
@@ -947,7 +946,7 @@
}
}
}
- if (!codecChange) {
+ if (!codecChanged) {
updateBluetoothPreferredModes_l(btDevice /*connectedDevice*/);
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index ed58c40..40099581 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -47,6 +47,7 @@
import static com.android.media.audio.Flags.disablePrescaleAbsoluteVolume;
import static com.android.media.audio.Flags.ringerModeAffectsAlarm;
import static com.android.media.audio.Flags.setStreamVolumeOrder;
+import static com.android.media.audio.Flags.vgsVssSyncMuteOrder;
import static com.android.server.audio.SoundDoseHelper.ACTION_CHECK_MUSIC_ACTIVE;
import static com.android.server.utils.EventLogger.Event.ALOGE;
import static com.android.server.utils.EventLogger.Event.ALOGI;
@@ -4544,6 +4545,8 @@
+ setStreamVolumeOrder());
pw.println("\tandroid.media.audio.roForegroundAudioControl:"
+ roForegroundAudioControl());
+ pw.println("\tcom.android.media.audio.vgsVssSyncMuteOrder:"
+ + vgsVssSyncMuteOrder());
}
private void dumpAudioMode(PrintWriter pw) {
@@ -8317,13 +8320,23 @@
synced = true;
continue;
}
+ if (vgsVssSyncMuteOrder()) {
+ if ((isMuted() != streamMuted) && isVssMuteBijective(
+ stream)) {
+ mStreamStates[stream].mute(isMuted(),
+ "VGS.applyAllVolumes#1");
+ }
+ }
if (indexForStream != index) {
mStreamStates[stream].setIndex(index * 10, device, caller,
true /*hasModifyAudioSettings*/);
}
- if ((isMuted() != streamMuted) && isVssMuteBijective(stream)) {
- mStreamStates[stream].mute(isMuted(),
- "VGS.applyAllVolumes#1");
+ if (!vgsVssSyncMuteOrder()) {
+ if ((isMuted() != streamMuted) && isVssMuteBijective(
+ stream)) {
+ mStreamStates[stream].mute(isMuted(),
+ "VGS.applyAllVolumes#1");
+ }
}
}
}
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index f3a5fdb..edeabdc 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -98,9 +98,16 @@
private @Nullable BluetoothLeAudio mLeAudio;
+ private @Nullable BluetoothLeAudioCodecConfig mLeAudioCodecConfig;
+
// Reference to BluetoothA2dp to query for AbsoluteVolume.
private @Nullable BluetoothA2dp mA2dp;
+ private @Nullable BluetoothCodecConfig mA2dpCodecConfig;
+
+ private @AudioSystem.AudioFormatNativeEnumForBtCodec
+ int mLeAudioBroadcastCodec = AudioSystem.AUDIO_FORMAT_DEFAULT;
+
// If absolute volume is supported in AVRCP device
private boolean mAvrcpAbsVolSupported = false;
@@ -265,12 +272,15 @@
}
}
- /*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec int getCodec(
+ private synchronized Pair<Integer, Boolean> getCodec(
@NonNull BluetoothDevice device, @AudioService.BtProfile int profile) {
+
switch (profile) {
case BluetoothProfile.A2DP: {
+ boolean changed = mA2dpCodecConfig != null;
if (mA2dp == null) {
- return AudioSystem.AUDIO_FORMAT_DEFAULT;
+ mA2dpCodecConfig = null;
+ return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed);
}
BluetoothCodecStatus btCodecStatus = null;
try {
@@ -279,17 +289,24 @@
Log.e(TAG, "Exception while getting status of " + device, e);
}
if (btCodecStatus == null) {
- return AudioSystem.AUDIO_FORMAT_DEFAULT;
+ mA2dpCodecConfig = null;
+ return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed);
}
final BluetoothCodecConfig btCodecConfig = btCodecStatus.getCodecConfig();
if (btCodecConfig == null) {
- return AudioSystem.AUDIO_FORMAT_DEFAULT;
+ mA2dpCodecConfig = null;
+ return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed);
}
- return AudioSystem.bluetoothA2dpCodecToAudioFormat(btCodecConfig.getCodecType());
+ changed = !btCodecConfig.equals(mA2dpCodecConfig);
+ mA2dpCodecConfig = btCodecConfig;
+ return new Pair<>(AudioSystem.bluetoothA2dpCodecToAudioFormat(
+ btCodecConfig.getCodecType()), changed);
}
case BluetoothProfile.LE_AUDIO: {
+ boolean changed = mLeAudioCodecConfig != null;
if (mLeAudio == null) {
- return AudioSystem.AUDIO_FORMAT_DEFAULT;
+ mLeAudioCodecConfig = null;
+ return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed);
}
BluetoothLeAudioCodecStatus btLeCodecStatus = null;
int groupId = mLeAudio.getGroupId(device);
@@ -299,42 +316,54 @@
Log.e(TAG, "Exception while getting status of " + device, e);
}
if (btLeCodecStatus == null) {
- return AudioSystem.AUDIO_FORMAT_DEFAULT;
+ mLeAudioCodecConfig = null;
+ return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed);
}
BluetoothLeAudioCodecConfig btLeCodecConfig =
btLeCodecStatus.getOutputCodecConfig();
if (btLeCodecConfig == null) {
- return AudioSystem.AUDIO_FORMAT_DEFAULT;
+ mLeAudioCodecConfig = null;
+ return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed);
}
- return AudioSystem.bluetoothLeCodecToAudioFormat(btLeCodecConfig.getCodecType());
+ changed = !btLeCodecConfig.equals(mLeAudioCodecConfig);
+ mLeAudioCodecConfig = btLeCodecConfig;
+ return new Pair<>(AudioSystem.bluetoothLeCodecToAudioFormat(
+ btLeCodecConfig.getCodecType()), changed);
+ }
+ case BluetoothProfile.LE_AUDIO_BROADCAST: {
+ // We assume LC3 for LE Audio broadcast codec as there is no API to get the codec
+ // config on LE Broadcast profile proxy.
+ boolean changed = mLeAudioBroadcastCodec != AudioSystem.AUDIO_FORMAT_LC3;
+ mLeAudioBroadcastCodec = AudioSystem.AUDIO_FORMAT_LC3;
+ return new Pair<>(mLeAudioBroadcastCodec, changed);
}
default:
- return AudioSystem.AUDIO_FORMAT_DEFAULT;
+ return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, false);
}
}
- /*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec
- int getCodecWithFallback(
- @NonNull BluetoothDevice device, @AudioService.BtProfile int profile,
- boolean isLeOutput, @NonNull String source) {
+ /*package*/ synchronized Pair<Integer, Boolean>
+ getCodecWithFallback(@NonNull BluetoothDevice device,
+ @AudioService.BtProfile int profile,
+ boolean isLeOutput, @NonNull String source) {
// For profiles other than A2DP and LE Audio output, the audio codec format must be
// AUDIO_FORMAT_DEFAULT as native audio policy manager expects a specific audio format
// only if audio HW module selection based on format is supported for the device type.
if (!(profile == BluetoothProfile.A2DP
|| (isLeOutput && ((profile == BluetoothProfile.LE_AUDIO)
|| (profile == BluetoothProfile.LE_AUDIO_BROADCAST))))) {
- return AudioSystem.AUDIO_FORMAT_DEFAULT;
+ return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, false);
}
- @AudioSystem.AudioFormatNativeEnumForBtCodec int codec =
+ Pair<Integer, Boolean> codecAndChanged =
getCodec(device, profile);
- if (codec == AudioSystem.AUDIO_FORMAT_DEFAULT) {
+ if (codecAndChanged.first == AudioSystem.AUDIO_FORMAT_DEFAULT) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"getCodec DEFAULT from " + source + " fallback to "
+ (profile == BluetoothProfile.A2DP ? "SBC" : "LC3")));
- return profile == BluetoothProfile.A2DP
- ? AudioSystem.AUDIO_FORMAT_SBC : AudioSystem.AUDIO_FORMAT_LC3;
+ return new Pair<>(profile == BluetoothProfile.A2DP
+ ? AudioSystem.AUDIO_FORMAT_SBC : AudioSystem.AUDIO_FORMAT_LC3, true);
}
- return codec;
+ return codecAndChanged;
}
// @GuardedBy("mDeviceBroker.mSetModeLock")
@@ -539,15 +568,19 @@
break;
case BluetoothProfile.A2DP:
mA2dp = null;
+ mA2dpCodecConfig = null;
break;
case BluetoothProfile.HEARING_AID:
mHearingAid = null;
break;
case BluetoothProfile.LE_AUDIO:
mLeAudio = null;
+ mLeAudioCodecConfig = null;
+ break;
+ case BluetoothProfile.LE_AUDIO_BROADCAST:
+ mLeAudioBroadcastCodec = AudioSystem.AUDIO_FORMAT_DEFAULT;
break;
case BluetoothProfile.A2DP_SINK:
- case BluetoothProfile.LE_AUDIO_BROADCAST:
// nothing to do in BtHelper
break;
default:
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 10030b3..dc0e80c 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -117,6 +117,7 @@
() -> mNormalBrightnessModeController.setAutoBrightnessState(state),
() -> mHbmController.setAutoBrightnessEnabled(state)
);
+ mHdrClamper.setAutoBrightnessState(state);
}
void onBrightnessChanged(float brightness, float unthrottledBrightness,
diff --git a/services/core/java/com/android/server/display/NormalBrightnessModeController.java b/services/core/java/com/android/server/display/NormalBrightnessModeController.java
index 135ebd8..e94cf00 100644
--- a/services/core/java/com/android/server/display/NormalBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/NormalBrightnessModeController.java
@@ -79,10 +79,12 @@
maxBrightnessPoints = mMaxBrightnessLimits.get(BrightnessLimitMapType.ADAPTIVE);
}
- if (maxBrightnessPoints == null) {
+ // AutoBrightnessController sends ambientLux values *only* when auto brightness enabled.
+ // Temporary disabling this Controller if auto brightness is off, to avoid capping
+ // brightness based on stale ambient lux. The issue is tracked here: b/322445088
+ if (mAutoBrightnessEnabled && maxBrightnessPoints == null) {
maxBrightnessPoints = mMaxBrightnessLimits.get(BrightnessLimitMapType.DEFAULT);
}
-
if (maxBrightnessPoints != null) {
for (Map.Entry<Float, Float> brightnessPoint : maxBrightnessPoints.entrySet()) {
float ambientBoundary = brightnessPoint.getKey();
diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
index 01a8d360a..f1cb66c 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
@@ -24,6 +24,7 @@
import android.view.SurfaceControlHdrLayerInfoListener;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.AutomaticBrightnessController;
import com.android.server.display.config.HdrBrightnessData;
import java.io.PrintWriter;
@@ -56,6 +57,8 @@
private float mTransitionRate = -1f;
private float mDesiredTransitionRate = -1f;
+ private boolean mAutoBrightnessEnabled = false;
+
public HdrClamper(BrightnessClamperController.ClamperChangeListener clamperChangeListener,
Handler handler) {
this(clamperChangeListener, handler, new Injector());
@@ -122,6 +125,18 @@
recalculateBrightnessCap(data, mAmbientLux, mHdrVisible);
}
+ /**
+ * Sets state of auto brightness to temporary disabling this Clamper if auto brightness is off.
+ * The issue is tracked here: b/322445088
+ */
+ public void setAutoBrightnessState(int state) {
+ boolean isEnabled = state == AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
+ if (isEnabled != mAutoBrightnessEnabled) {
+ mAutoBrightnessEnabled = isEnabled;
+ recalculateBrightnessCap(mHdrBrightnessData, mAmbientLux, mHdrVisible);
+ }
+ }
+
/** Clean up all resources */
@SuppressLint("AndroidFrameworkRequiresPermission")
public void stop() {
@@ -145,6 +160,7 @@
: mHdrBrightnessData.toString()));
pw.println(" mHdrListener registered=" + (mRegisteredDisplayToken != null));
pw.println(" mAmbientLux=" + mAmbientLux);
+ pw.println(" mAutoBrightnessEnabled=" + mAutoBrightnessEnabled);
}
private void reset() {
@@ -163,7 +179,10 @@
private void recalculateBrightnessCap(HdrBrightnessData data, float ambientLux,
boolean hdrVisible) {
- if (data == null || !hdrVisible) {
+ // AutoBrightnessController sends ambientLux values *only* when auto brightness enabled.
+ // Temporary disabling this Clamper if auto brightness is off, to avoid capping
+ // brightness based on stale ambient lux. The issue is tracked here: b/322445088
+ if (data == null || !hdrVisible || !mAutoBrightnessEnabled) {
reset();
return;
}
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 67d3fe9..db83d4b 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -232,7 +232,9 @@
if (!mRunning) {
return false;
}
- if (!getSessionInfos().isEmpty() || mIsManagerScanning) {
+ boolean bindDueToManagerScan =
+ mIsManagerScanning && Flags.enablePreventionOfManagerScansWhenNoAppsScan();
+ if (!getSessionInfos().isEmpty() || bindDueToManagerScan) {
// We bind if any manager is scanning (regardless of whether an app is scanning) to give
// the opportunity for providers to publish routing sessions that were established
// directly between the app and the provider (typically via AndroidX MediaRouter). See
diff --git a/services/core/java/com/android/server/media/TEST_MAPPING b/services/core/java/com/android/server/media/TEST_MAPPING
index b3e5b9e..43e2afd 100644
--- a/services/core/java/com/android/server/media/TEST_MAPPING
+++ b/services/core/java/com/android/server/media/TEST_MAPPING
@@ -2,9 +2,7 @@
"presubmit": [
{
"name": "CtsMediaBetterTogetherTestCases"
- }
- ],
- "postsubmit": [
+ },
{
"name": "MediaRouterServiceTests"
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BundlePolicySerializer.java b/services/devicepolicy/java/com/android/server/devicepolicy/BundlePolicySerializer.java
index 82f9aad..d24afabe 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BundlePolicySerializer.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BundlePolicySerializer.java
@@ -92,7 +92,7 @@
while (count > 0 && (type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG
&& parser.getName().equals(TAG_VALUE)) {
- values.add(parser.nextText().trim());
+ values.add(parser.nextText());
count--;
}
}
@@ -111,7 +111,7 @@
restrictions.putParcelableArray(key,
bundleList.toArray(new Bundle[bundleList.size()]));
} else {
- String value = parser.nextText().trim();
+ String value = parser.nextText();
if (ATTR_TYPE_BOOLEAN.equals(valType)) {
restrictions.putBoolean(key, Boolean.parseBoolean(value));
} else if (ATTR_TYPE_INTEGER.equals(valType)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b34092c..3dd7b54 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -11509,10 +11509,17 @@
@Override
public void setApplicationRestrictions(ComponentName who, String callerPackage,
- String packageName, Bundle restrictions) {
+ String packageName, Bundle restrictions, boolean parent) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_APPLICATION_RESTRICTIONS);
+ // This check is eventually made in UMS, checking here to fail early.
+ String validationResult =
+ FrameworkParsingPackageUtils.validateName(packageName, false, false);
+ if (validationResult != null) {
+ throw new IllegalArgumentException("Invalid package name: " + validationResult);
+ }
+
if (isUnicornFlagEnabled()) {
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
who,
@@ -11520,12 +11527,6 @@
caller.getPackageName(),
caller.getUserId()
);
- // This check is eventually made in UMS, checking here to fail early.
- String validationResult =
- FrameworkParsingPackageUtils.validateName(packageName, false, false);
- if (validationResult != null) {
- throw new IllegalArgumentException("Invalid package name: " + validationResult);
- }
if (restrictions == null || restrictions.isEmpty()) {
mDevicePolicyEngine.removeLocalPolicy(
@@ -11541,6 +11542,57 @@
}
setBackwardsCompatibleAppRestrictions(
caller, packageName, restrictions, caller.getUserHandle());
+ } else if (Flags.dmrhCanSetAppRestriction()) {
+ final boolean isRoleHolder;
+ if (who != null) {
+ // DO or PO
+ Preconditions.checkCallAuthorization(
+ (isProfileOwner(caller) || isDefaultDeviceOwner(caller)));
+ Preconditions.checkCallAuthorization(!parent,
+ "DO or PO cannot call this on parent");
+ // Caller has opted to be treated as DPC (by passing a non-null who), so don't
+ // consider it as the DMRH, even if the caller is both the DPC and the DMRH.
+ isRoleHolder = false;
+ } else {
+ // Delegates, or the DMRH. Only DMRH can call this on COPE parent
+ isRoleHolder = isCallerDevicePolicyManagementRoleHolder(caller);
+ if (parent) {
+ Preconditions.checkCallAuthorization(isRoleHolder);
+ Preconditions.checkState(isOrganizationOwnedDeviceWithManagedProfile(),
+ "Role Holder can only operate parent app restriction on COPE devices");
+ } else {
+ Preconditions.checkCallAuthorization(isRoleHolder
+ || isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS));
+ }
+ }
+ // DMRH caller uses policy engine, others still use legacy code path
+ if (isRoleHolder) {
+ EnforcingAdmin enforcingAdmin = getEnforcingAdminForCaller(/* who */ null,
+ caller.getPackageName());
+ int affectedUserId = parent
+ ? getProfileParentId(caller.getUserId()) : caller.getUserId();
+ if (restrictions == null || restrictions.isEmpty()) {
+ mDevicePolicyEngine.removeLocalPolicy(
+ PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
+ enforcingAdmin,
+ affectedUserId);
+ } else {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
+ enforcingAdmin,
+ new BundlePolicyValue(restrictions),
+ affectedUserId);
+ }
+ Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
+ changeIntent.setPackage(packageName);
+ changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mContext.sendBroadcastAsUser(changeIntent, UserHandle.of(affectedUserId));
+ } else {
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ mUserManager.setApplicationRestrictions(packageName, restrictions,
+ caller.getUserHandle());
+ });
+ }
} else {
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
&& (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
@@ -12872,7 +12924,7 @@
@Override
public Bundle getApplicationRestrictions(ComponentName who, String callerPackage,
- String packageName) {
+ String packageName, boolean parent) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
if (isUnicornFlagEnabled()) {
@@ -12891,6 +12943,50 @@
return Bundle.EMPTY;
}
return policies.get(enforcingAdmin).getValue();
+ } else if (Flags.dmrhCanSetAppRestriction()) {
+ final boolean isRoleHolder;
+ if (who != null) {
+ // Caller is DO or PO. They cannot call this on parent
+ Preconditions.checkCallAuthorization(!parent
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)));
+ // Caller has opted to be treated as DPC (by passing a non-null who), so don't
+ // consider it as the DMRH, even if the caller is both the DPC and the DMRH.
+ isRoleHolder = false;
+ } else {
+ // Caller is delegates or the DMRH. Only DMRH can call this on parent
+ isRoleHolder = isCallerDevicePolicyManagementRoleHolder(caller);
+ if (parent) {
+ Preconditions.checkCallAuthorization(isRoleHolder);
+ Preconditions.checkState(isOrganizationOwnedDeviceWithManagedProfile(),
+ "Role Holder can only operate parent app restriction on COPE devices");
+ } else {
+ Preconditions.checkCallAuthorization(isRoleHolder
+ || isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS));
+ }
+ }
+ if (isRoleHolder) {
+ EnforcingAdmin enforcingAdmin = getEnforcingAdminForCaller(/* who */ null,
+ caller.getPackageName());
+ int affectedUserId = parent
+ ? getProfileParentId(caller.getUserId()) : caller.getUserId();
+ LinkedHashMap<EnforcingAdmin, PolicyValue<Bundle>> policies =
+ mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
+ PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
+ affectedUserId);
+ if (!policies.containsKey(enforcingAdmin)) {
+ return Bundle.EMPTY;
+ }
+ return policies.get(enforcingAdmin).getValue();
+ } else {
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ Bundle bundle = mUserManager.getApplicationRestrictions(packageName,
+ caller.getUserHandle());
+ // if no restrictions were saved, mUserManager.getApplicationRestrictions
+ // returns null, but DPM method should return an empty Bundle as per JavaDoc
+ return bundle != null ? bundle : Bundle.EMPTY;
+ });
+ }
+
} else {
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
&& (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
@@ -15811,19 +15907,16 @@
for (EnforcingAdmin admin : policies.keySet()) {
restrictions.add(policies.get(admin).getValue());
}
- if (!restrictions.isEmpty()) {
- return restrictions;
- }
return mInjector.binderWithCleanCallingIdentity(() -> {
- // Could be a device that has a DPC that hasn't migrated yet, so just return any
+ // Could be a device that has a DPC that hasn't migrated yet, so also return any
// restrictions saved in userManager.
Bundle bundle = mUserManager.getApplicationRestrictions(
packageName, UserHandle.of(userId));
- if (bundle == null || bundle.isEmpty()) {
- return new ArrayList<>();
+ if (bundle != null && !bundle.isEmpty()) {
+ restrictions.add(bundle);
}
- return List.of(bundle);
+ return restrictions;
});
}
diff --git a/services/permission/TEST_MAPPING b/services/permission/TEST_MAPPING
index 00bfcd3..4de4a56 100644
--- a/services/permission/TEST_MAPPING
+++ b/services/permission/TEST_MAPPING
@@ -103,6 +103,28 @@
"include-filter": "android.permission.cts.PermissionMaxSdkVersionTest"
}
]
+ },
+ {
+ "name": "CtsVirtualDevicesAudioTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "include-filter": "android.virtualdevice.cts.audio.VirtualAudioPermissionTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsVirtualDevicesAppLaunchTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "include-filter": "android.virtualdevice.cts.applaunch.VirtualDevicePermissionTest"
+ }
+ ]
}
],
"imports": [
diff --git a/services/tests/displayservicetests/src/com/android/server/display/NormalBrightnessModeControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/NormalBrightnessModeControllerTest.java
index c379d6b..3fd3cef 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/NormalBrightnessModeControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/NormalBrightnessModeControllerTest.java
@@ -43,6 +43,11 @@
private final NormalBrightnessModeController mController = new NormalBrightnessModeController();
+ // AutoBrightnessController sends ambientLux values *only* when auto brightness enabled.
+ // NormalBrightnessModeController is temporary disabled if auto brightness is off,
+ // to avoid capping brightness based on stale ambient lux. Temporary disabling tests with
+ // auto brightness off and default config pres
+ // The issue is tracked here: b/322445088
@Keep
private static Object[][] brightnessData() {
return new Object[][]{
@@ -59,10 +64,10 @@
ImmutableMap.of(99f, 0.1f, 101f, 0.2f)
), 0.2f},
// Auto brightness - off, config only for default
- {100, AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, ImmutableMap.of(
- BrightnessLimitMapType.DEFAULT,
- ImmutableMap.of(99f, 0.1f, 101f, 0.2f)
- ), 0.2f},
+ // {100, AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, ImmutableMap.of(
+ // BrightnessLimitMapType.DEFAULT,
+ // ImmutableMap.of(99f, 0.1f, 101f, 0.2f)
+ // ), 0.2f},
// Auto brightness - off, config only for adaptive
{100, AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, ImmutableMap.of(
BrightnessLimitMapType.ADAPTIVE,
@@ -81,12 +86,12 @@
ImmutableMap.of(99f, 0.3f, 101f, 0.4f)
), 0.4f},
// Auto brightness - off, config for both
- {100, AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, ImmutableMap.of(
- BrightnessLimitMapType.DEFAULT,
- ImmutableMap.of(99f, 0.1f, 101f, 0.2f),
- BrightnessLimitMapType.ADAPTIVE,
- ImmutableMap.of(99f, 0.3f, 101f, 0.4f)
- ), 0.2f},
+ // {100, AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, ImmutableMap.of(
+ // BrightnessLimitMapType.DEFAULT,
+ // ImmutableMap.of(99f, 0.1f, 101f, 0.2f),
+ // BrightnessLimitMapType.ADAPTIVE,
+ // ImmutableMap.of(99f, 0.3f, 101f, 0.4f)
+ // ), 0.2f},
// Auto brightness - on, config for both, ambient high
{1000, AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, ImmutableMap.of(
BrightnessLimitMapType.DEFAULT,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
index 87fc7a4..39ffe5b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
@@ -33,6 +33,7 @@
import androidx.test.filters.SmallTest;
+import com.android.server.display.AutomaticBrightnessController;
import com.android.server.display.config.HdrBrightnessData;
import com.android.server.testutils.OffsettableClock;
import com.android.server.testutils.TestHandler;
@@ -230,6 +231,11 @@
}
private void configureClamper() {
+ // AutoBrightnessController sends ambientLux values *only* when auto brightness enabled.
+ // HdrClamper is temporary disabled if auto brightness is off.
+ // Temporary setting AutoBrightnessState to enabled for this test
+ // The issue is tracked here: b/322445088
+ mHdrClamper.setAutoBrightnessState(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED);
mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, MIN_HDR_PERCENT, mMockBinder);
mHdrChangeListener.onHdrVisible(true);
}
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp
index 30333da..682adbc 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -82,13 +82,30 @@
jarjar_rules: "jarjar-rules.txt",
}
-// Host-side stub generator tool.
-java_binary_host {
- name: "hoststubgen",
- main_class: "com.android.hoststubgen.Main",
+// For sharing the code with other tools
+java_library_host {
+ name: "hoststubgen-lib",
+ defaults: ["ravenwood-internal-only-visibility-java"],
srcs: ["src/**/*.kt"],
static_libs: [
"hoststubgen-helper-runtime",
+ ],
+ libs: [
+ "junit",
+ "ow2-asm",
+ "ow2-asm-analysis",
+ "ow2-asm-commons",
+ "ow2-asm-tree",
+ "ow2-asm-util",
+ ],
+}
+
+// Host-side stub generator tool.
+java_binary_host {
+ name: "hoststubgen",
+ main_class: "com.android.hoststubgen.HostStubGenMain",
+ static_libs: [
+ "hoststubgen-lib",
"junit",
"ow2-asm",
"ow2-asm-analysis",
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 1089f82..803dc28 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -32,7 +32,6 @@
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ClassWriter
-import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.util.CheckClassAdapter
import java.io.BufferedInputStream
import java.io.FileOutputStream
@@ -52,7 +51,7 @@
val stats = HostStubGenStats()
// Load all classes.
- val allClasses = loadClassStructures(options.inJar.get)
+ val allClasses = ClassNodes.loadClassStructures(options.inJar.get)
// Dump the classes, if specified.
options.inputJarDumpFile.ifSet {
@@ -92,55 +91,6 @@
}
/**
- * Load all the classes, without code.
- */
- private fun loadClassStructures(inJar: String): ClassNodes {
- log.i("Reading class structure from $inJar ...")
- val start = System.currentTimeMillis()
-
- val allClasses = ClassNodes()
-
- log.withIndent {
- ZipFile(inJar).use { inZip ->
- val inEntries = inZip.entries()
-
- while (inEntries.hasMoreElements()) {
- val entry = inEntries.nextElement()
-
- BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
- if (entry.name.endsWith(".class")) {
- val cr = ClassReader(bis)
- val cn = ClassNode()
- cr.accept(cn, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG
- or ClassReader.SKIP_FRAMES)
- if (!allClasses.addClass(cn)) {
- log.w("Duplicate class found: ${cn.name}")
- }
- } else if (entry.name.endsWith(".dex")) {
- // Seems like it's an ART jar file. We can't process it.
- // It's a fatal error.
- throw InvalidJarFileException(
- "$inJar is not a desktop jar file. It contains a *.dex file.")
- } else {
- // Unknown file type. Skip.
- while (bis.available() > 0) {
- bis.skip((1024 * 1024).toLong())
- }
- }
- }
- }
- }
- }
- if (allClasses.size == 0) {
- log.w("$inJar contains no *.class files.")
- }
-
- val end = System.currentTimeMillis()
- log.i("Done reading class structure in %.1f second(s).", (end - start) / 1000.0)
- return allClasses
- }
-
- /**
* Build the filter, which decides what classes/methods/fields should be put in stub or impl
* jars, and "how". (e.g. with substitution?)
*/
@@ -229,7 +179,7 @@
val intersectingJars = mutableMapOf<String, ClassNodes>()
filenames.forEach { filename ->
- intersectingJars[filename] = loadClassStructures(filename)
+ intersectingJars[filename] = ClassNodes.loadClassStructures(filename)
}
return intersectingJars
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
similarity index 86%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt
rename to tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
index 4882c00..45e7e30 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
@@ -13,18 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@file:JvmName("Main")
+@file:JvmName("HostStubGenMain")
package com.android.hoststubgen
import java.io.PrintWriter
-const val COMMAND_NAME = "HostStubGen"
-
/**
* Entry point.
*/
fun main(args: Array<String>) {
+ executableName = "HostStubGen"
+
var success = false
var clanupOnError = false
@@ -33,7 +33,7 @@
val options = HostStubGenOptions.parseArgs(args)
clanupOnError = options.cleanUpOnError.get
- log.v("HostStubGen started")
+ log.v("$executableName started")
log.v("Options: $options")
// Run.
@@ -41,7 +41,7 @@
success = true
} catch (e: Throwable) {
- log.e("$COMMAND_NAME: Error: ${e.message}")
+ log.e("$executableName: Error: ${e.message}")
if (e !is UserErrorException) {
e.printStackTrace(PrintWriter(log.getWriter(LogLevel.Error)))
}
@@ -49,7 +49,7 @@
TODO("Remove output jars here")
}
} finally {
- log.i("$COMMAND_NAME finished")
+ log.i("$executableName finished")
log.flush()
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index 9f5d524..9ff798a 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -268,7 +268,7 @@
}
if (!ret.outStubJar.isSet && !ret.outImplJar.isSet) {
log.w("Neither --out-stub-jar nor --out-impl-jar is set." +
- " $COMMAND_NAME will not generate jar files.")
+ " $executableName will not generate jar files.")
}
if (ret.enableNonStubMethodCallDetection.get) {
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
index 937e56c..aa63d8d9 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
@@ -16,6 +16,11 @@
package com.android.hoststubgen
/**
+ * Name of this executable. Set it in the main method.
+ */
+var executableName = "[command name not set]"
+
+/**
* A regex that maches whitespate.
*/
val whitespaceRegex = """\s+""".toRegex()
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
index 0579c2b..83e122f 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
@@ -34,6 +34,9 @@
/** Descriptor of the class initializer method. */
val CLASS_INITIALIZER_DESC = "()V"
+/** Name of constructors. */
+val CTOR_NAME = "<init>"
+
/**
* Find any of [anyAnnotations] from the list of visible / invisible annotations.
*/
@@ -135,10 +138,10 @@
// Note, long and double will consume two local variable spaces, so the extra `i++`.
when (type) {
Type.VOID_TYPE -> throw HostStubGenInternalException("VOID_TYPE not expected")
- Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.SHORT_TYPE, Type.CHAR_TYPE
+ Type.BOOLEAN_TYPE, Type.CHAR_TYPE, Type.BYTE_TYPE, Type.SHORT_TYPE, Type.INT_TYPE
-> writer.visitVarInsn(Opcodes.ILOAD, i)
- Type.LONG_TYPE -> writer.visitVarInsn(Opcodes.LLOAD, i++)
Type.FLOAT_TYPE -> writer.visitVarInsn(Opcodes.FLOAD, i)
+ Type.LONG_TYPE -> writer.visitVarInsn(Opcodes.LLOAD, i++)
Type.DOUBLE_TYPE -> writer.visitVarInsn(Opcodes.DLOAD, i++)
else -> writer.visitVarInsn(Opcodes.ALOAD, i)
}
@@ -154,10 +157,10 @@
// See https://en.wikipedia.org/wiki/List_of_Java_bytecode_instructions
when (type) {
Type.VOID_TYPE -> writer.visitInsn(Opcodes.RETURN)
- Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.SHORT_TYPE, Type.CHAR_TYPE
+ Type.BOOLEAN_TYPE, Type.CHAR_TYPE, Type.BYTE_TYPE, Type.SHORT_TYPE, Type.INT_TYPE
-> writer.visitInsn(Opcodes.IRETURN)
- Type.LONG_TYPE -> writer.visitInsn(Opcodes.LRETURN)
Type.FLOAT_TYPE -> writer.visitInsn(Opcodes.FRETURN)
+ Type.LONG_TYPE -> writer.visitInsn(Opcodes.LRETURN)
Type.DOUBLE_TYPE -> writer.visitInsn(Opcodes.DRETURN)
else -> writer.visitInsn(Opcodes.ARETURN)
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
index bc34ef0..92906a7 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
@@ -16,13 +16,18 @@
package com.android.hoststubgen.asm
import com.android.hoststubgen.ClassParseException
+import com.android.hoststubgen.InvalidJarFileException
+import com.android.hoststubgen.log
+import org.objectweb.asm.ClassReader
import org.objectweb.asm.tree.AnnotationNode
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.FieldNode
import org.objectweb.asm.tree.MethodNode
import org.objectweb.asm.tree.TypeAnnotationNode
+import java.io.BufferedInputStream
import java.io.PrintWriter
import java.util.Arrays
+import java.util.zip.ZipFile
/**
* Stores all classes loaded from a jar file, in a form of [ClassNode]
@@ -62,8 +67,8 @@
/** Find a field, which may not exist. */
fun findField(
- className: String,
- fieldName: String,
+ className: String,
+ fieldName: String,
): FieldNode? {
return findClass(className)?.fields?.firstOrNull { it.name == fieldName }?.let { fn ->
return fn
@@ -72,14 +77,14 @@
/** Find a method, which may not exist. */
fun findMethod(
- className: String,
- methodName: String,
- descriptor: String,
+ className: String,
+ methodName: String,
+ descriptor: String,
): MethodNode? {
return findClass(className)?.methods
- ?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn ->
- return mn
- }
+ ?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn ->
+ return mn
+ }
}
/** @return true if a class has a class initializer. */
@@ -106,26 +111,33 @@
private fun dumpClass(pw: PrintWriter, cn: ClassNode) {
pw.printf("Class: %s [access: %x]\n", cn.name, cn.access)
- dumpAnnotations(pw, " ",
- cn.visibleTypeAnnotations, cn.invisibleTypeAnnotations,
- cn.visibleAnnotations, cn.invisibleAnnotations,
- )
+ dumpAnnotations(
+ pw, " ",
+ cn.visibleTypeAnnotations, cn.invisibleTypeAnnotations,
+ cn.visibleAnnotations, cn.invisibleAnnotations,
+ )
for (f in cn.fields ?: emptyList()) {
- pw.printf(" Field: %s [sig: %s] [desc: %s] [access: %x]\n",
- f.name, f.signature, f.desc, f.access)
- dumpAnnotations(pw, " ",
- f.visibleTypeAnnotations, f.invisibleTypeAnnotations,
- f.visibleAnnotations, f.invisibleAnnotations,
- )
+ pw.printf(
+ " Field: %s [sig: %s] [desc: %s] [access: %x]\n",
+ f.name, f.signature, f.desc, f.access
+ )
+ dumpAnnotations(
+ pw, " ",
+ f.visibleTypeAnnotations, f.invisibleTypeAnnotations,
+ f.visibleAnnotations, f.invisibleAnnotations,
+ )
}
for (m in cn.methods ?: emptyList()) {
- pw.printf(" Method: %s [sig: %s] [desc: %s] [access: %x]\n",
- m.name, m.signature, m.desc, m.access)
- dumpAnnotations(pw, " ",
- m.visibleTypeAnnotations, m.invisibleTypeAnnotations,
- m.visibleAnnotations, m.invisibleAnnotations,
- )
+ pw.printf(
+ " Method: %s [sig: %s] [desc: %s] [access: %x]\n",
+ m.name, m.signature, m.desc, m.access
+ )
+ dumpAnnotations(
+ pw, " ",
+ m.visibleTypeAnnotations, m.invisibleTypeAnnotations,
+ m.visibleAnnotations, m.invisibleAnnotations,
+ )
}
}
@@ -136,7 +148,7 @@
invisibleTypeAnnotations: List<TypeAnnotationNode>?,
visibleAnnotations: List<AnnotationNode>?,
invisibleAnnotations: List<AnnotationNode>?,
- ) {
+ ) {
for (an in visibleTypeAnnotations ?: emptyList()) {
pw.printf("%sTypeAnnotation(vis): %s\n", prefix, an.desc)
}
@@ -166,4 +178,55 @@
}
}
}
+
+ companion object {
+ /**
+ * Load all the classes, without code.
+ */
+ fun loadClassStructures(inJar: String): ClassNodes {
+ log.i("Reading class structure from $inJar ...")
+ val start = System.currentTimeMillis()
+
+ val allClasses = ClassNodes()
+
+ log.withIndent {
+ ZipFile(inJar).use { inZip ->
+ val inEntries = inZip.entries()
+
+ while (inEntries.hasMoreElements()) {
+ val entry = inEntries.nextElement()
+
+ BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
+ if (entry.name.endsWith(".class")) {
+ val cr = ClassReader(bis)
+ val cn = ClassNode()
+ cr.accept(cn, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG
+ or ClassReader.SKIP_FRAMES)
+ if (!allClasses.addClass(cn)) {
+ log.w("Duplicate class found: ${cn.name}")
+ }
+ } else if (entry.name.endsWith(".dex")) {
+ // Seems like it's an ART jar file. We can't process it.
+ // It's a fatal error.
+ throw InvalidJarFileException(
+ "$inJar is not a desktop jar file. It contains a *.dex file.")
+ } else {
+ // Unknown file type. Skip.
+ while (bis.available() > 0) {
+ bis.skip((1024 * 1024).toLong())
+ }
+ }
+ }
+ }
+ }
+ }
+ if (allClasses.size == 0) {
+ log.w("$inJar contains no *.class files.")
+ }
+
+ val end = System.currentTimeMillis()
+ log.i("Done reading class structure in %.1f second(s).", (end - start) / 1000.0)
+ return allClasses
+ }
+ }
}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
index 78b13fd..5a26fc6 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
@@ -19,14 +19,14 @@
import com.android.hoststubgen.HostStubGenInternalException
import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
-import com.android.hoststubgen.asm.isAnonymousInnerClass
-import com.android.hoststubgen.log
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.isAnnotation
+import com.android.hoststubgen.asm.isAnonymousInnerClass
import com.android.hoststubgen.asm.isAutoGeneratedEnumMember
import com.android.hoststubgen.asm.isEnum
import com.android.hoststubgen.asm.isSynthetic
import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate
+import com.android.hoststubgen.log
import org.objectweb.asm.tree.ClassNode
/**
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index f70a17d..fa8fe6c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -1833,7 +1833,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 10, attributes: 2
+ interfaces: 0, fields: 1, methods: 11, attributes: 2
int value;
descriptor: I
flags: (0x0000)
@@ -1938,6 +1938,10 @@
x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
x: athrow
LineNumberTable:
+
+ public static native byte nativeBytePlus(byte, byte);
+ descriptor: (BB)B
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
}
SourceFile: "TinyFrameworkNative.java"
RuntimeInvisibleAnnotations:
@@ -1955,7 +1959,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 4, attributes: 2
+ interfaces: 0, fields: 0, methods: 5, attributes: 2
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -2013,6 +2017,22 @@
Start Length Slot Name Signature
0 7 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
0 7 1 arg I
+
+ public static byte nativeBytePlus(byte, byte);
+ descriptor: (BB)B
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: iload_0
+ x: iload_1
+ x: iadd
+ x: i2b
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 arg1 B
+ 0 5 1 arg2 B
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeInvisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index 37de857..c605f76 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -1554,7 +1554,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 9, attributes: 3
+ interfaces: 0, fields: 1, methods: 10, attributes: 3
int value;
descriptor: I
flags: (0x0000)
@@ -1686,6 +1686,15 @@
com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public static native byte nativeBytePlus(byte, byte);
+ descriptor: (BB)B
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index c9c607c..11d5939 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -2236,7 +2236,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 10, attributes: 3
+ interfaces: 0, fields: 1, methods: 11, attributes: 3
int value;
descriptor: I
flags: (0x0000)
@@ -2435,6 +2435,23 @@
com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public static byte nativeBytePlus(byte, byte);
+ descriptor: (BB)B
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: iload_0
+ x: iload_1
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeBytePlus:(BB)B
+ x: ireturn
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
@@ -2457,7 +2474,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 4, attributes: 3
+ interfaces: 0, fields: 0, methods: 5, attributes: 3
public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
@@ -2551,6 +2568,31 @@
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public static byte nativeBytePlus(byte, byte);
+ descriptor: (BB)B
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeBytePlus
+ x: ldc #x // String (BB)B
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_0
+ x: iload_1
+ x: iadd
+ x: i2b
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 5 0 arg1 B
+ 15 5 1 arg2 B
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeVisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index 37de857..c605f76 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -1554,7 +1554,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 9, attributes: 3
+ interfaces: 0, fields: 1, methods: 10, attributes: 3
int value;
descriptor: I
flags: (0x0000)
@@ -1686,6 +1686,15 @@
com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public static native byte nativeBytePlus(byte, byte);
+ descriptor: (BB)B
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
index a57907d..088bc80 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -2743,7 +2743,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 11, attributes: 3
+ interfaces: 0, fields: 1, methods: 12, attributes: 3
int value;
descriptor: I
flags: (0x0000)
@@ -3002,6 +3002,28 @@
com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public static byte nativeBytePlus(byte, byte);
+ descriptor: (BB)B
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ x: ldc #x // String nativeBytePlus
+ x: ldc #x // String (BB)B
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_0
+ x: iload_1
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeBytePlus:(BB)B
+ x: ireturn
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
@@ -3024,7 +3046,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 3
+ interfaces: 0, fields: 0, methods: 6, attributes: 3
private static {};
descriptor: ()V
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
@@ -3148,6 +3170,36 @@
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public static byte nativeBytePlus(byte, byte);
+ descriptor: (BB)B
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeBytePlus
+ x: ldc #x // String (BB)B
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeBytePlus
+ x: ldc #x // String (BB)B
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_0
+ x: iload_1
+ x: iadd
+ x: i2b
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 arg1 B
+ 26 5 1 arg2 B
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeVisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
index 5a5e22d..09ee183 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
@@ -52,4 +52,6 @@
public static void nativeStillNotSupported_should_be_like_this() {
throw new RuntimeException();
}
+
+ public static native byte nativeBytePlus(byte arg1, byte arg2);
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
index 749ebaa..b23c216 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
@@ -34,4 +34,8 @@
public static int nativeNonStaticAddToValue(TinyFrameworkNative source, int arg) {
return source.value + arg;
}
+
+ public static byte nativeBytePlus(byte arg1, byte arg2) {
+ return (byte) (arg1 + arg2);
+ }
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index ba17c75..762180d 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -154,13 +154,22 @@
}
@Test
+ public void testNativeSubstitutionLong() {
+ assertThat(TinyFrameworkNative.nativeLongPlus(1L, 2L)).isEqualTo(3L);
+ }
+
+ @Test
+ public void testNativeSubstitutionByte() {
+ assertThat(TinyFrameworkNative.nativeBytePlus((byte) 3, (byte) 4)).isEqualTo(7);
+ }
+
+ @Test
public void testNativeSubstitutionClass_nonStatic() {
TinyFrameworkNative instance = new TinyFrameworkNative();
instance.setValue(5);
assertThat(instance.nativeNonStaticAddToValue(3)).isEqualTo(8);
}
-
@Test
public void testSubstituteNativeWithThrow() throws Exception {
// We can't use TinyFrameworkNative.nativeStillNotSupported() directly in this class,