diff options
417 files changed, 10041 insertions, 6254 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index f779b4d96b45..b0b3b1f3cb5e 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -319,6 +319,8 @@ public class DeviceIdleController extends SystemService private SensorManager mSensorManager; private final boolean mUseMotionSensor; private Sensor mMotionSensor; + private final boolean mIsLocationPrefetchEnabled; + @Nullable private LocationRequest mLocationRequest; private Intent mIdleIntent; private Bundle mIdleIntentOptions; @@ -2460,6 +2462,11 @@ public class DeviceIdleController extends SystemService return null; } + boolean isLocationPrefetchEnabled() { + return mContext.getResources().getBoolean( + com.android.internal.R.bool.config_autoPowerModePrefetchLocation); + } + boolean useMotionSensor() { return mContext.getResources().getBoolean( com.android.internal.R.bool.config_autoPowerModeUseMotionSensor); @@ -2489,6 +2496,7 @@ public class DeviceIdleController extends SystemService mAppStateTracker = mInjector.getAppStateTracker(context, AppSchedulingModuleThread.get().getLooper()); LocalServices.addService(AppStateTracker.class, mAppStateTracker); + mIsLocationPrefetchEnabled = mInjector.isLocationPrefetchEnabled(); mUseMotionSensor = mInjector.useMotionSensor(); } @@ -2602,8 +2610,7 @@ public class DeviceIdleController extends SystemService mMotionSensor = mInjector.getMotionSensor(); } - if (getContext().getResources().getBoolean( - com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) { + if (mIsLocationPrefetchEnabled) { mLocationRequest = new LocationRequest.Builder(/*intervalMillis=*/ 0) .setQuality(LocationRequest.QUALITY_HIGH_ACCURACY) .setMaxUpdates(1) @@ -3779,34 +3786,40 @@ public class DeviceIdleController extends SystemService case STATE_SENSING: cancelSensingTimeoutAlarmLocked(); moveToStateLocked(STATE_LOCATING, reason); - scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT); - LocationManager locationManager = mInjector.getLocationManager(); - if (locationManager != null - && locationManager.getProvider(LocationManager.FUSED_PROVIDER) != null) { - locationManager.requestLocationUpdates(LocationManager.FUSED_PROVIDER, - mLocationRequest, - AppSchedulingModuleThread.getExecutor(), - mGenericLocationListener); - mLocating = true; - } else { - mHasFusedLocation = false; - } - if (locationManager != null - && locationManager.getProvider(LocationManager.GPS_PROVIDER) != null) { - mHasGps = true; - locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5, - mGpsLocationListener, mHandler.getLooper()); - mLocating = true; + if (mIsLocationPrefetchEnabled) { + scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT); + LocationManager locationManager = mInjector.getLocationManager(); + if (locationManager != null + && locationManager.getProvider(LocationManager.FUSED_PROVIDER) + != null) { + locationManager.requestLocationUpdates(LocationManager.FUSED_PROVIDER, + mLocationRequest, + AppSchedulingModuleThread.getExecutor(), + mGenericLocationListener); + mLocating = true; + } else { + mHasFusedLocation = false; + } + if (locationManager != null + && locationManager.getProvider(LocationManager.GPS_PROVIDER) != null) { + mHasGps = true; + locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, + 1000, 5, mGpsLocationListener, mHandler.getLooper()); + mLocating = true; + } else { + mHasGps = false; + } + // If we have a location provider, we're all set, the listeners will move state + // forward. + if (mLocating) { + break; + } + // Otherwise, we have to move from locating into idle maintenance. } else { - mHasGps = false; - } - // If we have a location provider, we're all set, the listeners will move state - // forward. - if (mLocating) { - break; + mLocating = false; } - // Otherwise, we have to move from locating into idle maintenance. + // We're not doing any locating work, so move on to the next state. case STATE_LOCATING: cancelAlarmLocked(); cancelLocatingLocked(); @@ -5303,15 +5316,19 @@ public class DeviceIdleController extends SystemService pw.print(" "); pw.print(mStationaryListeners.size()); pw.println(" stationary listeners registered"); } - pw.print(" mLocating="); pw.print(mLocating); - pw.print(" mHasGps="); pw.print(mHasGps); - pw.print(" mHasFused="); pw.print(mHasFusedLocation); - pw.print(" mLocated="); pw.println(mLocated); - if (mLastGenericLocation != null) { - pw.print(" mLastGenericLocation="); pw.println(mLastGenericLocation); - } - if (mLastGpsLocation != null) { - pw.print(" mLastGpsLocation="); pw.println(mLastGpsLocation); + if (mIsLocationPrefetchEnabled) { + pw.print(" mLocating="); pw.print(mLocating); + pw.print(" mHasGps="); pw.print(mHasGps); + pw.print(" mHasFused="); pw.print(mHasFusedLocation); + pw.print(" mLocated="); pw.println(mLocated); + if (mLastGenericLocation != null) { + pw.print(" mLastGenericLocation="); pw.println(mLastGenericLocation); + } + if (mLastGpsLocation != null) { + pw.print(" mLastGpsLocation="); pw.println(mLastGpsLocation); + } + } else { + pw.println(" Location prefetching disabled"); } pw.print(" mState="); pw.print(stateToString(mState)); pw.print(" mLightState="); diff --git a/config/dirty-image-objects b/config/dirty-image-objects index dfd091cc1aff..2584610e2848 100644 --- a/config/dirty-image-objects +++ b/config/dirty-image-objects @@ -28,359 +28,270 @@ # Then, grep for lines containing "Private dirty object" from the output. # This particular file was generated by dumping systemserver and systemui. # -Landroid/accounts/Account; -Landroid/accounts/OnAccountsUpdateListener; Landroid/animation/LayoutTransition; Landroid/app/ActivityManager; -Landroid/app/ActivityManager$OnUidImportanceListener; Landroid/app/ActivityTaskManager; Landroid/app/ActivityThread; -Landroid/app/admin/DevicePolicyManager; Landroid/app/AlarmManager; -Landroid/app/Application; Landroid/app/AppOpsManager; -Landroid/app/backup/BackupManager; Landroid/app/ContextImpl; -Landroid/app/INotificationManager; -Landroid/app/Notification$BigPictureStyle; -Landroid/app/Notification$BigTextStyle; -Landroid/app/Notification$InboxStyle; -Landroid/app/NotificationChannel; -Landroid/app/NotificationChannelGroup; +Landroid/app/Notification; Landroid/app/NotificationManager; -Landroid/app/PendingIntent; -Landroid/app/PendingIntent$OnFinished; +Landroid/app/PendingIntent$FinishedDispatcher; +Landroid/app/PropertyInvalidatedCache$NoPreloadHolder; Landroid/app/QueuedWork; Landroid/app/ResourcesManager; +Landroid/app/SystemServiceRegistry; Landroid/app/WallpaperManager; -Landroid/app/WindowConfiguration; -Landroid/bluetooth/BluetoothAdapter; -Landroid/bluetooth/BluetoothDevice; -Landroid/bluetooth/BluetoothProfile; -Landroid/bluetooth/IBluetoothA2dp; -Landroid/bluetooth/IBluetoothHeadsetPhone; -Landroid/bluetooth/IBluetoothHidDevice; -Landroid/bluetooth/IBluetoothHidHost; -Landroid/bluetooth/IBluetoothMap; -Landroid/bluetooth/IBluetoothPan; -Landroid/bluetooth/IBluetoothPbap; -Landroid/bluetooth/IBluetoothSap; -Landroid/content/ClipboardManager$OnPrimaryClipChangedListener; -Landroid/content/ComponentName; -Landroid/content/ContentProvider$PipeDataWriter; +Landroid/app/backup/BackupManager; +Landroid/compat/Compatibility; +Landroid/content/AsyncQueryHandler; +Landroid/content/ContentProviderClient; Landroid/content/ContentResolver; Landroid/content/Context; -Landroid/content/Intent; -Landroid/content/pm/PackageManager$OnPermissionsChangedListener; -Landroid/content/pm/VersionedPackage; -Landroid/content/res/Configuration; -Landroid/content/SharedPreferences$OnSharedPreferenceChangeListener; +Landroid/content/pm/PackageItemInfo; +Landroid/content/pm/UserPackage; +Landroid/content/res/ResourceTimer; Landroid/database/CursorWindow; Landroid/database/sqlite/SQLiteCompatibilityWalFlags; -Landroid/database/sqlite/SQLiteDatabase$CursorFactory; +Landroid/database/sqlite/SQLiteDebug$NoPreloadHolder; Landroid/database/sqlite/SQLiteGlobal; -Landroid/database/sqlite/SQLiteTransactionListener; Landroid/ddm/DdmHandleAppName; Landroid/graphics/Bitmap; Landroid/graphics/Canvas; -Landroid/graphics/drawable/AdaptiveIconDrawable; -Landroid/graphics/drawable/ColorDrawable; -Landroid/graphics/drawable/GradientDrawable; -Landroid/graphics/drawable/Icon; -Landroid/graphics/drawable/InsetDrawable; -Landroid/graphics/drawable/RippleDrawable; -Landroid/graphics/drawable/VectorDrawable$VGroup; -Landroid/graphics/ImageDecoder; -Landroid/graphics/Rect; +Landroid/graphics/Compatibility; +Landroid/graphics/HardwareRenderer; Landroid/graphics/TemporaryBuffer; -Landroid/hardware/biometrics/BiometricSourceType; -Landroid/hardware/display/ColorDisplayManager$ColorDisplayManagerInternal; -Landroid/hardware/display/DisplayManagerGlobal; -Landroid/hardware/display/NightDisplayListener$Callback; -Landroid/hardware/input/InputManager; -Landroid/hardware/input/InputManager$InputDeviceListener; +Landroid/graphics/Typeface; +Landroid/graphics/drawable/AdaptiveIconDrawable; Landroid/hardware/SensorPrivacyManager; Landroid/hardware/SystemSensorManager; -Landroid/icu/impl/OlsonTimeZone; -Landroid/icu/text/BreakIterator; +Landroid/hardware/devicestate/DeviceStateManagerGlobal; +Landroid/hardware/display/ColorDisplayManager$ColorDisplayManagerInternal; +Landroid/hardware/display/DisplayManagerGlobal; +Landroid/hardware/input/InputManagerGlobal; +Landroid/hardware/location/GeofenceHardwareImpl; +Landroid/icu/impl/number/range/StandardPluralRanges; Landroid/icu/text/Collator; -Landroid/icu/text/DateFormat$BooleanAttribute; -Landroid/icu/text/DateTimePatternGenerator$DTPGflags; -Landroid/icu/text/PluralRules$Operand; Landroid/icu/util/TimeZone; -Landroid/location/GpsStatus$Listener; -Landroid/location/LocationListener; +Landroid/location/LocationManager; Landroid/media/AudioManager; +Landroid/media/AudioPlaybackConfiguration; +Landroid/media/AudioSystem; +Landroid/media/MediaCodec; +Landroid/media/MediaCodecList; +Landroid/media/MediaFrameworkPlatformInitializer; +Landroid/media/MediaRouter2Manager; Landroid/media/MediaRouter; Landroid/media/PlayerBase; -Landroid/media/session/MediaSessionManager; -Landroid/net/apf/ApfCapabilities; -Landroid/net/ConnectivityManager; -Landroid/net/ConnectivityManager$OnNetworkActiveListener; -Landroid/net/ConnectivityThread$Singleton; -Landroid/net/IpConfiguration$IpAssignment; -Landroid/net/IpConfiguration$ProxySettings; -Landroid/net/IpPrefix; -Landroid/net/LinkAddress; -Landroid/net/LinkProperties; -Landroid/net/Network; -Landroid/net/NetworkCapabilities; -Landroid/net/NetworkInfo; -Landroid/net/NetworkInfo$State; -Landroid/net/NetworkRequest; -Landroid/net/NetworkRequest$Type; -Landroid/net/RouteInfo; -Landroid/net/StringNetworkSpecifier; -Landroid/net/TrafficStats; -Landroid/net/UidRange; -Landroid/net/Uri$HierarchicalUri; -Landroid/net/Uri$StringUri; -Landroid/net/wifi/WifiManager; -Landroid/net/wifi/WifiManager$SoftApCallback; -Landroid/os/AsyncResult; +Landroid/media/audiopolicy/AudioProductStrategy; +Landroid/media/audiopolicy/AudioVolumeGroup; +Landroid/nfc/NfcAdapter; +Landroid/nfc/NfcFrameworkInitializer; +Landroid/nfc/cardemulation/CardEmulation; Landroid/os/AsyncTask; +Landroid/os/BaseBundle; +Landroid/os/Binder; Landroid/os/BinderProxy; -Landroid/os/Bundle; -Landroid/os/DeadObjectException; Landroid/os/Environment; Landroid/os/FileObserver; Landroid/os/Handler; -Landroid/os/IDeviceIdleController; Landroid/os/LocaleList; Landroid/os/Looper; Landroid/os/Message; -Landroid/os/ParcelUuid; +Landroid/os/NullVibrator; +Landroid/os/Parcel; Landroid/os/Process; -Landroid/os/RecoverySystem; Landroid/os/ServiceManager; -Landroid/os/storage/StorageManager; Landroid/os/StrictMode; -Landroid/os/Trace; +Landroid/os/UEventObserver; +Landroid/os/UserManager; Landroid/os/WorkSource; -Landroid/os/WorkSource$WorkChain; +Landroid/os/storage/StorageManager; Landroid/permission/PermissionManager; +Landroid/provider/DeviceConfigInitializer; Landroid/provider/FontsContract; -Landroid/provider/Settings$SettingNotFoundException; +Landroid/provider/Settings; +Landroid/renderscript/RenderScript; Landroid/renderscript/RenderScriptCacheDir; -Landroid/security/IKeyChainService; -Landroid/security/keystore/AndroidKeyStoreProvider; +Landroid/security/keystore2/KeyStoreCryptoOperationUtils; Landroid/security/net/config/ApplicationConfig; Landroid/security/net/config/SystemCertificateSource$NoPreloadHolder; -Landroid/telecom/PhoneAccountHandle; +Landroid/security/net/config/UserCertificateSource$NoPreloadHolder; +Landroid/telecom/Log; +Landroid/telecom/TelecomManager; Landroid/telephony/AnomalyReporter; -Landroid/telephony/CellSignalStrengthCdma; -Landroid/telephony/CellSignalStrengthGsm; -Landroid/telephony/CellSignalStrengthLte; -Landroid/telephony/CellSignalStrengthNr; -Landroid/telephony/CellSignalStrengthTdscdma; -Landroid/telephony/CellSignalStrengthWcdma; -Landroid/telephony/DataSpecificRegistrationInfo; -Landroid/telephony/emergency/EmergencyNumber; -Landroid/telephony/ims/ImsMmTelManager$CapabilityCallback$CapabilityBinder; -Landroid/telephony/ims/ImsMmTelManager$RegistrationCallback$RegistrationBinder; -Landroid/telephony/ims/ImsReasonInfo; -Landroid/telephony/ims/ProvisioningManager$Callback$CallbackBinder; -Landroid/telephony/ModemActivityInfo; -Landroid/telephony/ModemInfo; -Landroid/telephony/NetworkRegistrationInfo; -Landroid/telephony/NetworkService; +Landroid/telephony/TelephonyFrameworkInitializer; +Landroid/telephony/TelephonyLocalConnection; Landroid/telephony/TelephonyManager; -Landroid/telephony/VoiceSpecificRegistrationInfo; +Landroid/telephony/TelephonyRegistryManager; +Landroid/text/DynamicLayout; +Landroid/text/TextUtils; Landroid/text/format/DateFormat; +Landroid/text/format/DateUtils; +Landroid/text/method/ArrowKeyMovementMethod; +Landroid/text/method/LinkMovementMethod; Landroid/text/method/SingleLineTransformationMethod; -Landroid/text/Selection$MemoryTextWatcher; -Landroid/text/SpanWatcher; -Landroid/text/style/AlignmentSpan; -Landroid/text/style/CharacterStyle; -Landroid/text/style/LeadingMarginSpan; -Landroid/text/style/LineBackgroundSpan; -Landroid/text/style/LineHeightSpan; -Landroid/text/style/MetricAffectingSpan; -Landroid/text/style/ReplacementSpan; -Landroid/text/style/SuggestionSpan; -Landroid/text/style/TabStopSpan; -Landroid/text/TextUtils; -Landroid/text/TextWatcher; -Landroid/transition/ChangeClipBounds; -Landroid/transition/ChangeImageTransform; -Landroid/transition/ChangeTransform; +Landroid/text/style/ClickableSpan; +Landroid/timezone/TelephonyLookup; +Landroid/timezone/TimeZoneFinder; Landroid/util/ArrayMap; Landroid/util/ArraySet; -Landroid/util/DisplayMetrics; Landroid/util/EventLog; -Landroid/util/Log; -Landroid/util/Patterns; -Landroid/view/AbsSavedState$1; -Landroid/view/accessibility/AccessibilityManager; -Landroid/view/accessibility/AccessibilityManager$AccessibilityServicesStateChangeListener; -Landroid/view/accessibility/AccessibilityManager$TouchExplorationStateChangeListener; -Landroid/view/accessibility/AccessibilityNodeIdManager; -Landroid/view/autofill/AutofillManager; -Landroid/view/autofill/Helper; +Landroid/util/NtpTrustedTime; Landroid/view/Choreographer; -Landroid/view/inputmethod/InputMethodManager; -Landroid/view/IWindowManager; +Landroid/view/CrossWindowBlurListeners; +Landroid/view/DisplayCutout; +Landroid/view/KeyEvent; +Landroid/view/MotionEvent; Landroid/view/PointerIcon; -Landroid/view/RemoteAnimationAdapter; -Landroid/view/ThreadedRenderer; +Landroid/view/RoundedCorners; +Landroid/view/SurfaceControl; Landroid/view/View; -Landroid/view/View$OnHoverListener; +Landroid/view/ViewGroup$TouchTarget; Landroid/view/ViewRootImpl; -Landroid/view/ViewStub; -Landroid/view/ViewStub$OnInflateListener; Landroid/view/ViewTreeObserver; -Landroid/view/WindowManager$LayoutParams; Landroid/view/WindowManagerGlobal; -Landroid/widget/ActionMenuPresenter$OverflowMenuButton; -Landroid/widget/ActionMenuView; -Landroid/widget/Button; -Landroid/widget/CheckBox; -Landroid/widget/FrameLayout; -Landroid/widget/ImageButton; +Landroid/view/accessibility/AccessibilityManager; +Landroid/view/accessibility/AccessibilityNodeIdManager; +Landroid/view/autofill/Helper; +Landroid/view/inputmethod/IInputMethodManagerGlobalInvoker; +Landroid/view/inputmethod/InputMethodManager; +Landroid/webkit/CookieSyncManager; +Landroid/webkit/WebView; +Landroid/webkit/WebViewFactory; +Landroid/webkit/WebViewZygote; +Landroid/widget/AbsListView; Landroid/widget/ImageView; Landroid/widget/LinearLayout; -Landroid/widget/RelativeLayout; -Landroid/widget/SeekBar; -Landroid/widget/Space; -Landroid/widget/TextView; -Landroid/widget/Toolbar; -[B -Lcom/android/ims/ImsManager; +Landroid/widget/Toast; +Landroid/window/SurfaceSyncGroup; +Lcom/android/i18n/timezone/TelephonyLookup; +Lcom/android/i18n/timezone/TimeZoneFinder; +Lcom/android/internal/config/appcloning/AppCloningDeviceConfigHelper; +Lcom/android/internal/content/om/OverlayConfig; +Lcom/android/internal/display/BrightnessSynchronizer; +Lcom/android/internal/infra/AndroidFuture; +Lcom/android/internal/inputmethod/ImeTracing; +Lcom/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry; +Lcom/android/internal/jank/InteractionJankMonitor$InstanceHolder; +Lcom/android/internal/jank/InteractionJankMonitor; Lcom/android/internal/logging/MetricsLogger; Lcom/android/internal/os/BackgroundThread; Lcom/android/internal/os/BinderInternal; -Lcom/android/internal/os/BinderInternal$BinderProxyLimitListener; +Lcom/android/internal/os/KernelCpuBpfTracking; Lcom/android/internal/os/RuntimeInit; Lcom/android/internal/os/SomeArgs; -Lcom/android/internal/policy/DecorView; -Lcom/android/internal/statusbar/IStatusBarService; -Lcom/android/internal/telephony/AppSmsManager; -Landroid/telephony/CallerInfoAsyncQuery$OnQueryCompleteListener; -Lcom/android/internal/telephony/CarrierActionAgent; -Lcom/android/internal/telephony/cat/CatService; -Lcom/android/internal/telephony/cat/IconLoader; -Lcom/android/internal/telephony/cat/RilMessageDecoder; -Lcom/android/internal/telephony/cdma/CdmaSubscriptionSourceManager; -Lcom/android/internal/telephony/cdma/EriManager; -Lcom/android/internal/telephony/CellularNetworkValidator; -Lcom/android/internal/telephony/CommandException; -Lcom/android/internal/telephony/dataconnection/DataConnection$DcActivatingState; -Lcom/android/internal/telephony/dataconnection/DataConnection$DcActiveState; -Lcom/android/internal/telephony/dataconnection/DataConnection$DcInactiveState; -Lcom/android/internal/telephony/dataconnection/DataEnabledSettings; -Lcom/android/internal/telephony/dataconnection/DcTracker; -Lcom/android/internal/telephony/euicc/EuiccCardController; -Lcom/android/internal/telephony/euicc/EuiccController; -Lcom/android/internal/telephony/GsmAlphabet; -Lcom/android/internal/telephony/GsmCdmaCallTracker; -Lcom/android/internal/telephony/GsmCdmaPhone; -Lcom/android/internal/telephony/IccPhoneBookInterfaceManager; -Lcom/android/internal/telephony/IccSmsInterfaceManager; -Lcom/android/internal/telephony/ims/ImsResolver; -Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker; -Lcom/android/internal/telephony/imsphone/ImsPhone; -Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker; -Lcom/android/internal/telephony/ims/RcsMessageStoreController; +Lcom/android/internal/os/ZygoteInit; +Lcom/android/internal/policy/AttributeCache; +Lcom/android/internal/protolog/BaseProtoLogImpl; +Lcom/android/internal/protolog/ProtoLogImpl; +Lcom/android/internal/statusbar/NotificationVisibility; +Lcom/android/internal/telephony/CellBroadcastServiceManager; Lcom/android/internal/telephony/IntentBroadcaster; -Lcom/android/internal/telephony/ITelephonyRegistry$Stub$Proxy; -Lcom/android/internal/telephony/metrics/TelephonyMetrics; +Lcom/android/internal/telephony/MccTable; Lcom/android/internal/telephony/MultiSimSettingController; -Lcom/android/internal/telephony/nano/CarrierIdProto$CarrierAttribute; -Lcom/android/internal/telephony/nano/CarrierIdProto$CarrierId; -Lcom/android/internal/telephony/nano/TelephonyProto$RilDataCall; -Lcom/android/internal/telephony/nano/TelephonyProto$SmsSession$Event; -Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyCallSession$Event$RilCall; -Lcom/android/internal/telephony/NitzStateMachine; +Lcom/android/internal/telephony/PackageChangeReceiver; Lcom/android/internal/telephony/PhoneConfigurationManager; Lcom/android/internal/telephony/PhoneFactory; -Lcom/android/internal/telephony/PhoneSwitcher; Lcom/android/internal/telephony/ProxyController; -Lcom/android/internal/telephony/RadioConfig; -Lcom/android/internal/telephony/RIL; Lcom/android/internal/telephony/RILRequest; -Lcom/android/internal/telephony/RilWakelockInfo; -Lcom/android/internal/telephony/ServiceStateTracker; -Lcom/android/internal/telephony/SimActivationTracker; +Lcom/android/internal/telephony/RadioConfig; +Lcom/android/internal/telephony/RadioInterfaceCapabilityController; Lcom/android/internal/telephony/SmsApplication; Lcom/android/internal/telephony/SmsBroadcastUndelivered; -Lcom/android/internal/telephony/SmsStorageMonitor; -Lcom/android/internal/telephony/SmsUsageMonitor; -Lcom/android/internal/telephony/SubscriptionController; -Lcom/android/internal/telephony/SubscriptionInfoUpdater; +Lcom/android/internal/telephony/SomeArgs; Lcom/android/internal/telephony/TelephonyComponentFactory; Lcom/android/internal/telephony/TelephonyDevController; -Lcom/android/internal/telephony/TelephonyTester; -Lcom/android/internal/telephony/uicc/AdnRecordCache; -Lcom/android/internal/telephony/uicc/UiccCardApplication; +Lcom/android/internal/telephony/cat/CatService; +Lcom/android/internal/telephony/cdma/CdmaInboundSmsHandler; +Lcom/android/internal/telephony/cdma/CdmaSubscriptionSourceManager; +Lcom/android/internal/telephony/euicc/EuiccCardController; +Lcom/android/internal/telephony/euicc/EuiccController; +Lcom/android/internal/telephony/ims/ImsResolver; +Lcom/android/internal/telephony/metrics/TelephonyMetrics; +Lcom/android/internal/telephony/nano/PersistAtomsProto$CarrierIdMismatch; +Lcom/android/internal/telephony/nano/PersistAtomsProto$CellularDataServiceSwitch; +Lcom/android/internal/telephony/nano/PersistAtomsProto$CellularServiceState; +Lcom/android/internal/telephony/nano/PersistAtomsProto$DataCallSession; +Lcom/android/internal/telephony/nano/PersistAtomsProto$EmergencyNumbersInfo; +Lcom/android/internal/telephony/nano/PersistAtomsProto$GbaEvent; +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsDedicatedBearerEvent; +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsDedicatedBearerListenerEvent; +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationFeatureTagStats; +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationServiceDescStats; +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationStats; +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationTermination; +Lcom/android/internal/telephony/nano/PersistAtomsProto$IncomingSms; +Lcom/android/internal/telephony/nano/PersistAtomsProto$NetworkRequests; +Lcom/android/internal/telephony/nano/PersistAtomsProto$NetworkRequestsV2; +Lcom/android/internal/telephony/nano/PersistAtomsProto$OutgoingShortCodeSms; +Lcom/android/internal/telephony/nano/PersistAtomsProto$OutgoingSms; +Lcom/android/internal/telephony/nano/PersistAtomsProto$PresenceNotifyEvent; +Lcom/android/internal/telephony/nano/PersistAtomsProto$RcsAcsProvisioningStats; +Lcom/android/internal/telephony/nano/PersistAtomsProto$RcsClientProvisioningStats; +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteController; +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteIncomingDatagram; +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteOutgoingDatagram; +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteProvision; +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteSession; +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteSosMessageRecommender; +Lcom/android/internal/telephony/nano/PersistAtomsProto$SipDelegateStats; +Lcom/android/internal/telephony/nano/PersistAtomsProto$SipMessageResponse; +Lcom/android/internal/telephony/nano/PersistAtomsProto$SipTransportFeatureTagStats; +Lcom/android/internal/telephony/nano/PersistAtomsProto$SipTransportSession; +Lcom/android/internal/telephony/nano/PersistAtomsProto$UceEventStats; +Lcom/android/internal/telephony/nano/PersistAtomsProto$UnmeteredNetworks; +Lcom/android/internal/telephony/nano/PersistAtomsProto$VoiceCallRatUsage; +Lcom/android/internal/telephony/nano/PersistAtomsProto$VoiceCallSession; +Lcom/android/internal/telephony/nano/TelephonyProto$RilDataCall; +Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyCallSession$Event$RilCall; +Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyServiceState$NetworkRegistrationInfo; +Lcom/android/internal/telephony/satellite/PointingAppController; +Lcom/android/internal/telephony/satellite/SatelliteModemInterface; Lcom/android/internal/telephony/uicc/UiccController; -Lcom/android/internal/telephony/uicc/UiccProfile; Lcom/android/internal/telephony/uicc/UiccStateChangedLauncher; -Lcom/android/internal/telephony/uicc/UsimFileHandler; -Lcom/android/internal/telephony/uicc/VoiceMailConstants; -Lcom/android/internal/util/LatencyTracker; -Lcom/android/internal/util/StateMachine$SmHandler; -Lcom/android/okhttp/OkHttpClient; -Lcom/android/okhttp/okio/AsyncTimeout; -Lcom/android/okhttp/okio/SegmentPool; +Lcom/android/internal/util/ContrastColorUtil; +Lcom/android/internal/view/WindowManagerPolicyThread; +Lcom/android/org/bouncycastle/crypto/CryptoServicesRegistrar; Lcom/android/phone/ecc/nano/ProtobufEccData$CountryInfo; Lcom/android/phone/ecc/nano/ProtobufEccData$EccInfo; -Lcom/android/server/sip/SipWakeupTimer; -Lcom/android/server/SystemConfig; +Lcom/android/server/AppWidgetBackupBridge; Ldalvik/system/BaseDexClassLoader; Ldalvik/system/BlockGuard; Ldalvik/system/CloseGuard; Ldalvik/system/RuntimeHooks; Ldalvik/system/SocketTagger; -Ljava/io/BufferedReader; -Ljava/lang/AssertionError; -Ljava/lang/Boolean; -Ljava/lang/Byte; -Ljava/lang/Character; -Ljava/lang/CharSequence; -Ljava/lang/Class; -Ljava/lang/IllegalAccessException; -Ljava/lang/IllegalStateException; -Ljava/lang/NoSuchMethodException; -Ljava/lang/NullPointerException; -Ljava/lang/Object; -[Ljava/lang/Object; -Ljava/lang/ref/FinalizerReference; -Ljava/lang/Runnable; -Ljava/lang/SecurityException; -Ljava/lang/Short; -[Ljava/lang/String; +Ldalvik/system/VMRuntime; +Ldalvik/system/ZipPathValidator; +Ldalvik/system/ZygoteHooks; Ljava/lang/System; Ljava/lang/Thread; Ljava/lang/Throwable; -Ljava/lang/UnsatisfiedLinkError; -Ljava/net/Inet6Address; -Ljava/net/Socket; -Ljava/net/SocketException; +Ljava/lang/ref/FinalizerReference; +Ljava/lang/ref/ReferenceQueue; +Ljava/net/ResponseCache; Ljava/nio/Bits; Ljava/nio/charset/Charset; -Ljava/security/interfaces/RSAPrivateKey; Ljava/security/Provider; Ljava/util/Collections; -Ljava/util/concurrent/Executor; Ljava/util/GregorianCalendar; -Ljava/util/Locale; Ljava/util/Locale$NoImagePreloadHolder; +Ljava/util/Locale; Ljava/util/Scanner; -Ljava/util/Set; Ljava/util/TimeZone; +Ljava/util/concurrent/ForkJoinPool; +Ljava/util/concurrent/ThreadLocalRandom; +Ljavax/net/ServerSocketFactory; Ljavax/net/SocketFactory; -Ljavax/net/ssl/HttpsURLConnection; Ljavax/net/ssl/HttpsURLConnection$NoPreloadHolder; +Ljavax/net/ssl/HttpsURLConnection; Ljavax/net/ssl/SSLContext; -Ljavax/net/ssl/SSLSessionContext; +Ljavax/net/ssl/SSLServerSocketFactory; Ljavax/net/ssl/SSLSocketFactory; Llibcore/io/Libcore; -Llibcore/io/Memory; Llibcore/net/NetworkSecurityPolicy; -Llibcore/timezone/TimeZoneFinder; -Lorg/apache/http/params/HttpParams; Lsun/misc/Cleaner; -Lsun/nio/ch/FileChannelImpl; Lsun/nio/ch/FileChannelImpl$Unmapper; -Lsun/nio/fs/UnixChannelFactory; +Lsun/nio/ch/FileChannelImpl; Lsun/security/jca/Providers; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 332c53cb224f..d97f71847592 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3632,18 +3632,12 @@ package android.view.autofill { } public final class AutofillManager { - method public void clearAutofillRequestCallback(); - method @RequiresPermission(android.Manifest.permission.PROVIDE_OWN_AUTOFILL_SUGGESTIONS) public void setAutofillRequestCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.autofill.AutofillRequestCallback); field public static final String ANY_HINT = "any"; field public static final int FLAG_SMART_SUGGESTION_OFF = 0; // 0x0 field public static final int FLAG_SMART_SUGGESTION_SYSTEM = 1; // 0x1 field public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 120000; // 0x1d4c0 } - public interface AutofillRequestCallback { - method public void onFillRequest(@Nullable android.view.inputmethod.InlineSuggestionsRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.FillCallback); - } - } package android.view.contentcapture { @@ -3767,11 +3761,6 @@ package android.view.inputmethod { method @NonNull public static android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(@NonNull android.widget.inline.InlinePresentationSpec, @NonNull String, @Nullable String[], @NonNull String, boolean); } - public static final class InlineSuggestionsRequest.Builder { - method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setClientSupported(boolean); - method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setServiceSupported(boolean); - } - public final class InlineSuggestionsResponse implements android.os.Parcelable { method @NonNull public static android.view.inputmethod.InlineSuggestionsResponse newInlineSuggestionsResponse(@NonNull java.util.List<android.view.inputmethod.InlineSuggestion>); } diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 61d6787b4f85..2ae721648656 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -403,8 +403,11 @@ public class ActivityOptions extends ComponentOptions { private static final String KEY_SPLASH_SCREEN_STYLE = "android.activity.splashScreenStyle"; - /** See {@link #setTransientLaunch()}. */ - private static final String KEY_TRANSIENT_LAUNCH = "android.activity.transientLaunch"; + /** + * See {@link #setTransientLaunch()}. + * @hide + */ + public static final String KEY_TRANSIENT_LAUNCH = "android.activity.transientLaunch"; /** see {@link #makeLaunchIntoPip(PictureInPictureParams)}. */ private static final String KEY_LAUNCH_INTO_PIP_PARAMS = diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 36e57620c9dd..3c6ff2865d04 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -137,7 +137,7 @@ oneway interface ITaskStackListener { * activities inside it belong to a managed profile user, and that user has just * been locked. */ - void onTaskProfileLocked(in ActivityManager.RunningTaskInfo taskInfo); + void onTaskProfileLocked(in ActivityManager.RunningTaskInfo taskInfo, int userId); /** * Called when a task snapshot got updated. diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 99a7fa21b911..705b5ee84d01 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -407,7 +407,7 @@ public final class PendingIntent implements Parcelable { } private static void checkPendingIntent(int flags, @NonNull Intent intent, - @NonNull Context context) { + @NonNull Context context, boolean isActivityResultType) { final boolean isFlagImmutableSet = (flags & PendingIntent.FLAG_IMMUTABLE) != 0; final boolean isFlagMutableSet = (flags & PendingIntent.FLAG_MUTABLE) != 0; final String packageName = context.getPackageName(); @@ -428,11 +428,12 @@ public final class PendingIntent implements Parcelable { throw new IllegalArgumentException(msg); } - // For apps with target SDK < U, warn that creation or retrieval of a mutable - // implicit PendingIntent will be blocked from target SDK U onwards for security - // reasons. The block itself happens on the server side, but this warning has to - // stay here to preserve the client side stack trace for app developers. - if (isNewMutableDisallowedImplicitPendingIntent(flags, intent) + // For apps with target SDK < U, warn that creation or retrieval of a mutable implicit + // PendingIntent that is not of type {@link ActivityManager#INTENT_SENDER_ACTIVITY_RESULT} + // will be blocked from target SDK U onwards for security reasons. The block itself + // happens on the server side, but this warning has to stay here to preserve the client + // side stack trace for app developers. + if (isNewMutableDisallowedImplicitPendingIntent(flags, intent, isActivityResultType) && !Compatibility.isChangeEnabled(BLOCK_MUTABLE_IMPLICIT_PENDING_INTENT)) { String msg = "New mutable implicit PendingIntent: pkg=" + packageName + ", action=" + intent.getAction() @@ -445,7 +446,13 @@ public final class PendingIntent implements Parcelable { /** @hide */ public static boolean isNewMutableDisallowedImplicitPendingIntent(int flags, - @NonNull Intent intent) { + @NonNull Intent intent, boolean isActivityResultType) { + if (isActivityResultType) { + // Pending intents of type {@link ActivityManager#INTENT_SENDER_ACTIVITY_RESULT} + // should be ignored as they are intrinsically tied to a target which means they + // are already explicit. + return false; + } boolean isFlagNoCreateSet = (flags & PendingIntent.FLAG_NO_CREATE) != 0; boolean isFlagMutableSet = (flags & PendingIntent.FLAG_MUTABLE) != 0; boolean isImplicit = (intent.getComponent() == null) && (intent.getPackage() == null); @@ -534,7 +541,7 @@ public final class PendingIntent implements Parcelable { @NonNull Intent intent, int flags, Bundle options, UserHandle user) { String packageName = context.getPackageName(); String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver()); - checkPendingIntent(flags, intent, context); + checkPendingIntent(flags, intent, context, /* isActivityResultType */ false); try { intent.migrateExtraStreamToClipData(context); intent.prepareToLeaveProcess(context); @@ -668,7 +675,7 @@ public final class PendingIntent implements Parcelable { intents[i].migrateExtraStreamToClipData(context); intents[i].prepareToLeaveProcess(context); resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver()); - checkPendingIntent(flags, intents[i], context); + checkPendingIntent(flags, intents[i], context, /* isActivityResultType */ false); } try { IIntentSender target = @@ -721,7 +728,7 @@ public final class PendingIntent implements Parcelable { Intent intent, int flags, UserHandle userHandle) { String packageName = context.getPackageName(); String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver()); - checkPendingIntent(flags, intent, context); + checkPendingIntent(flags, intent, context, /* isActivityResultType */ false); try { intent.prepareToLeaveProcess(context); IIntentSender target = @@ -800,7 +807,7 @@ public final class PendingIntent implements Parcelable { Intent intent, int flags, int serviceKind) { String packageName = context.getPackageName(); String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver()); - checkPendingIntent(flags, intent, context); + checkPendingIntent(flags, intent, context, /* isActivityResultType */ false); try { intent.prepareToLeaveProcess(context); IIntentSender target = diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index 774bc06e1adb..0290cee94dc3 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -154,8 +154,18 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override + public void onTaskProfileLocked(RunningTaskInfo taskInfo, int userId) + throws RemoteException { + onTaskProfileLocked(taskInfo); + } + + /** + * @deprecated see {@link #onTaskProfileLocked(RunningTaskInfo, int)} + */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public void onTaskProfileLocked(RunningTaskInfo taskInfo) throws RemoteException { + public void onTaskProfileLocked(RunningTaskInfo taskInfo) + throws RemoteException { } @Override diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 27f5545d8f03..e0364cbe1d69 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -13401,11 +13401,10 @@ public class DevicePolicyManager { /** * Called by a device admin or holder of the permission * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE} to set the short - * support message. This will be displayed to the user - * in settings screens where funtionality has been disabled by the admin. The message should be - * limited to a short statement such as "This setting is disabled by your administrator. Contact - * someone@example.com for support." If the message is longer than 200 characters it may be - * truncated. + * support message. This will be displayed to the user in settings screens where functionality + * has been disabled by the admin. The message should be limited to a short statement such as + * "This setting is disabled by your administrator. Contact someone@example.com for support." + * If the message is longer than 200 characters it may be truncated. * <p> * If the short support message needs to be localized, it is the responsibility of the * {@link DeviceAdminReceiver} to listen to the {@link Intent#ACTION_LOCALE_CHANGED} broadcast @@ -13460,7 +13459,8 @@ public class DevicePolicyManager { /** * Called by a device admin to set the long support message. This will be displayed to the user - * in the device administators settings screen. + * in the device administrators settings screen. If the message is longer than 20000 characters + * it may be truncated. * <p> * If the long support message needs to be localized, it is the responsibility of the * {@link DeviceAdminReceiver} to listen to the {@link Intent#ACTION_LOCALE_CHANGED} broadcast diff --git a/core/java/android/credentials/ui/RequestInfo.java b/core/java/android/credentials/ui/RequestInfo.java index 09d2db89a043..9ebb0585afd0 100644 --- a/core/java/android/credentials/ui/RequestInfo.java +++ b/core/java/android/credentials/ui/RequestInfo.java @@ -30,6 +30,8 @@ import com.android.internal.util.AnnotationValidations; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; /** * Contains information about the request that initiated this UX flow. @@ -64,6 +66,9 @@ public final class RequestInfo implements Parcelable { @Nullable private final CreateCredentialRequest mCreateCredentialRequest; + @NonNull + private final List<String> mDefaultProviderIds; + @Nullable private final GetCredentialRequest mGetCredentialRequest; @@ -83,7 +88,8 @@ public final class RequestInfo implements Parcelable { @NonNull String appPackageName) { return new RequestInfo( token, TYPE_CREATE, appPackageName, createCredentialRequest, null, - /*hasPermissionToOverrideDefault=*/ false); + /*hasPermissionToOverrideDefault=*/ false, + /*defaultProviderIds=*/ new ArrayList<>()); } /** @@ -94,10 +100,11 @@ public final class RequestInfo implements Parcelable { @NonNull public static RequestInfo newCreateRequestInfo( @NonNull IBinder token, @NonNull CreateCredentialRequest createCredentialRequest, - @NonNull String appPackageName, boolean hasPermissionToOverrideDefault) { + @NonNull String appPackageName, boolean hasPermissionToOverrideDefault, + @NonNull List<String> defaultProviderIds) { return new RequestInfo( token, TYPE_CREATE, appPackageName, createCredentialRequest, null, - hasPermissionToOverrideDefault); + hasPermissionToOverrideDefault, defaultProviderIds); } /** Creates new {@code RequestInfo} for a get-credential flow. */ @@ -107,7 +114,8 @@ public final class RequestInfo implements Parcelable { @NonNull String appPackageName) { return new RequestInfo( token, TYPE_GET, appPackageName, null, getCredentialRequest, - /*hasPermissionToOverrideDefault=*/ false); + /*hasPermissionToOverrideDefault=*/ false, + /*defaultProviderIds=*/ new ArrayList<>()); } @@ -149,6 +157,20 @@ public final class RequestInfo implements Parcelable { } /** + * Returns default provider identifier (flattened component name) configured from the user + * settings. + * + * Will only be possibly non-empty for the create use case. Not meaningful for the sign-in use + * case. + * + * @hide + */ + @NonNull + public List<String> getDefaultProviderIds() { + return mDefaultProviderIds; + } + + /** * Returns the non-null GetCredentialRequest when the type of the request is {@link * #TYPE_GET}, or null otherwise. */ @@ -161,13 +183,15 @@ public final class RequestInfo implements Parcelable { @NonNull String appPackageName, @Nullable CreateCredentialRequest createCredentialRequest, @Nullable GetCredentialRequest getCredentialRequest, - boolean hasPermissionToOverrideDefault) { + boolean hasPermissionToOverrideDefault, + @NonNull List<String> defaultProviderIds) { mToken = token; mType = type; mAppPackageName = appPackageName; mCreateCredentialRequest = createCredentialRequest; mGetCredentialRequest = getCredentialRequest; mHasPermissionToOverrideDefault = hasPermissionToOverrideDefault; + mDefaultProviderIds = defaultProviderIds == null ? new ArrayList<>() : defaultProviderIds; } private RequestInfo(@NonNull Parcel in) { @@ -188,6 +212,7 @@ public final class RequestInfo implements Parcelable { mCreateCredentialRequest = createCredentialRequest; mGetCredentialRequest = getCredentialRequest; mHasPermissionToOverrideDefault = in.readBoolean(); + mDefaultProviderIds = in.createStringArrayList(); } @Override @@ -198,6 +223,7 @@ public final class RequestInfo implements Parcelable { dest.writeTypedObject(mCreateCredentialRequest, flags); dest.writeTypedObject(mGetCredentialRequest, flags); dest.writeBoolean(mHasPermissionToOverrideDefault); + dest.writeStringList(mDefaultProviderIds); } @Override diff --git a/core/java/android/net/metrics/WakeupStats.java b/core/java/android/net/metrics/WakeupStats.java index bb36536fe2ce..fac747c8f1a3 100644 --- a/core/java/android/net/metrics/WakeupStats.java +++ b/core/java/android/net/metrics/WakeupStats.java @@ -80,18 +80,20 @@ public class WakeupStats { break; } - switch (ev.dstHwAddr.getAddressType()) { - case MacAddress.TYPE_UNICAST: - l2UnicastCount++; - break; - case MacAddress.TYPE_MULTICAST: - l2MulticastCount++; - break; - case MacAddress.TYPE_BROADCAST: - l2BroadcastCount++; - break; - default: - break; + if (ev.dstHwAddr != null) { + switch (ev.dstHwAddr.getAddressType()) { + case MacAddress.TYPE_UNICAST: + l2UnicastCount++; + break; + case MacAddress.TYPE_MULTICAST: + l2MulticastCount++; + break; + case MacAddress.TYPE_BROADCAST: + l2BroadcastCount++; + break; + default: + break; + } } increment(ethertypes, ev.ethertype); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 3487b013fa0b..4a46beb670de 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4677,16 +4677,22 @@ public final class Settings { "display_color_mode_vendor_hint"; /** - * Whether or not the peak refresh rate should be forced. 0=no, 1=yes + * The user selected min refresh rate in frames per second. + * + * If this isn't set, 0 will be used. * @hide */ - public static final String FORCE_PEAK_REFRESH_RATE = "force_peak_refresh_rate"; + @Readable + public static final String MIN_REFRESH_RATE = "min_refresh_rate"; /** - * Whether or not the peak refresh rate should be used for some content. 0=no, 1=yes + * The user selected peak refresh rate in frames per second. + * + * If this isn't set, the system falls back to a device specific default. * @hide */ - public static final String SMOOTH_DISPLAY = "smooth_display"; + @Readable + public static final String PEAK_REFRESH_RATE = "peak_refresh_rate"; /** * The amount of time in milliseconds before the device goes to sleep or begins @@ -11496,6 +11502,8 @@ public final class Settings { public static final int DEVICE_STATE_ROTATION_KEY_HALF_FOLDED = 1; /** @hide */ public static final int DEVICE_STATE_ROTATION_KEY_UNFOLDED = 2; + /** @hide */ + public static final int DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY = 3; /** * The different postures that can be used as keys with @@ -11507,6 +11515,7 @@ public final class Settings { DEVICE_STATE_ROTATION_KEY_FOLDED, DEVICE_STATE_ROTATION_KEY_HALF_FOLDED, DEVICE_STATE_ROTATION_KEY_UNFOLDED, + DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY, }) @Retention(RetentionPolicy.SOURCE) public @interface DeviceStateRotationLockKey { diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java index 4a848dd86463..8afae74735e2 100644 --- a/core/java/android/service/autofill/FillRequest.java +++ b/core/java/android/service/autofill/FillRequest.java @@ -97,8 +97,6 @@ public final class FillRequest implements Parcelable { */ public static final @RequestFlags int FLAG_VIEW_NOT_FOCUSED = 0x10; - // The flag value 0x20 has been defined in AutofillManager. - /** * Indicates the request supports fill dialog presentation for the fields, the * system will send the request when the activity just started. diff --git a/core/java/android/service/credentials/CredentialProviderInfoFactory.java b/core/java/android/service/credentials/CredentialProviderInfoFactory.java index 1a1df6f5f989..751c675b28f4 100644 --- a/core/java/android/service/credentials/CredentialProviderInfoFactory.java +++ b/core/java/android/service/credentials/CredentialProviderInfoFactory.java @@ -75,12 +75,13 @@ public final class CredentialProviderInfoFactory { /** * Constructs an information instance of the credential provider. * - * @param context the context object + * @param context the context object * @param serviceComponent the serviceComponent of the provider service - * @param userId the android userId for which the current process is running + * @param userId the android userId for which the current process is running * @param isSystemProvider whether this provider is a system provider * @throws PackageManager.NameNotFoundException If provider service is not found - * @throws SecurityException If provider does not require the relevant permission + * @throws SecurityException If provider does not require the relevant + * permission */ public static CredentialProviderInfo create( @NonNull Context context, @@ -99,13 +100,15 @@ public final class CredentialProviderInfoFactory { /** * Constructs an information instance of the credential provider. * - * @param context the context object - * @param serviceInfo the service info for the provider app. This must be retrieved from the - * {@code PackageManager} - * @param isSystemProvider whether the provider app is a system provider + * @param context the context object + * @param serviceInfo the service info for the provider app. This must + * be retrieved from the + * {@code PackageManager} + * @param isSystemProvider whether the provider app is a system provider * @param disableSystemAppVerificationForTests whether to disable system app permission - * verification so that tests can install system providers - * @param isEnabled whether the user enabled this provider + * verification so that tests can install system + * providers + * @param isEnabled whether the user enabled this provider * @throws SecurityException If provider does not require the relevant permission */ public static CredentialProviderInfo create( @@ -374,7 +377,6 @@ public final class CredentialProviderInfoFactory { if (appInfo == null || serviceInfo == null) { continue; } - services.add(serviceInfo); } catch (SecurityException | PackageManager.NameNotFoundException e) { Slog.e(TAG, "Error getting info for " + serviceInfo, e); diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java index 53a5fd5c634d..cf2e6a639fce 100644 --- a/core/java/android/service/credentials/CredentialProviderService.java +++ b/core/java/android/service/credentials/CredentialProviderService.java @@ -18,7 +18,6 @@ package android.service.credentials; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; -import android.Manifest; import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.SdkConstant; @@ -35,7 +34,7 @@ import android.os.ICancellationSignal; import android.os.Looper; import android.os.OutcomeReceiver; import android.os.RemoteException; -import android.util.Log; +import android.util.Slog; import java.util.Objects; @@ -226,7 +225,7 @@ public abstract class CredentialProviderService extends Service { if (SERVICE_INTERFACE.equals(intent.getAction())) { return mInterface.asBinder(); } - Log.d(TAG, "Failed to bind with intent: " + intent); + Slog.w(TAG, "Failed to bind with intent: " + intent); return null; } @@ -252,11 +251,6 @@ public abstract class CredentialProviderService extends Service { GetCredentialException>() { @Override public void onResult(BeginGetCredentialResponse result) { - // If provider service does not possess the HYBRID permission, this - // check will throw an exception in the provider process. - if (result.getRemoteCredentialEntry() != null) { - enforceRemoteEntryPermission(); - } try { callback.onSuccess(result); } catch (RemoteException e) { @@ -274,15 +268,6 @@ public abstract class CredentialProviderService extends Service { } )); } - private void enforceRemoteEntryPermission() { - String permission = - Manifest.permission.PROVIDE_REMOTE_CREDENTIALS; - getApplicationContext().enforceCallingOrSelfPermission( - permission, - String.format("Provider must have %s, in order to set a " - + "remote entry", permission) - ); - } @Override public void onBeginCreateCredential(BeginCreateCredentialRequest request, @@ -305,11 +290,6 @@ public abstract class CredentialProviderService extends Service { BeginCreateCredentialResponse, CreateCredentialException>() { @Override public void onResult(BeginCreateCredentialResponse result) { - // If provider service does not possess the HYBRID permission, this - // check will throw an exception in the provider process. - if (result.getRemoteCreateEntry() != null) { - enforceRemoteEntryPermission(); - } try { callback.onSuccess(result); } catch (RemoteException e) { diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index 79a4f54bac0a..7822ddeb73d8 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -913,8 +913,6 @@ public class VoiceInteractionService extends Service { * sandboxed process. * @param callback The callback to notify of detection events. * @return An instanece of {@link VisualQueryDetector}. - * @throws UnsupportedOperationException if only single detector is supported. Multiple detector - * is only available for apps targeting {@link Build.VERSION_CODES#TIRAMISU} and above. * @throws IllegalStateException when there is an existing {@link VisualQueryDetector}, or when * there is a non-trusted hotword detector running. * @@ -935,21 +933,16 @@ public class VoiceInteractionService extends Service { throw new IllegalStateException("Not available until onReady() is called"); } synchronized (mLock) { - if (!CompatChanges.isChangeEnabled(MULTIPLE_ACTIVE_HOTWORD_DETECTORS)) { - throw new UnsupportedOperationException("VisualQueryDetector is only available if " - + "multiple detectors are allowed"); - } else { - if (mActiveVisualQueryDetector != null) { + if (mActiveVisualQueryDetector != null) { + throw new IllegalStateException( + "There is already an active VisualQueryDetector. " + + "It must be destroyed to create a new one."); + } + for (HotwordDetector detector : mActiveDetectors) { + if (!detector.isUsingSandboxedDetectionService()) { throw new IllegalStateException( - "There is already an active VisualQueryDetector. " - + "It must be destroyed to create a new one."); - } - for (HotwordDetector detector : mActiveDetectors) { - if (!detector.isUsingSandboxedDetectionService()) { - throw new IllegalStateException( - "It disallows to create trusted and non-trusted detectors " - + "at the same time."); - } + "It disallows to create trusted and non-trusted detectors " + + "at the same time."); } } @@ -1070,21 +1063,6 @@ public class VoiceInteractionService extends Service { mActiveVisualQueryDetector = null; } mActiveDetectors.remove(detector); - shutdownHotwordDetectionServiceIfRequiredLocked(); - } - } - - private void shutdownHotwordDetectionServiceIfRequiredLocked() { - for (HotwordDetector detector : mActiveDetectors) { - if (detector.isUsingSandboxedDetectionService()) { - return; - } - } - - try { - mSystemService.shutdownHotwordDetectionService(); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); } } diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index f2373fbfa858..d8fa533dbef7 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -360,6 +360,14 @@ public final class DisplayInfo implements Parcelable { public SparseArray<SurfaceControl.RefreshRateRange> thermalRefreshRateThrottling = new SparseArray<>(); + /** + * The ID of the brightness throttling data that should be used. This can change e.g. in + * concurrent displays mode in which a stricter brightness throttling policy might need to be + * used. + */ + @Nullable + public String thermalBrightnessThrottlingDataId; + public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() { @Override public DisplayInfo createFromParcel(Parcel source) { @@ -437,7 +445,9 @@ public final class DisplayInfo implements Parcelable { && Objects.equals(displayShape, other.displayShape) && Objects.equals(layoutLimitedRefreshRate, other.layoutLimitedRefreshRate) && BrightnessSynchronizer.floatEquals(hdrSdrRatio, other.hdrSdrRatio) - && thermalRefreshRateThrottling.contentEquals(other.thermalRefreshRateThrottling); + && thermalRefreshRateThrottling.contentEquals(other.thermalRefreshRateThrottling) + && Objects.equals( + thermalBrightnessThrottlingDataId, other.thermalBrightnessThrottlingDataId); } @Override @@ -495,6 +505,7 @@ public final class DisplayInfo implements Parcelable { layoutLimitedRefreshRate = other.layoutLimitedRefreshRate; hdrSdrRatio = other.hdrSdrRatio; thermalRefreshRateThrottling = other.thermalRefreshRateThrottling; + thermalBrightnessThrottlingDataId = other.thermalBrightnessThrottlingDataId; } public void readFromParcel(Parcel source) { @@ -559,6 +570,7 @@ public final class DisplayInfo implements Parcelable { hdrSdrRatio = source.readFloat(); thermalRefreshRateThrottling = source.readSparseArray(null, SurfaceControl.RefreshRateRange.class); + thermalBrightnessThrottlingDataId = source.readString8(); } @Override @@ -620,6 +632,7 @@ public final class DisplayInfo implements Parcelable { dest.writeTypedObject(layoutLimitedRefreshRate, flags); dest.writeFloat(hdrSdrRatio); dest.writeSparseArray(thermalRefreshRateThrottling); + dest.writeString8(thermalBrightnessThrottlingDataId); } @Override @@ -889,6 +902,8 @@ public final class DisplayInfo implements Parcelable { } sb.append(", thermalRefreshRateThrottling "); sb.append(thermalRefreshRateThrottling); + sb.append(", thermalBrightnessThrottlingDataId "); + sb.append(thermalBrightnessThrottlingDataId); sb.append("}"); return sb.toString(); } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index f7b7d3387938..e39b3a182b28 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -16,7 +16,6 @@ package android.view.autofill; -import static android.Manifest.permission.PROVIDE_OWN_AUTOFILL_SUGGESTIONS; import static android.service.autofill.FillRequest.FLAG_IME_SHOWING; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; @@ -30,7 +29,6 @@ import static android.view.autofill.Helper.sVerbose; import static android.view.autofill.Helper.toList; import android.accessibilityservice.AccessibilityServiceInfo; -import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -52,21 +50,16 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.Rect; import android.metrics.LogMaker; -import android.os.Binder; import android.os.Build; import android.os.Bundle; -import android.os.CancellationSignal; import android.os.Handler; import android.os.IBinder; -import android.os.ICancellationSignal; import android.os.Looper; import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; import android.service.autofill.AutofillService; -import android.service.autofill.FillCallback; import android.service.autofill.FillEventHistory; -import android.service.autofill.IFillCallback; import android.service.autofill.UserData; import android.text.TextUtils; import android.util.ArrayMap; @@ -87,7 +80,6 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.AccessibilityWindowInfo; -import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InputMethodManager; import android.widget.CheckBox; import android.widget.DatePicker; @@ -187,12 +179,6 @@ import sun.misc.Cleaner; * shows an autofill save UI if the value of savable views have changed. If the user selects the * option to Save, the current value of the views is then sent to the autofill service. * - * <p>There is another choice for the application to provide it's datasets to the Autofill framework - * by setting an {@link AutofillRequestCallback} through - * {@link #setAutofillRequestCallback(Executor, AutofillRequestCallback)}. The application can use - * its callback instead of the default {@link AutofillService}. See - * {@link AutofillRequestCallback} for more details. - * * <h3 id="additional-notes">Additional notes</h3> * * <p>It is safe to call <code>AutofillManager</code> methods from any thread. @@ -326,7 +312,6 @@ public final class AutofillManager { /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2; /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4; /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY = 0x8; - /** @hide */ public static final int FLAG_ENABLED_CLIENT_SUGGESTIONS = 0x20; // NOTE: flag below is used by the session start receiver only, hence it can have values above /** @hide */ public static final int RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY = 0x1; @@ -653,11 +638,6 @@ public final class AutofillManager { @GuardedBy("mLock") private boolean mEnabledForAugmentedAutofillOnly; - @GuardedBy("mLock") - @Nullable private AutofillRequestCallback mAutofillRequestCallback; - @GuardedBy("mLock") - @Nullable private Executor mRequestCallbackExecutor; - /** * Indicates whether there is already a field to do a fill request after * the activity started. @@ -2338,44 +2318,6 @@ public final class AutofillManager { return new AutofillId(parent.getAutofillViewId(), virtualId); } - /** - * Sets the client's suggestions callback for autofill. - * - * @see AutofillRequestCallback - * - * @param executor specifies the thread upon which the callbacks will be invoked. - * @param callback which handles autofill request to provide client's suggestions. - * - * @hide - */ - @TestApi - @RequiresPermission(PROVIDE_OWN_AUTOFILL_SUGGESTIONS) - public void setAutofillRequestCallback(@NonNull @CallbackExecutor Executor executor, - @NonNull AutofillRequestCallback callback) { - if (mContext.checkSelfPermission(PROVIDE_OWN_AUTOFILL_SUGGESTIONS) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires PROVIDE_OWN_AUTOFILL_SUGGESTIONS permission!"); - } - - synchronized (mLock) { - mRequestCallbackExecutor = executor; - mAutofillRequestCallback = callback; - } - } - - /** - * clears the client's suggestions callback for autofill. - * - * @hide - */ - @TestApi - public void clearAutofillRequestCallback() { - synchronized (mLock) { - mRequestCallbackExecutor = null; - mAutofillRequestCallback = null; - } - } - @GuardedBy("mLock") private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds, @NonNull AutofillValue value, int flags) { @@ -2436,13 +2378,6 @@ public final class AutofillManager { } } - if (mAutofillRequestCallback != null) { - if (sDebug) { - Log.d(TAG, "startSession with the client suggestions provider"); - } - flags |= FLAG_ENABLED_CLIENT_SUGGESTIONS; - } - mService.startSession(client.autofillClientGetActivityToken(), mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), mCallback != null, flags, clientActivity, @@ -2796,28 +2731,6 @@ public final class AutofillManager { } } - private void onFillRequest(InlineSuggestionsRequest request, - CancellationSignal cancellationSignal, FillCallback callback) { - final AutofillRequestCallback autofillRequestCallback; - final Executor executor; - synchronized (mLock) { - autofillRequestCallback = mAutofillRequestCallback; - executor = mRequestCallbackExecutor; - } - if (autofillRequestCallback != null && executor != null) { - final long ident = Binder.clearCallingIdentity(); - try { - executor.execute(() -> - autofillRequestCallback.onFillRequest( - request, cancellationSignal, callback)); - } finally { - Binder.restoreCallingIdentity(ident); - } - } else { - callback.onSuccess(null); - } - } - /** @hide */ public static final int SET_STATE_FLAG_ENABLED = 0x01; /** @hide */ @@ -4374,23 +4287,6 @@ public final class AutofillManager { } @Override - public void requestFillFromClient(int id, InlineSuggestionsRequest request, - IFillCallback callback) { - final AutofillManager afm = mAfm.get(); - if (afm != null) { - ICancellationSignal transport = CancellationSignal.createTransport(); - try { - callback.onCancellable(transport); - } catch (RemoteException e) { - Slog.w(TAG, "Error requesting a cancellation", e); - } - - afm.onFillRequest(request, CancellationSignal.fromTransport(transport), - new FillCallback(callback, id)); - } - } - - @Override public void notifyFillDialogTriggerIds(List<AutofillId> ids) { final AutofillManager afm = mAfm.get(); if (afm != null) { diff --git a/core/java/android/view/autofill/AutofillRequestCallback.java b/core/java/android/view/autofill/AutofillRequestCallback.java deleted file mode 100644 index 10a088b4ebfa..000000000000 --- a/core/java/android/view/autofill/AutofillRequestCallback.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.view.autofill; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.TestApi; -import android.os.CancellationSignal; -import android.service.autofill.FillCallback; -import android.view.inputmethod.InlineSuggestionsRequest; - -/** - * <p>This class is used to provide some input suggestions to the Autofill framework. - * - * <P>When the user is requested to input something, Autofill will try to query input suggestions - * for the user choosing. If the application want to provide some internal input suggestions, - * implements this callback and register via - * {@link AutofillManager#setAutofillRequestCallback(java.util.concurrent.Executor, - * AutofillRequestCallback)}. Autofill will callback the - * {@link #onFillRequest(InlineSuggestionsRequest, CancellationSignal, FillCallback)} to request - * input suggestions. - * - * <P>To make sure the callback to take effect, must register before the autofill session starts. - * If the autofill session is started, calls {@link AutofillManager#cancel()} to finish current - * session, and then the callback will be used at the next restarted session. - * - * <P>To create a {@link android.service.autofill.FillResponse}, application should fetch - * {@link AutofillId}s from its view structure. Below is an example: - * <pre class="prettyprint"> - * AutofillId usernameId = findViewById(R.id.username).getAutofillId(); - * AutofillId passwordId = findViewById(R.id.password).getAutofillId(); - * </pre> - * To learn more about creating a {@link android.service.autofill.FillResponse}, read - * <a href="/guide/topics/text/autofill-services#fill">Fill out client views</a>. - * - * <P>To fallback to the default {@link android.service.autofill.AutofillService}, just respond - * a null of the {@link android.service.autofill.FillResponse}. And then Autofill will do a fill - * request with the default {@link android.service.autofill.AutofillService}. Or clear the callback - * from {@link AutofillManager} via {@link AutofillManager#clearAutofillRequestCallback()}. If the - * client would like to keep no suggestions for the field, respond with an empty - * {@link android.service.autofill.FillResponse} which has no dataset. - * - * <P>IMPORTANT: This should not be used for displaying anything other than input suggestions, or - * the keyboard may choose to block your app from the inline strip. - * - * @hide - */ -@TestApi -public interface AutofillRequestCallback { - /** - * Called by the Android system to decide if a screen can be autofilled by the callback. - * - * @param inlineSuggestionsRequest the {@link InlineSuggestionsRequest request} to handle if - * currently inline suggestions are supported and can be displayed. - * @param cancellationSignal signal for observing cancellation requests. The system will use - * this to notify you that the fill result is no longer needed and you should stop - * handling this fill request in order to save resources. - * @param callback object used to notify the result of the request. - */ - void onFillRequest(@Nullable InlineSuggestionsRequest inlineSuggestionsRequest, - @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback); -} diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl index 2e5967cc32d1..51afe4cf784d 100644 --- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl +++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl @@ -24,11 +24,9 @@ import android.content.Intent; import android.content.IntentSender; import android.graphics.Rect; import android.os.IBinder; -import android.service.autofill.IFillCallback; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; import android.view.autofill.IAutofillWindowPresenter; -import android.view.inputmethod.InlineSuggestionsRequest; import android.view.KeyEvent; import com.android.internal.os.IResultReceiver; @@ -144,12 +142,6 @@ oneway interface IAutoFillManagerClient { void requestShowSoftInput(in AutofillId id); /** - * Requests to determine if a screen can be autofilled by the client app. - */ - void requestFillFromClient(int id, in InlineSuggestionsRequest request, - in IFillCallback callback); - - /** * Notifies autofill ids that require to show the fill dialog. */ void notifyFillDialogTriggerIds(in List<AutofillId> ids); diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java index 77a2b5b877be..581feca2de55 100644 --- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java +++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java @@ -112,22 +112,6 @@ public final class InlineSuggestionsRequest implements Parcelable { private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec; /** - * Whether the IME supports inline suggestions from the default Autofill service that - * provides the input view. - * - * Note: The default value is {@code true}. - */ - private boolean mServiceSupported; - - /** - * Whether the IME supports inline suggestions from the application that provides the - * input view. - * - * Note: The default value is {@code true}. - */ - private boolean mClientSupported; - - /** * @hide * @see {@link #mHostInputToken}. */ @@ -221,15 +205,9 @@ public final class InlineSuggestionsRequest implements Parcelable { return Bundle.EMPTY; } - private static boolean defaultServiceSupported() { - return true; - } - - private static boolean defaultClientSupported() { - return true; - } - - /** @hide */ + /** + * @hide + */ abstract static class BaseBuilder { abstract Builder setInlinePresentationSpecs( @NonNull List<android.widget.inline.InlinePresentationSpec> specs); @@ -241,25 +219,14 @@ public final class InlineSuggestionsRequest implements Parcelable { abstract Builder setHostDisplayId(int value); } - /** @hide */ - public boolean isServiceSupported() { - return mServiceSupported; - } - - /** @hide */ - public boolean isClientSupported() { - return mClientSupported; - } - - - // Code below generated by codegen v1.0.22. + // Code below generated by codegen v1.0.23. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java + // $ codegen $ANDROID_BUILD_TOP/./frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -275,9 +242,7 @@ public final class InlineSuggestionsRequest implements Parcelable { @NonNull Bundle extras, @Nullable IBinder hostInputToken, int hostDisplayId, - @Nullable InlinePresentationSpec inlineTooltipPresentationSpec, - boolean serviceSupported, - boolean clientSupported) { + @Nullable InlinePresentationSpec inlineTooltipPresentationSpec) { this.mMaxSuggestionCount = maxSuggestionCount; this.mInlinePresentationSpecs = inlinePresentationSpecs; com.android.internal.util.AnnotationValidations.validate( @@ -294,8 +259,6 @@ public final class InlineSuggestionsRequest implements Parcelable { this.mHostInputToken = hostInputToken; this.mHostDisplayId = hostDisplayId; this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec; - this.mServiceSupported = serviceSupported; - this.mClientSupported = clientSupported; onConstructed(); } @@ -379,9 +342,7 @@ public final class InlineSuggestionsRequest implements Parcelable { } /** - * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response. - * - * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec) + * Specifies the UI specification for the inline suggestion tooltip in the response. */ @DataClass.Generated.Member public @Nullable InlinePresentationSpec getInlineTooltipPresentationSpec() { @@ -402,9 +363,7 @@ public final class InlineSuggestionsRequest implements Parcelable { "extras = " + mExtras + ", " + "hostInputToken = " + mHostInputToken + ", " + "hostDisplayId = " + mHostDisplayId + ", " + - "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec + ", " + - "serviceSupported = " + mServiceSupported + ", " + - "clientSupported = " + mClientSupported + + "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec + " }"; } @@ -428,9 +387,7 @@ public final class InlineSuggestionsRequest implements Parcelable { && extrasEquals(that.mExtras) && java.util.Objects.equals(mHostInputToken, that.mHostInputToken) && mHostDisplayId == that.mHostDisplayId - && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec) - && mServiceSupported == that.mServiceSupported - && mClientSupported == that.mClientSupported; + && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec); } @Override @@ -448,8 +405,6 @@ public final class InlineSuggestionsRequest implements Parcelable { _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken); _hash = 31 * _hash + mHostDisplayId; _hash = 31 * _hash + java.util.Objects.hashCode(mInlineTooltipPresentationSpec); - _hash = 31 * _hash + Boolean.hashCode(mServiceSupported); - _hash = 31 * _hash + Boolean.hashCode(mClientSupported); return _hash; } @@ -460,8 +415,6 @@ public final class InlineSuggestionsRequest implements Parcelable { // void parcelFieldName(Parcel dest, int flags) { ... } int flg = 0; - if (mServiceSupported) flg |= 0x100; - if (mClientSupported) flg |= 0x200; if (mHostInputToken != null) flg |= 0x20; if (mInlineTooltipPresentationSpec != null) flg |= 0x80; dest.writeInt(flg); @@ -487,11 +440,9 @@ public final class InlineSuggestionsRequest implements Parcelable { // static FieldType unparcelFieldName(Parcel in) { ... } int flg = in.readInt(); - boolean serviceSupported = (flg & 0x100) != 0; - boolean clientSupported = (flg & 0x200) != 0; int maxSuggestionCount = in.readInt(); List<InlinePresentationSpec> inlinePresentationSpecs = new ArrayList<>(); - in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader(), android.widget.inline.InlinePresentationSpec.class); + in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader()); String hostPackageName = in.readString(); LocaleList supportedLocales = (LocaleList) in.readTypedObject(LocaleList.CREATOR); Bundle extras = in.readBundle(); @@ -515,8 +466,6 @@ public final class InlineSuggestionsRequest implements Parcelable { this.mHostInputToken = hostInputToken; this.mHostDisplayId = hostDisplayId; this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec; - this.mServiceSupported = serviceSupported; - this.mClientSupported = clientSupported; onConstructed(); } @@ -550,8 +499,6 @@ public final class InlineSuggestionsRequest implements Parcelable { private @Nullable IBinder mHostInputToken; private int mHostDisplayId; private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec; - private boolean mServiceSupported; - private boolean mClientSupported; private long mBuilderFieldsSet = 0L; @@ -684,9 +631,7 @@ public final class InlineSuggestionsRequest implements Parcelable { } /** - * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response. - * - * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)s + * Specifies the UI specification for the inline suggestion tooltip in the response. */ @DataClass.Generated.Member public @NonNull Builder setInlineTooltipPresentationSpec(@NonNull InlinePresentationSpec value) { @@ -696,44 +641,10 @@ public final class InlineSuggestionsRequest implements Parcelable { return this; } - /** - * Whether the IME supports inline suggestions from the default Autofill service that - * provides the input view. - * - * Note: The default value is {@code true}. - * - * @hide - */ - @TestApi - @DataClass.Generated.Member - public @NonNull Builder setServiceSupported(boolean value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x100; - mServiceSupported = value; - return this; - } - - /** - * Whether the IME supports inline suggestions from the application that provides the - * input view. - * - * Note: The default value is {@code true}. - * - * @hide - */ - @TestApi - @DataClass.Generated.Member - public @NonNull Builder setClientSupported(boolean value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x200; - mClientSupported = value; - return this; - } - /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull InlineSuggestionsRequest build() { checkNotUsed(); - mBuilderFieldsSet |= 0x400; // Mark builder used + mBuilderFieldsSet |= 0x100; // Mark builder used if ((mBuilderFieldsSet & 0x1) == 0) { mMaxSuggestionCount = defaultMaxSuggestionCount(); @@ -756,12 +667,6 @@ public final class InlineSuggestionsRequest implements Parcelable { if ((mBuilderFieldsSet & 0x80) == 0) { mInlineTooltipPresentationSpec = defaultInlineTooltipPresentationSpec(); } - if ((mBuilderFieldsSet & 0x100) == 0) { - mServiceSupported = defaultServiceSupported(); - } - if ((mBuilderFieldsSet & 0x200) == 0) { - mClientSupported = defaultClientSupported(); - } InlineSuggestionsRequest o = new InlineSuggestionsRequest( mMaxSuggestionCount, mInlinePresentationSpecs, @@ -770,14 +675,12 @@ public final class InlineSuggestionsRequest implements Parcelable { mExtras, mHostInputToken, mHostDisplayId, - mInlineTooltipPresentationSpec, - mServiceSupported, - mClientSupported); + mInlineTooltipPresentationSpec); return o; } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x400) != 0) { + if ((mBuilderFieldsSet & 0x100) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -785,10 +688,10 @@ public final class InlineSuggestionsRequest implements Parcelable { } @DataClass.Generated( - time = 1615798784918L, - codegenVersion = "1.0.22", + time = 1682382296877L, + codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java", - inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate boolean mServiceSupported\nprivate boolean mClientSupported\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nprivate static boolean defaultServiceSupported()\nprivate static boolean defaultClientSupported()\npublic boolean isServiceSupported()\npublic boolean isClientSupported()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []") + inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java index 8f270f5c5eeb..62f3c909dd4f 100644 --- a/core/java/android/view/inputmethod/InputConnectionWrapper.java +++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java @@ -412,4 +412,32 @@ public class InputConnectionWrapper implements InputConnection { public boolean setImeConsumesInput(boolean imeConsumesInput) { return mTarget.setImeConsumesInput(imeConsumesInput); } + + /** + * Called by the system when it needs to take a snapshot of multiple text-related data in an + * atomic manner. + * + * <p><strong>Editor authors</strong>: Supporting this method is strongly encouraged. Atomically + * taken {@link TextSnapshot} is going to be really helpful for the system when optimizing IPCs + * in a safe and deterministic manner. Return {@code null} if an atomically taken + * {@link TextSnapshot} is unavailable. The system continues supporting such a scenario + * gracefully.</p> + * + * <p><strong>IME authors</strong>: Currently IMEs cannot call this method directly and always + * receive {@code null} as the result.</p> + * + * <p>Beware that there is a bug that this method was not overridden in + * {@link InputConnectionWrapper}, which ended up always returning {@code null} when gets + * called even if the wrapped {@link InputConnection} implements this method. The bug was + * fixed in {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}.</p> + * + * @return {@code null} if {@link TextSnapshot} is unavailable and/or this API is called from + * IMEs. Beware the bug in older devices mentioned above. + * @throws NullPointerException if the target is {@code null}. + */ + @Nullable + @Override + public TextSnapshot takeSnapshot() { + return mTarget.takeSnapshot(); + } } diff --git a/core/java/android/window/ScreenCapture.java b/core/java/android/window/ScreenCapture.java index 95451a966055..fa7f577dadb8 100644 --- a/core/java/android/window/ScreenCapture.java +++ b/core/java/android/window/ScreenCapture.java @@ -198,17 +198,21 @@ public class ScreenCapture { * Create ScreenshotHardwareBuffer from an existing HardwareBuffer object. * * @param hardwareBuffer The existing HardwareBuffer object - * @param namedColorSpace Integer value of a named color space {@link ColorSpace.Named} + * @param dataspace Dataspace describing the content. + * {@see android.hardware.DataSpace} * @param containsSecureLayers Indicates whether this graphic buffer contains captured * contents of secure layers, in which case the screenshot * should not be persisted. * @param containsHdrLayers Indicates whether this graphic buffer contains HDR content. */ private static ScreenshotHardwareBuffer createFromNative(HardwareBuffer hardwareBuffer, - int namedColorSpace, boolean containsSecureLayers, boolean containsHdrLayers) { - ColorSpace colorSpace = ColorSpace.get(ColorSpace.Named.values()[namedColorSpace]); + int dataspace, boolean containsSecureLayers, boolean containsHdrLayers) { + ColorSpace colorSpace = ColorSpace.getFromDataSpace(dataspace); return new ScreenshotHardwareBuffer( - hardwareBuffer, colorSpace, containsSecureLayers, containsHdrLayers); + hardwareBuffer, + colorSpace != null ? colorSpace : ColorSpace.get(ColorSpace.Named.SRGB), + containsSecureLayers, + containsHdrLayers); } public ColorSpace getColorSpace() { @@ -271,8 +275,8 @@ public class ScreenCapture { public final boolean mAllowProtected; public final long mUid; public final boolean mGrayscale; - final SurfaceControl[] mExcludeLayers; + public final boolean mHintForSeamlessTransition; private CaptureArgs(CaptureArgs.Builder<? extends CaptureArgs.Builder<?>> builder) { mPixelFormat = builder.mPixelFormat; @@ -284,6 +288,7 @@ public class ScreenCapture { mUid = builder.mUid; mGrayscale = builder.mGrayscale; mExcludeLayers = builder.mExcludeLayers; + mHintForSeamlessTransition = builder.mHintForSeamlessTransition; } private CaptureArgs(Parcel in) { @@ -305,6 +310,7 @@ public class ScreenCapture { } else { mExcludeLayers = null; } + mHintForSeamlessTransition = in.readBoolean(); } /** Release any layers if set using {@link Builder#setExcludeLayers(SurfaceControl[])}. */ @@ -352,6 +358,7 @@ public class ScreenCapture { private long mUid = -1; private boolean mGrayscale; private SurfaceControl[] mExcludeLayers; + private boolean mHintForSeamlessTransition; /** * Construct a new {@link CaptureArgs} with the set parameters. The builder remains @@ -449,6 +456,21 @@ public class ScreenCapture { } /** + * Set whether the screenshot will be used in a system animation. + * This hint is used for picking the "best" colorspace for the screenshot, in particular + * for mixing HDR and SDR content. + * E.g., hintForSeamlessTransition is false, then a colorspace suitable for file + * encoding, such as BT2100, may be chosen. Otherwise, then the display's color space + * would be chosen, with the possibility of having an extended brightness range. This + * is important for screenshots that are directly re-routed to a SurfaceControl in + * order to preserve accurate colors. + */ + public T setHintForSeamlessTransition(boolean hintForSeamlessTransition) { + mHintForSeamlessTransition = hintForSeamlessTransition; + return getThis(); + } + + /** * Each sub class should return itself to allow the builder to chain properly */ T getThis() { @@ -471,7 +493,6 @@ public class ScreenCapture { dest.writeBoolean(mAllowProtected); dest.writeLong(mUid); dest.writeBoolean(mGrayscale); - if (mExcludeLayers != null) { dest.writeInt(mExcludeLayers.length); for (SurfaceControl excludeLayer : mExcludeLayers) { @@ -480,6 +501,7 @@ public class ScreenCapture { } else { dest.writeInt(0); } + dest.writeBoolean(mHintForSeamlessTransition); } public static final Parcelable.Creator<CaptureArgs> CREATOR = @@ -627,6 +649,7 @@ public class ScreenCapture { setUid(args.mUid); setGrayscale(args.mGrayscale); setExcludeLayers(args.mExcludeLayers); + setHintForSeamlessTransition(args.mHintForSeamlessTransition); } public Builder(SurfaceControl layer) { diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index 51382a4b265f..4d0132e46ba7 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -21,6 +21,8 @@ import android.annotation.Nullable; import android.app.Activity; import android.content.Context; import android.content.ContextWrapper; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.os.Handler; import android.os.RemoteException; import android.os.SystemProperties; @@ -421,36 +423,45 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { return false; } - boolean requestsPredictiveBack; + boolean requestsPredictiveBack = false; // Check if the context is from an activity. while ((context instanceof ContextWrapper) && !(context instanceof Activity)) { context = ((ContextWrapper) context).getBaseContext(); } + boolean shouldCheckActivity = false; + if (context instanceof Activity) { final Activity activity = (Activity) context; - if (activity.getActivityInfo().hasOnBackInvokedCallbackEnabled()) { - requestsPredictiveBack = - activity.getActivityInfo().isOnBackInvokedCallbackEnabled(); + final ActivityInfo activityInfo = activity.getActivityInfo(); + if (activityInfo != null) { + if (activityInfo.hasOnBackInvokedCallbackEnabled()) { + shouldCheckActivity = true; + requestsPredictiveBack = activityInfo.isOnBackInvokedCallbackEnabled(); + + if (DEBUG) { + Log.d(TAG, TextUtils.formatSimple( + "Activity: %s isPredictiveBackEnabled=%s", + activity.getComponentName(), + requestsPredictiveBack)); + } + } } else { - requestsPredictiveBack = - context.getApplicationInfo().isOnBackInvokedCallbackEnabled(); + Log.w(TAG, "The ActivityInfo is null, so we cannot verify if this Activity" + + " has the 'android:enableOnBackInvokedCallback' attribute." + + " The application attribute will be used as a fallback."); } + } - if (DEBUG) { - Log.d(TAG, TextUtils.formatSimple("Activity: %s isPredictiveBackEnabled=%s", - activity.getComponentName(), - requestsPredictiveBack)); - } - } else { - requestsPredictiveBack = - context.getApplicationInfo().isOnBackInvokedCallbackEnabled(); + if (!shouldCheckActivity) { + final ApplicationInfo applicationInfo = context.getApplicationInfo(); + requestsPredictiveBack = applicationInfo.isOnBackInvokedCallbackEnabled(); if (DEBUG) { Log.d(TAG, TextUtils.formatSimple("App: %s requestsPredictiveBack=%s", - context.getApplicationInfo().packageName, + applicationInfo.packageName, requestsPredictiveBack)); } } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 499d38c31b59..50f393b53277 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -1725,16 +1725,17 @@ public class ResolverActivity extends Activity implements if (inWorkProfile) { ((TextView) findViewById(R.id.open_cross_profile)).setText( - devicePolicyResourcesManager.getString(MINIRESOLVER_OPEN_IN_WORK, - () -> getString(R.string.miniresolver_open_in_work, targetDisplayLabel), + devicePolicyResourcesManager.getString(MINIRESOLVER_OPEN_IN_PERSONAL, + () -> getString(R.string.miniresolver_open_in_personal, + targetDisplayLabel), targetDisplayLabel)); ((Button) findViewById(R.id.use_same_profile_browser)).setText( devicePolicyResourcesManager.getString(MINIRESOLVER_USE_WORK_BROWSER, () -> getString(R.string.miniresolver_use_work_browser))); } else { ((TextView) findViewById(R.id.open_cross_profile)).setText( - devicePolicyResourcesManager.getString(MINIRESOLVER_OPEN_IN_PERSONAL, - () -> getString(R.string.miniresolver_open_in_personal, + devicePolicyResourcesManager.getString(MINIRESOLVER_OPEN_IN_WORK, + () -> getString(R.string.miniresolver_open_in_work, targetDisplayLabel), targetDisplayLabel)); ((Button) findViewById(R.id.use_same_profile_browser)).setText( diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java index 853fe2f114f7..86c2893c9fab 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java @@ -76,7 +76,7 @@ public class SystemUiSystemPropertiesFlags { /** Gating the removal of sorting-notifications-by-interruptiveness. */ public static final Flag NO_SORT_BY_INTERRUPTIVENESS = - devFlag("persist.sysui.notification.no_sort_by_interruptiveness"); + releasedFlag("persist.sysui.notification.no_sort_by_interruptiveness"); /** Gating the logging of DND state change events. */ public static final Flag LOG_DND_STATE_EVENTS = @@ -115,7 +115,7 @@ public class SystemUiSystemPropertiesFlags { } /** - * Creates a flag that is enabled by default in debuggable builds. + * Creates a flag that is disabled by default in debuggable builds. * It can be enabled by setting this flag's SystemProperty to 1. * * This flag is ALWAYS disabled in release builds. diff --git a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java deleted file mode 100644 index 39d8380c7e95..000000000000 --- a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java +++ /dev/null @@ -1,92 +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.internal.display; - -import android.content.ContentResolver; -import android.content.Context; -import android.hardware.display.DisplayManager; -import android.provider.Settings; -import android.util.Log; -import android.view.Display; - -/** - * Constants and utility methods for refresh rate settings. - */ -public class RefreshRateSettingsUtils { - - private static final String TAG = "RefreshRateSettingsUtils"; - - public static final float DEFAULT_REFRESH_RATE = 60f; - - /** - * Find the highest refresh rate among all the modes of the default display. - * @param context The context - * @return The highest refresh rate - */ - public static float findHighestRefreshRateForDefaultDisplay(Context context) { - final DisplayManager dm = context.getSystemService(DisplayManager.class); - final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY); - - if (display == null) { - Log.w(TAG, "No valid default display device"); - return DEFAULT_REFRESH_RATE; - } - - float maxRefreshRate = DEFAULT_REFRESH_RATE; - for (Display.Mode mode : display.getSupportedModes()) { - if (Math.round(mode.getRefreshRate()) > maxRefreshRate) { - maxRefreshRate = mode.getRefreshRate(); - } - } - return maxRefreshRate; - } - - /** - * Get the min refresh rate which is determined by - * {@link Settings.System.FORCE_PEAK_REFRESH_RATE}. - * @param context The context - * @return The min refresh rate - */ - public static float getMinRefreshRate(Context context) { - final ContentResolver cr = context.getContentResolver(); - int forcePeakRefreshRateSetting = Settings.System.getIntForUser(cr, - Settings.System.FORCE_PEAK_REFRESH_RATE, -1, cr.getUserId()); - return forcePeakRefreshRateSetting == 1 - ? findHighestRefreshRateForDefaultDisplay(context) - : 0; - } - - /** - * Get the peak refresh rate which is determined by {@link Settings.System.SMOOTH_DISPLAY}. - * @param context The context - * @param defaultPeakRefreshRate The refresh rate to return if the setting doesn't have a value - * @return The peak refresh rate - */ - public static float getPeakRefreshRate(Context context, float defaultPeakRefreshRate) { - final ContentResolver cr = context.getContentResolver(); - int smoothDisplaySetting = Settings.System.getIntForUser(cr, - Settings.System.SMOOTH_DISPLAY, -1, cr.getUserId()); - switch (smoothDisplaySetting) { - case 0: - return DEFAULT_REFRESH_RATE; - case 1: - return findHighestRefreshRateForDefaultDisplay(context); - default: - return defaultPeakRefreshRate; - } - } -} diff --git a/core/java/com/android/internal/util/TraceBuffer.java b/core/java/com/android/internal/util/TraceBuffer.java index bfcd65d4f277..fcc77bd4f043 100644 --- a/core/java/com/android/internal/util/TraceBuffer.java +++ b/core/java/com/android/internal/util/TraceBuffer.java @@ -103,6 +103,10 @@ public class TraceBuffer<P, S extends P, T extends P> { this(bufferCapacity, new ProtoOutputStreamProvider(), null); } + public TraceBuffer(int bufferCapacity, Consumer<T> protoDequeuedCallback) { + this(bufferCapacity, new ProtoOutputStreamProvider(), protoDequeuedCallback); + } + public TraceBuffer(int bufferCapacity, ProtoProvider protoProvider, Consumer<T> protoDequeuedCallback) { mBufferCapacity = bufferCapacity; diff --git a/core/jni/android_window_ScreenCapture.cpp b/core/jni/android_window_ScreenCapture.cpp index 1b67a0da57e1..986dbe9a204c 100644 --- a/core/jni/android_window_ScreenCapture.cpp +++ b/core/jni/android_window_ScreenCapture.cpp @@ -46,6 +46,7 @@ static struct { jfieldID uid; jfieldID grayscale; jmethodID getNativeExcludeLayers; + jfieldID hintForSeamlessTransition; } gCaptureArgsClassInfo; static struct { @@ -69,23 +70,6 @@ static struct { jmethodID builder; } gScreenshotHardwareBufferClassInfo; -enum JNamedColorSpace : jint { - // ColorSpace.Named.SRGB.ordinal() = 0; - SRGB = 0, - - // ColorSpace.Named.DISPLAY_P3.ordinal() = 7; - DISPLAY_P3 = 7, -}; - -constexpr jint fromDataspaceToNamedColorSpaceValue(const ui::Dataspace dataspace) { - switch (dataspace) { - case ui::Dataspace::DISPLAY_P3: - return JNamedColorSpace::DISPLAY_P3; - default: - return JNamedColorSpace::SRGB; - } -} - static void checkAndClearException(JNIEnv* env, const char* methodName) { if (env->ExceptionCheck()) { ALOGE("An exception was thrown by callback '%s'.", methodName); @@ -119,12 +103,11 @@ public: captureResults.fenceResult.value()->waitForever(LOG_TAG); jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer( env, captureResults.buffer->toAHardwareBuffer()); - const jint namedColorSpace = - fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace); jobject screenshotHardwareBuffer = env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz, gScreenshotHardwareBufferClassInfo.builder, - jhardwareBuffer, namedColorSpace, + jhardwareBuffer, + static_cast<jint>(captureResults.capturedDataspace), captureResults.capturedSecureLayers, captureResults.capturedHdrLayers); checkAndClearException(env, "builder"); @@ -185,6 +168,9 @@ static void getCaptureArgs(JNIEnv* env, jobject captureArgsObject, CaptureArgs& captureArgs.excludeHandles.emplace(excludeObject->getHandle()); } } + captureArgs.hintForSeamlessTransition = + env->GetBooleanField(captureArgsObject, + gCaptureArgsClassInfo.hintForSeamlessTransition); } static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env, @@ -318,9 +304,10 @@ int register_android_window_ScreenCapture(JNIEnv* env) { GetFieldIDOrDie(env, captureArgsClazz, "mAllowProtected", "Z"); gCaptureArgsClassInfo.uid = GetFieldIDOrDie(env, captureArgsClazz, "mUid", "J"); gCaptureArgsClassInfo.grayscale = GetFieldIDOrDie(env, captureArgsClazz, "mGrayscale", "Z"); - gCaptureArgsClassInfo.getNativeExcludeLayers = GetMethodIDOrDie(env, captureArgsClazz, "getNativeExcludeLayers", "()[J"); + gCaptureArgsClassInfo.hintForSeamlessTransition = + GetFieldIDOrDie(env, captureArgsClazz, "mHintForSeamlessTransition", "Z"); jclass displayCaptureArgsClazz = FindClassOrDie(env, "android/window/ScreenCapture$DisplayCaptureArgs"); diff --git a/core/proto/android/companion/telecom.proto b/core/proto/android/companion/telecom.proto index 9ccadbf6eb2d..3a9e5eeb4877 100644 --- a/core/proto/android/companion/telecom.proto +++ b/core/proto/android/companion/telecom.proto @@ -20,9 +20,9 @@ package android.companion; option java_multiple_files = true; -// Next index: 2 +// Next index: 4 message Telecom { - // Next index: 5 + // Next index: 6 message Call { // UUID representing this call int64 id = 1; @@ -34,6 +34,8 @@ message Telecom { // Human-readable name of the app processing this call string app_name = 2; bytes app_icon = 3; + // Unique identifier for this app, such as a package name. + string app_identifier = 4; } Origin origin = 2; @@ -59,9 +61,11 @@ message Telecom { REJECT_AND_BLOCK = 9; IGNORE = 10; } - repeated Control controls_available = 4; + repeated Control controls = 4; } // The list of active calls. repeated Call calls = 1; + // The list of requested calls or call changes. + repeated Call requests = 2; } diff --git a/core/res/res/drawable-hdpi/pointer_all_scroll.png b/core/res/res/drawable-hdpi/pointer_all_scroll.png Binary files differindex 095aadce579d..4af84c3e9202 100644 --- a/core/res/res/drawable-hdpi/pointer_all_scroll.png +++ b/core/res/res/drawable-hdpi/pointer_all_scroll.png diff --git a/core/res/res/drawable-hdpi/pointer_horizontal_double_arrow.png b/core/res/res/drawable-hdpi/pointer_horizontal_double_arrow.png Binary files differindex 9388f162b17e..c4018c83da86 100644 --- a/core/res/res/drawable-hdpi/pointer_horizontal_double_arrow.png +++ b/core/res/res/drawable-hdpi/pointer_horizontal_double_arrow.png diff --git a/core/res/res/drawable-hdpi/pointer_top_left_diagonal_double_arrow.png b/core/res/res/drawable-hdpi/pointer_top_left_diagonal_double_arrow.png Binary files differindex ab52bffd9de5..58bb0d4cb2c9 100644 --- a/core/res/res/drawable-hdpi/pointer_top_left_diagonal_double_arrow.png +++ b/core/res/res/drawable-hdpi/pointer_top_left_diagonal_double_arrow.png diff --git a/core/res/res/drawable-hdpi/pointer_top_right_diagonal_double_arrow.png b/core/res/res/drawable-hdpi/pointer_top_right_diagonal_double_arrow.png Binary files differindex 1250d35df469..1981d41e2c34 100644 --- a/core/res/res/drawable-hdpi/pointer_top_right_diagonal_double_arrow.png +++ b/core/res/res/drawable-hdpi/pointer_top_right_diagonal_double_arrow.png diff --git a/core/res/res/drawable-hdpi/pointer_vertical_double_arrow.png b/core/res/res/drawable-hdpi/pointer_vertical_double_arrow.png Binary files differindex 6730c7b4a365..d4ba79ad32b2 100644 --- a/core/res/res/drawable-hdpi/pointer_vertical_double_arrow.png +++ b/core/res/res/drawable-hdpi/pointer_vertical_double_arrow.png diff --git a/core/res/res/drawable-mdpi/pointer_all_scroll.png b/core/res/res/drawable-mdpi/pointer_all_scroll.png Binary files differindex 3db456e885d2..1b81d0aaa1ab 100644 --- a/core/res/res/drawable-mdpi/pointer_all_scroll.png +++ b/core/res/res/drawable-mdpi/pointer_all_scroll.png diff --git a/core/res/res/drawable-mdpi/pointer_all_scroll_large.png b/core/res/res/drawable-mdpi/pointer_all_scroll_large.png Binary files differindex 120e1d72d233..9e1f5c915544 100644 --- a/core/res/res/drawable-mdpi/pointer_all_scroll_large.png +++ b/core/res/res/drawable-mdpi/pointer_all_scroll_large.png diff --git a/core/res/res/drawable-mdpi/pointer_horizontal_double_arrow.png b/core/res/res/drawable-mdpi/pointer_horizontal_double_arrow.png Binary files differindex 20f319ac5cc4..d1b3441d9117 100644 --- a/core/res/res/drawable-mdpi/pointer_horizontal_double_arrow.png +++ b/core/res/res/drawable-mdpi/pointer_horizontal_double_arrow.png diff --git a/core/res/res/drawable-mdpi/pointer_horizontal_double_arrow_large.png b/core/res/res/drawable-mdpi/pointer_horizontal_double_arrow_large.png Binary files differindex 33ef5c96ac8a..4e26371e7c7f 100644 --- a/core/res/res/drawable-mdpi/pointer_horizontal_double_arrow_large.png +++ b/core/res/res/drawable-mdpi/pointer_horizontal_double_arrow_large.png diff --git a/core/res/res/drawable-mdpi/pointer_top_left_diagonal_double_arrow.png b/core/res/res/drawable-mdpi/pointer_top_left_diagonal_double_arrow.png Binary files differindex fe7d49602aa5..34c0c6a7a804 100644 --- a/core/res/res/drawable-mdpi/pointer_top_left_diagonal_double_arrow.png +++ b/core/res/res/drawable-mdpi/pointer_top_left_diagonal_double_arrow.png diff --git a/core/res/res/drawable-mdpi/pointer_top_left_diagonal_double_arrow_large.png b/core/res/res/drawable-mdpi/pointer_top_left_diagonal_double_arrow_large.png Binary files differindex 7b2e20c9d19c..87ec1847d524 100644 --- a/core/res/res/drawable-mdpi/pointer_top_left_diagonal_double_arrow_large.png +++ b/core/res/res/drawable-mdpi/pointer_top_left_diagonal_double_arrow_large.png diff --git a/core/res/res/drawable-mdpi/pointer_top_right_diagonal_double_arrow.png b/core/res/res/drawable-mdpi/pointer_top_right_diagonal_double_arrow.png Binary files differindex 95a662017927..40b9c7e24cde 100644 --- a/core/res/res/drawable-mdpi/pointer_top_right_diagonal_double_arrow.png +++ b/core/res/res/drawable-mdpi/pointer_top_right_diagonal_double_arrow.png diff --git a/core/res/res/drawable-mdpi/pointer_top_right_diagonal_double_arrow_large.png b/core/res/res/drawable-mdpi/pointer_top_right_diagonal_double_arrow_large.png Binary files differindex 2e2904b6562d..6a85b493499c 100644 --- a/core/res/res/drawable-mdpi/pointer_top_right_diagonal_double_arrow_large.png +++ b/core/res/res/drawable-mdpi/pointer_top_right_diagonal_double_arrow_large.png diff --git a/core/res/res/drawable-mdpi/pointer_vertical_double_arrow.png b/core/res/res/drawable-mdpi/pointer_vertical_double_arrow.png Binary files differindex ae6bfed37812..9bd89bf3013b 100644 --- a/core/res/res/drawable-mdpi/pointer_vertical_double_arrow.png +++ b/core/res/res/drawable-mdpi/pointer_vertical_double_arrow.png diff --git a/core/res/res/drawable-mdpi/pointer_vertical_double_arrow_large.png b/core/res/res/drawable-mdpi/pointer_vertical_double_arrow_large.png Binary files differindex 3beb1d1e9c8c..5a69bbc4713b 100644 --- a/core/res/res/drawable-mdpi/pointer_vertical_double_arrow_large.png +++ b/core/res/res/drawable-mdpi/pointer_vertical_double_arrow_large.png diff --git a/core/res/res/drawable-xhdpi/pointer_all_scroll.png b/core/res/res/drawable-xhdpi/pointer_all_scroll.png Binary files differindex e9d05d5079be..85aa0229dc9b 100644 --- a/core/res/res/drawable-xhdpi/pointer_all_scroll.png +++ b/core/res/res/drawable-xhdpi/pointer_all_scroll.png diff --git a/core/res/res/drawable-xhdpi/pointer_all_scroll_large.png b/core/res/res/drawable-xhdpi/pointer_all_scroll_large.png Binary files differindex 1fd54fb3cc9b..74483394ab71 100644 --- a/core/res/res/drawable-xhdpi/pointer_all_scroll_large.png +++ b/core/res/res/drawable-xhdpi/pointer_all_scroll_large.png diff --git a/core/res/res/drawable-xhdpi/pointer_horizontal_double_arrow.png b/core/res/res/drawable-xhdpi/pointer_horizontal_double_arrow.png Binary files differindex caf2a97bb7be..dd37f926edd6 100644 --- a/core/res/res/drawable-xhdpi/pointer_horizontal_double_arrow.png +++ b/core/res/res/drawable-xhdpi/pointer_horizontal_double_arrow.png diff --git a/core/res/res/drawable-xhdpi/pointer_horizontal_double_arrow_large.png b/core/res/res/drawable-xhdpi/pointer_horizontal_double_arrow_large.png Binary files differindex 2f22640f99e6..9e031e85fadc 100644 --- a/core/res/res/drawable-xhdpi/pointer_horizontal_double_arrow_large.png +++ b/core/res/res/drawable-xhdpi/pointer_horizontal_double_arrow_large.png diff --git a/core/res/res/drawable-xhdpi/pointer_top_left_diagonal_double_arrow.png b/core/res/res/drawable-xhdpi/pointer_top_left_diagonal_double_arrow.png Binary files differindex a36deb3f4995..150d80d91a40 100644 --- a/core/res/res/drawable-xhdpi/pointer_top_left_diagonal_double_arrow.png +++ b/core/res/res/drawable-xhdpi/pointer_top_left_diagonal_double_arrow.png diff --git a/core/res/res/drawable-xhdpi/pointer_top_left_diagonal_double_arrow_large.png b/core/res/res/drawable-xhdpi/pointer_top_left_diagonal_double_arrow_large.png Binary files differindex 6870e23ae817..bae907aca601 100644 --- a/core/res/res/drawable-xhdpi/pointer_top_left_diagonal_double_arrow_large.png +++ b/core/res/res/drawable-xhdpi/pointer_top_left_diagonal_double_arrow_large.png diff --git a/core/res/res/drawable-xhdpi/pointer_top_right_diagonal_double_arrow.png b/core/res/res/drawable-xhdpi/pointer_top_right_diagonal_double_arrow.png Binary files differindex c8d6d1f14a8a..3b23143cd44a 100644 --- a/core/res/res/drawable-xhdpi/pointer_top_right_diagonal_double_arrow.png +++ b/core/res/res/drawable-xhdpi/pointer_top_right_diagonal_double_arrow.png diff --git a/core/res/res/drawable-xhdpi/pointer_top_right_diagonal_double_arrow_large.png b/core/res/res/drawable-xhdpi/pointer_top_right_diagonal_double_arrow_large.png Binary files differindex 5bfb7712f59d..a90b28628a6b 100644 --- a/core/res/res/drawable-xhdpi/pointer_top_right_diagonal_double_arrow_large.png +++ b/core/res/res/drawable-xhdpi/pointer_top_right_diagonal_double_arrow_large.png diff --git a/core/res/res/drawable-xhdpi/pointer_vertical_double_arrow.png b/core/res/res/drawable-xhdpi/pointer_vertical_double_arrow.png Binary files differindex 720df913f9dc..3e7f850fbe70 100644 --- a/core/res/res/drawable-xhdpi/pointer_vertical_double_arrow.png +++ b/core/res/res/drawable-xhdpi/pointer_vertical_double_arrow.png diff --git a/core/res/res/drawable-xhdpi/pointer_vertical_double_arrow_large.png b/core/res/res/drawable-xhdpi/pointer_vertical_double_arrow_large.png Binary files differindex 82b30d1fedc2..090e3cac9cac 100644 --- a/core/res/res/drawable-xhdpi/pointer_vertical_double_arrow_large.png +++ b/core/res/res/drawable-xhdpi/pointer_vertical_double_arrow_large.png diff --git a/core/res/res/drawable-xxhdpi/pointer_all_scroll.png b/core/res/res/drawable-xxhdpi/pointer_all_scroll.png Binary files differindex 808143aeff08..92aae724fdb3 100644 --- a/core/res/res/drawable-xxhdpi/pointer_all_scroll.png +++ b/core/res/res/drawable-xxhdpi/pointer_all_scroll.png diff --git a/core/res/res/drawable-xxhdpi/pointer_horizontal_double_arrow.png b/core/res/res/drawable-xxhdpi/pointer_horizontal_double_arrow.png Binary files differindex 677ccadbb26a..b1e2509a1e23 100644 --- a/core/res/res/drawable-xxhdpi/pointer_horizontal_double_arrow.png +++ b/core/res/res/drawable-xxhdpi/pointer_horizontal_double_arrow.png diff --git a/core/res/res/drawable-xxhdpi/pointer_top_left_diagonal_double_arrow.png b/core/res/res/drawable-xxhdpi/pointer_top_left_diagonal_double_arrow.png Binary files differindex e01aa649780c..2d1217c58684 100644 --- a/core/res/res/drawable-xxhdpi/pointer_top_left_diagonal_double_arrow.png +++ b/core/res/res/drawable-xxhdpi/pointer_top_left_diagonal_double_arrow.png diff --git a/core/res/res/drawable-xxhdpi/pointer_top_right_diagonal_double_arrow.png b/core/res/res/drawable-xxhdpi/pointer_top_right_diagonal_double_arrow.png Binary files differindex e947e0ea6f14..a99fb24cb849 100644 --- a/core/res/res/drawable-xxhdpi/pointer_top_right_diagonal_double_arrow.png +++ b/core/res/res/drawable-xxhdpi/pointer_top_right_diagonal_double_arrow.png diff --git a/core/res/res/drawable-xxhdpi/pointer_vertical_double_arrow.png b/core/res/res/drawable-xxhdpi/pointer_vertical_double_arrow.png Binary files differindex c867247c08f4..1f065fa8f632 100644 --- a/core/res/res/drawable-xxhdpi/pointer_vertical_double_arrow.png +++ b/core/res/res/drawable-xxhdpi/pointer_vertical_double_arrow.png diff --git a/core/res/res/layout-television/user_switching_dialog.xml b/core/res/res/layout-television/user_switching_dialog.xml deleted file mode 100644 index 72150e32c0a0..000000000000 --- a/core/res/res/layout-television/user_switching_dialog.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2015 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<TextView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/message" - style="?attr/textAppearanceListItem" - android:layout_width="match_parent" - android:background="@color/background_leanback_dark" - android:textColor="@color/primary_text_leanback_dark" - android:layout_height="match_parent" - android:gravity="center" - android:paddingStart="?attr/dialogPreferredPadding" - android:paddingEnd="?attr/dialogPreferredPadding" - android:paddingTop="24dp" - android:paddingBottom="24dp" /> diff --git a/core/res/res/values-mcc310/config.xml b/core/res/res/values-mcc310/config.xml index df398f9aab32..76abceeb74b1 100644 --- a/core/res/res/values-mcc310/config.xml +++ b/core/res/res/values-mcc310/config.xml @@ -22,4 +22,7 @@ <!-- Whether safe headphone volume is enabled or not (country specific). --> <bool name="config_safe_media_volume_enabled">false</bool> + <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). --> + <bool name="config_safe_sound_dosage_mcc_enabled">false</bool> + </resources> diff --git a/core/res/res/values-mcc311/config.xml b/core/res/res/values-mcc311/config.xml index df398f9aab32..6e0b678f94d9 100644 --- a/core/res/res/values-mcc311/config.xml +++ b/core/res/res/values-mcc311/config.xml @@ -22,4 +22,7 @@ <!-- Whether safe headphone volume is enabled or not (country specific). --> <bool name="config_safe_media_volume_enabled">false</bool> + <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). --> + <bool name="config_safe_sound_dosage_enabled">false</bool> + </resources> diff --git a/core/res/res/values-mcc312/config.xml b/core/res/res/values-mcc312/config.xml index df398f9aab32..6e0b678f94d9 100644 --- a/core/res/res/values-mcc312/config.xml +++ b/core/res/res/values-mcc312/config.xml @@ -22,4 +22,7 @@ <!-- Whether safe headphone volume is enabled or not (country specific). --> <bool name="config_safe_media_volume_enabled">false</bool> + <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). --> + <bool name="config_safe_sound_dosage_enabled">false</bool> + </resources> diff --git a/core/res/res/values-mcc313/config.xml b/core/res/res/values-mcc313/config.xml index df398f9aab32..6e0b678f94d9 100644 --- a/core/res/res/values-mcc313/config.xml +++ b/core/res/res/values-mcc313/config.xml @@ -22,4 +22,7 @@ <!-- Whether safe headphone volume is enabled or not (country specific). --> <bool name="config_safe_media_volume_enabled">false</bool> + <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). --> + <bool name="config_safe_sound_dosage_enabled">false</bool> + </resources> diff --git a/core/res/res/values-mcc314/config.xml b/core/res/res/values-mcc314/config.xml index df398f9aab32..6e0b678f94d9 100644 --- a/core/res/res/values-mcc314/config.xml +++ b/core/res/res/values-mcc314/config.xml @@ -22,4 +22,7 @@ <!-- Whether safe headphone volume is enabled or not (country specific). --> <bool name="config_safe_media_volume_enabled">false</bool> + <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). --> + <bool name="config_safe_sound_dosage_enabled">false</bool> + </resources> diff --git a/core/res/res/values-mcc315/config.xml b/core/res/res/values-mcc315/config.xml index df398f9aab32..6e0b678f94d9 100644 --- a/core/res/res/values-mcc315/config.xml +++ b/core/res/res/values-mcc315/config.xml @@ -22,4 +22,7 @@ <!-- Whether safe headphone volume is enabled or not (country specific). --> <bool name="config_safe_media_volume_enabled">false</bool> + <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). --> + <bool name="config_safe_sound_dosage_enabled">false</bool> + </resources> diff --git a/core/res/res/values-mcc316/config.xml b/core/res/res/values-mcc316/config.xml index df398f9aab32..6e0b678f94d9 100644 --- a/core/res/res/values-mcc316/config.xml +++ b/core/res/res/values-mcc316/config.xml @@ -22,4 +22,7 @@ <!-- Whether safe headphone volume is enabled or not (country specific). --> <bool name="config_safe_media_volume_enabled">false</bool> + <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). --> + <bool name="config_safe_sound_dosage_enabled">false</bool> + </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 12dad7eb1faa..7bc3ab80da99 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2105,9 +2105,6 @@ <!-- The default volume for the ring stream --> <integer name="config_audio_ring_vol_default">5</integer> - <!-- Enable sound dose computation and warnings --> - <bool name="config_audio_csd_enabled_default">true</bool> - <!-- The default value for whether head tracking for spatial audio is enabled for a newly connected audio device --> <bool name="config_spatial_audio_head_tracking_enabled_default">false</bool> @@ -2939,6 +2936,9 @@ <!-- Whether safe headphone volume is enabled or not (country specific). --> <bool name="config_safe_media_volume_enabled">true</bool> + <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). --> + <bool name="config_safe_sound_dosage_enabled">true</bool> + <!-- Whether safe headphone volume warning dialog is disabled on Vol+ (operator specific). --> <bool name="config_safe_media_disable_on_volume_up">true</bool> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index bc0af12e9569..ad864b13b16d 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -96,12 +96,12 @@ <!-- Width of the navigation bar when it is placed vertically on the screen in car mode --> <dimen name="navigation_bar_width_car_mode">96dp</dimen> <!-- Height of notification icons in the status bar --> - <dimen name="status_bar_icon_size">22dip</dimen> + <dimen name="status_bar_icon_size">18sp</dimen> <!-- Desired size of system icons in status bar. --> - <dimen name="status_bar_system_icon_size">15dp</dimen> + <dimen name="status_bar_system_icon_size">15sp</dimen> <!-- Intrinsic size of most system icons in status bar. This is the default value that is used if a Drawable reports an intrinsic size of 0. --> - <dimen name="status_bar_system_icon_intrinsic_size">17dp</dimen> + <dimen name="status_bar_system_icon_intrinsic_size">17sp</dimen> <!-- Size of the giant number (unread count) in the notifications --> <dimen name="status_bar_content_number_size">48sp</dimen> <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. --> @@ -330,7 +330,7 @@ <dimen name="notification_icon_circle_start">16dp</dimen> <!-- size (width and height) of the icon in the notification header --> - <dimen name="notification_header_icon_size_ambient">18dp</dimen> + <dimen name="notification_header_icon_size_ambient">18sp</dimen> <!-- The margin before the start of the app name in the header. --> <dimen name="notification_header_app_name_margin_start">3dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index bb10f7ae7c93..e25425d6cb18 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -283,7 +283,6 @@ <java-symbol type="attr" name="autofillSaveCustomSubtitleMaxHeight"/> <java-symbol type="bool" name="action_bar_embed_tabs" /> <java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" /> - <java-symbol type="bool" name="config_audio_csd_enabled_default" /> <java-symbol type="integer" name="config_audio_notif_vol_default" /> <java-symbol type="integer" name="config_audio_notif_vol_steps" /> <java-symbol type="integer" name="config_audio_ring_vol_default" /> @@ -349,6 +348,7 @@ <java-symbol type="bool" name="config_useDevInputEventForAudioJack" /> <java-symbol type="bool" name="config_safe_media_volume_enabled" /> <java-symbol type="bool" name="config_safe_media_disable_on_volume_up" /> + <java-symbol type="bool" name="config_safe_sound_dosage_enabled" /> <java-symbol type="bool" name="config_camera_sound_forced" /> <java-symbol type="bool" name="config_dontPreferApn" /> <java-symbol type="bool" name="config_restartRadioAfterProvisioning" /> diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java index cce1b2bc3ece..6c701920bd51 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java @@ -80,7 +80,7 @@ final class AidlTestUtils { /* vendorIds= */ null); } - static android.hardware.broadcastradio.ProgramSelector makeHalFmSelector(int freq) { + static android.hardware.broadcastradio.ProgramSelector makeHalFmSelector(long freq) { ProgramIdentifier halId = makeHalIdentifier(IdentifierType.AMFM_FREQUENCY_KHZ, freq); return makeHalSelector(halId, /* secondaryIds= */ new ProgramIdentifier[0]); } diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsResultTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsResultTest.java index df3ddfd6bb71..b54c156c820e 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsResultTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsResultTest.java @@ -52,6 +52,7 @@ public final class ConversionUtilsResultTest { {Result.INVALID_STATE, RadioTuner.TUNER_RESULT_INVALID_STATE}, {Result.NOT_SUPPORTED, RadioTuner.TUNER_RESULT_NOT_SUPPORTED}, {Result.TIMEOUT, RadioTuner.TUNER_RESULT_TIMEOUT}, + {Result.CANCELED, RadioTuner.TUNER_RESULT_CANCELED}, {Result.UNKNOWN_ERROR, RadioTuner.TUNER_RESULT_UNKNOWN_ERROR} }); } diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java index aea01783ee68..2ef923d1339a 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java @@ -27,11 +27,13 @@ import android.hardware.broadcastradio.ProgramIdentifier; import android.hardware.broadcastradio.ProgramInfo; import android.hardware.broadcastradio.ProgramListChunk; import android.hardware.broadcastradio.Properties; +import android.hardware.broadcastradio.Result; import android.hardware.broadcastradio.VendorKeyValue; import android.hardware.radio.Announcement; import android.hardware.radio.ProgramList; import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; +import android.os.ServiceSpecificException; import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase; @@ -153,6 +155,16 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { } @Test + public void throwOnError_withCancelException() { + ServiceSpecificException halException = new ServiceSpecificException(Result.CANCELED); + + RuntimeException thrown = ConversionUtils.throwOnError(halException, "tune"); + + expect.withMessage("Exception thrown for canceling error").that(thrown) + .hasMessageThat().contains("tune: CANCELED"); + } + + @Test public void propertiesFromHalProperties_idsMatch() { expect.withMessage("Properties id") .that(MODULE_PROPERTIES.getId()).isEqualTo(TEST_ID); diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java index f85748c93c57..84aa86472e81 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java @@ -1168,13 +1168,29 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { doReturn(USER_ID_2).when(() -> RadioServiceUserController.getCurrentUser()); mHalTunerCallback.onCurrentProgramInfoChanged(AidlTestUtils.makeHalProgramInfo( - AidlTestUtils.makeHalFmSelector(/* freq= */ 97300), SIGNAL_QUALITY)); + AidlTestUtils.makeHalFmSelector(AM_FM_FREQUENCY_LIST[1]), SIGNAL_QUALITY)); verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) .onCurrentProgramInfoChanged(any()); } @Test + public void onTuneFailed_forTunerCallback() throws Exception { + int numSessions = 3; + openAidlClients(numSessions); + android.hardware.broadcastradio.ProgramSelector halSel = AidlTestUtils.makeHalFmSelector( + AM_FM_FREQUENCY_LIST[1]); + ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); + + mHalTunerCallback.onTuneFailed(Result.CANCELED, halSel); + + for (int index = 0; index < numSessions; index++) { + verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT) + .onTuneFailed(RadioTuner.TUNER_RESULT_CANCELED, sel); + } + } + + @Test public void onAntennaStateChange_forTunerCallback() throws Exception { int numSessions = 3; openAidlClients(numSessions); diff --git a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java index bf8c7c63590f..72969f7d8b96 100644 --- a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java +++ b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java @@ -112,6 +112,7 @@ public class BugreportManagerTest { Paths.get("/data/misc/wmtrace/layers_trace.winscope"), Paths.get("/data/misc/wmtrace/transactions_trace.winscope"), Paths.get("/data/misc/wmtrace/transition_trace.winscope"), + Paths.get("/data/misc/wmtrace/shell_transition_trace.winscope"), }; private static final Path[] UI_TRACES_GENERATED_DURING_BUGREPORT = { Paths.get("/data/misc/wmtrace/layers_trace_from_transactions.winscope"), diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java index a3eda8dfe8e0..f45db23ace76 100644 --- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java @@ -39,7 +39,6 @@ import android.view.WindowManager.LayoutParams; import android.widget.TextView; import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.FlakyTest; import androidx.test.platform.app.InstrumentationRegistry; import org.junit.Before; @@ -55,7 +54,6 @@ import org.mockito.Spy; * atest FrameworksCoreTests:ImeInsetsSourceConsumerTest */ @Presubmit -@FlakyTest(detail = "Promote once confirmed non-flaky") @RunWith(AndroidJUnit4.class) public class ImeInsetsSourceConsumerTest { diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 596f3515dda5..7c2759af156f 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -1183,6 +1183,12 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/AppTransitionController.java" }, + "-1005167552": { + "message": "Playing #%d in parallel on track #%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_TRANSITIONS", + "at": "com\/android\/server\/wm\/TransitionController.java" + }, "-1003678883": { "message": "Cleaning splash screen token=%s", "level": "VERBOSE", @@ -1447,6 +1453,12 @@ "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, + "-774908272": { + "message": "Marking #%d animation as SYNC.", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_TRANSITIONS", + "at": "com\/android\/server\/wm\/TransitionController.java" + }, "-771177730": { "message": "Removing focused app token:%s displayId=%d", "level": "VERBOSE", @@ -1495,12 +1507,6 @@ "group": "WM_DEBUG_CONFIGURATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "-741766551": { - "message": "Content Recording: Ignoring session on invalid virtual display", - "level": "VERBOSE", - "group": "WM_DEBUG_CONTENT_RECORDING", - "at": "com\/android\/server\/wm\/ContentRecordingController.java" - }, "-732715767": { "message": "Unable to retrieve window container to start recording for display %d", "level": "VERBOSE", @@ -2017,6 +2023,12 @@ "group": "WM_DEBUG_WALLPAPER", "at": "com\/android\/server\/wm\/WallpaperController.java" }, + "-266707683": { + "message": "Moving #%d from collecting to waiting.", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN", + "at": "com\/android\/server\/wm\/TransitionController.java" + }, "-262984451": { "message": "Relaunch failed %s", "level": "INFO", @@ -2089,6 +2101,12 @@ "group": "WM_DEBUG_WINDOW_MOVEMENT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-186693085": { + "message": "Starting a Recents transition which can be parallel.", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_TRANSITIONS", + "at": "com\/android\/server\/wm\/Transition.java" + }, "-182877285": { "message": "Wallpaper layer changed: assigning layers + relayout", "level": "VERBOSE", diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index 302c72ead52e..dd4b58eb83dc 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -40,6 +40,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.NinePatchDrawable; import android.media.MediaCodecInfo; import android.media.MediaCodecList; +import android.media.MediaFormat; import android.net.Uri; import android.os.Build; import android.os.Trace; @@ -914,8 +915,6 @@ public final class ImageDecoder implements AutoCloseable { case "image/jpeg": case "image/webp": case "image/gif": - case "image/heif": - case "image/heic": case "image/bmp": case "image/x-ico": case "image/vnd.wap.wbmp": @@ -930,6 +929,9 @@ public final class ImageDecoder implements AutoCloseable { case "image/x-pentax-pef": case "image/x-samsung-srw": return true; + case "image/heif": + case "image/heic": + return isHevcDecoderSupported(); case "image/avif": return isP010SupportedForAV1(); default: @@ -2067,6 +2069,28 @@ public final class ImageDecoder implements AutoCloseable { return decodeBitmapImpl(src, null); } + private static boolean sIsHevcDecoderSupported = false; + private static boolean sIsHevcDecoderSupportedInitialized = false; + private static final Object sIsHevcDecoderSupportedLock = new Object(); + + /* + * Check if HEVC decoder is supported by the device. + */ + @SuppressWarnings("AndroidFrameworkCompatChange") + private static boolean isHevcDecoderSupported() { + synchronized (sIsHevcDecoderSupportedLock) { + if (sIsHevcDecoderSupportedInitialized) { + return sIsHevcDecoderSupported; + } + MediaFormat format = new MediaFormat(); + format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_HEVC); + MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS); + sIsHevcDecoderSupported = mcl.findDecoderForFormat(format) != null; + sIsHevcDecoderSupportedInitialized = true; + return sIsHevcDecoderSupported; + } + } + private static boolean sIsP010SupportedForAV1 = false; private static boolean sIsP010SupportedForHEVC = false; private static boolean sIsP010SupportedFlagsInitialized = false; @@ -2105,20 +2129,20 @@ public final class ImageDecoder implements AutoCloseable { * Checks if the device supports decoding 10-bit for the given mime type. */ private static void checkP010SupportforAV1HEVC() { - MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS); + MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); for (MediaCodecInfo mediaCodecInfo : codecList.getCodecInfos()) { if (mediaCodecInfo.isEncoder()) { continue; } for (String mediaType : mediaCodecInfo.getSupportedTypes()) { - if (mediaType.equalsIgnoreCase("video/av01") - || mediaType.equalsIgnoreCase("video/hevc")) { + if (mediaType.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AV1) + || mediaType.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) { MediaCodecInfo.CodecCapabilities codecCapabilities = mediaCodecInfo.getCapabilitiesForType(mediaType); for (int i = 0; i < codecCapabilities.colorFormats.length; ++i) { if (codecCapabilities.colorFormats[i] == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010) { - if (mediaType.equalsIgnoreCase("video/av01")) { + if (mediaType.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AV1)) { sIsP010SupportedForAV1 = true; } else { sIsP010SupportedForHEVC = true; diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 54978bd4496d..6a79bc1f46c2 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -125,6 +125,34 @@ prebuilt_etc { // End ProtoLog +gensrcs { + name: "wm-shell-protos", + + tools: [ + "aprotoc", + "protoc-gen-javastream", + "soong_zip", + ], + + tool_files: [ + ":libprotobuf-internal-protos", + ], + + cmd: "mkdir -p $(genDir)/$(in) " + + "&& $(location aprotoc) " + + " --plugin=$(location protoc-gen-javastream) " + + " --javastream_out=$(genDir)/$(in) " + + " -Iexternal/protobuf/src " + + " -I . " + + " $(in) " + + "&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)", + + srcs: [ + "proto/**/*.proto", + ], + output_extension: "srcjar", +} + java_library { name: "WindowManager-Shell-proto", @@ -142,6 +170,7 @@ android_library { // TODO(b/168581922) protologtool do not support kotlin(*.kt) ":wm_shell-sources-kt", ":wm_shell-aidls", + ":wm-shell-protos", ], resource_dirs: [ "res", diff --git a/libs/WindowManager/Shell/proto/wm_shell_transition_trace.proto b/libs/WindowManager/Shell/proto/wm_shell_transition_trace.proto new file mode 100644 index 000000000000..6e0110193a05 --- /dev/null +++ b/libs/WindowManager/Shell/proto/wm_shell_transition_trace.proto @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package com.android.wm.shell; + +option java_multiple_files = true; + +/* Represents a file full of transition entries. + Encoded, it should start with 0x09 0x57 0x4D 0x53 0x54 0x52 0x41 0x43 0x45 (.WMSTRACE), such + that it can be easily identified. */ +message WmShellTransitionTraceProto { + /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L + (this is needed because enums have to be 32 bits and there's no nice way to put 64bit + constants into .proto files. */ + enum MagicNumber { + INVALID = 0; + MAGIC_NUMBER_L = 0x54534D57; /* WMST (little-endian ASCII) */ + MAGIC_NUMBER_H = 0x45434152; /* RACE (little-endian ASCII) */ + } + + // Must be the first field, set to value in MagicNumber + required fixed64 magic_number = 1; + repeated Transition transitions = 2; + repeated HandlerMapping handlerMappings = 3; +} + +message Transition { + required int32 id = 1; + optional int64 dispatch_time_ns = 2; + optional int32 handler = 3; + optional int64 merge_time_ns = 4; + optional int64 merge_request_time_ns = 5; + optional int32 merged_into = 6; + optional int64 abort_time_ns = 7; +} + +message HandlerMapping { + required int32 id = 1; + required string name = 2; +} diff --git a/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml index 5aff4159e135..7f1aac3551b6 100644 --- a/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml +++ b/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml @@ -73,11 +73,13 @@ android:textAlignment="center"/> <LinearLayout + android:id="@+id/letterbox_restart_dialog_checkbox_container" android:layout_width="match_parent" android:layout_height="wrap_content" + android:paddingVertical="14dp" android:orientation="horizontal" android:layout_gravity="center_vertical" - android:layout_marginVertical="32dp"> + android:layout_marginVertical="18dp"> <CheckBox android:id="@+id/letterbox_restart_dialog_checkbox" diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index 76eb0945d990..a3916b71592b 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -125,4 +125,7 @@ <!-- Whether the additional education about reachability is enabled --> <bool name="config_letterboxIsReachabilityEducationEnabled">false</bool> + + <!-- Whether DragAndDrop capability is enabled --> + <bool name="config_enableShellDragDrop">true</bool> </resources> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index d3f395846894..24fd86b45df4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -297,7 +297,7 @@ public class Bubble implements BubbleViewProvider { public BubbleInfo asBubbleBarBubble() { return new BubbleInfo(getKey(), getFlags(), - getShortcutInfo().getId(), + getShortcutId(), getIcon(), getUser().getIdentifier(), getPackageName()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 21f02b10035b..3eb9fa2eef6b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -260,7 +260,7 @@ public class BubbleController implements ConfigurationChangeListener, /** One handed mode controller to register transition listener. */ private Optional<OneHandedController> mOneHandedOptional; /** Drag and drop controller to register listener for onDragStarted. */ - private DragAndDropController mDragAndDropController; + private Optional<DragAndDropController> mDragAndDropController; /** Used to send bubble events to launcher. */ private Bubbles.BubbleStateListener mBubbleStateListener; @@ -286,7 +286,7 @@ public class BubbleController implements ConfigurationChangeListener, BubblePositioner positioner, DisplayController displayController, Optional<OneHandedController> oneHandedOptional, - DragAndDropController dragAndDropController, + Optional<DragAndDropController> dragAndDropController, @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler, @ShellBackgroundThread ShellExecutor bgExecutor, @@ -469,7 +469,7 @@ public class BubbleController implements ConfigurationChangeListener, }); mOneHandedOptional.ifPresent(this::registerOneHandedState); - mDragAndDropController.addListener(this::collapseStack); + mDragAndDropController.ifPresent(controller -> controller.addListener(this::collapseStack)); // Clear out any persisted bubbles on disk that no longer have a valid user. List<UserInfo> users = mUserManager.getAliveUsers(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java index 0f9260c9deaa..9abf0f678179 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java @@ -38,7 +38,7 @@ public interface TaskStackListenerCallback { default void onTaskStackChanged() { } - default void onTaskProfileLocked(RunningTaskInfo taskInfo) { } + default void onTaskProfileLocked(RunningTaskInfo taskInfo, int userId) { } default void onTaskDisplayChanged(int taskId, int newDisplayId) { } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java index e2106e478bb3..d8859bac471f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java @@ -150,8 +150,8 @@ public class TaskStackListenerImpl extends TaskStackListener implements Handler. } @Override - public void onTaskProfileLocked(ActivityManager.RunningTaskInfo taskInfo) { - mMainHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, taskInfo).sendToTarget(); + public void onTaskProfileLocked(ActivityManager.RunningTaskInfo taskInfo, int userId) { + mMainHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, userId, 0, taskInfo).sendToTarget(); } @Override @@ -348,8 +348,9 @@ public class TaskStackListenerImpl extends TaskStackListener implements Handler. case ON_TASK_PROFILE_LOCKED: { final ActivityManager.RunningTaskInfo info = (ActivityManager.RunningTaskInfo) msg.obj; + final int userId = msg.arg1; for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onTaskProfileLocked(info); + mTaskStackListeners.get(i).onTaskProfileLocked(info, userId); } break; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java index b0dea7231a1e..d27d05b207a6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java @@ -35,6 +35,7 @@ public class BubbleInfo implements Parcelable { private String mKey; // Same key as the Notification private int mFlags; // Flags from BubbleMetadata + @Nullable private String mShortcutId; private int mUserId; private String mPackageName; @@ -46,7 +47,7 @@ public class BubbleInfo implements Parcelable { @Nullable private Icon mIcon; - public BubbleInfo(String key, int flags, String shortcutId, @Nullable Icon icon, + public BubbleInfo(String key, int flags, @Nullable String shortcutId, @Nullable Icon icon, int userId, String packageName) { mKey = key; mFlags = flags; @@ -69,10 +70,12 @@ public class BubbleInfo implements Parcelable { return mKey; } + @Nullable public String getShortcutId() { return mShortcutId; } + @Nullable public Icon getIcon() { return mIcon; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 9eba5ecd36f1..e7dede757578 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -738,10 +738,6 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange getRefBounds2(mTempRect); t.setPosition(leash2, mTempRect.left, mTempRect.top) .setWindowCrop(leash2, mTempRect.width(), mTempRect.height()); - // Make right or bottom side surface always higher than left or top side to avoid weird - // animation when dismiss split. e.g. App surface fling above on decor surface. - t.setLayer(leash1, 1); - t.setLayer(leash2, 2); if (mImePositionProcessor.adjustSurfaceLayoutForIme( t, dividerLeash, leash1, leash2, dimLayer1, dimLayer2)) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogLayout.java index c53e6389331a..05fd5f1172e1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogLayout.java @@ -95,6 +95,9 @@ public class RestartDialogLayout extends ConstraintLayout implements DialogConta @Override protected void onFinishInflate() { super.onFinishInflate(); + final View checkboxContainer = findViewById( + R.id.letterbox_restart_dialog_checkbox_container); + final CheckBox checkbox = findViewById(R.id.letterbox_restart_dialog_checkbox); mDialogContainer = findViewById(R.id.letterbox_restart_dialog_container); mDialogTitle = findViewById(R.id.letterbox_restart_dialog_title); mBackgroundDim = getBackground().mutate(); @@ -103,5 +106,6 @@ public class RestartDialogLayout extends ConstraintLayout implements DialogConta // We add a no-op on-click listener to the dialog container so that clicks on it won't // propagate to the listener of the layout (which represents the background dim). mDialogContainer.setOnClickListener(view -> {}); + checkboxContainer.setOnClickListener(view -> checkbox.performClick()); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java index e9957fd4f4f1..12d51f54a09c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java @@ -41,11 +41,11 @@ import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; -import java.util.Optional; - import dagger.Module; import dagger.Provides; +import java.util.Optional; + /** * Provides dependencies from {@link com.android.wm.shell}, these dependencies are only * accessible from components within the WM subcomponent (can be explicitly exposed to the @@ -81,7 +81,7 @@ public class TvWMShellModule { DisplayController displayController, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, - DragAndDropController dragAndDropController, + Optional<DragAndDropController> dragAndDropController, Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 9808c591592f..80e920fefe5f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -186,15 +186,15 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides - static DragAndDropController provideDragAndDropController(Context context, + static Optional<DragAndDropController> provideDragAndDropController(Context context, ShellInit shellInit, ShellController shellController, DisplayController displayController, UiEventLogger uiEventLogger, IconProvider iconProvider, @ShellMainThread ShellExecutor mainExecutor) { - return new DragAndDropController(context, shellInit, shellController, displayController, - uiEventLogger, iconProvider, mainExecutor); + return Optional.ofNullable(DragAndDropController.create(context, shellInit, shellController, + displayController, uiEventLogger, iconProvider, mainExecutor)); } @WMSingleton @@ -544,13 +544,14 @@ public abstract class WMShellBaseModule { DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler, - @ShellAnimationThread ShellExecutor animExecutor) { + @ShellAnimationThread ShellExecutor animExecutor, + ShellCommandHandler shellCommandHandler) { if (!context.getResources().getBoolean(R.bool.config_registerShellTransitionsOnInit)) { // TODO(b/238217847): Force override shell init if registration is disabled shellInit = new ShellInit(mainExecutor); } return new Transitions(context, shellInit, shellController, organizer, pool, - displayController, mainExecutor, mainHandler, animExecutor); + displayController, mainExecutor, mainHandler, animExecutor, shellCommandHandler); } @WMSingleton @@ -796,7 +797,7 @@ public abstract class WMShellBaseModule { DisplayController displayController, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, - DragAndDropController dragAndDropController, + Optional<DragAndDropController> dragAndDropControllerOptional, ShellTaskOrganizer shellTaskOrganizer, Optional<BubbleController> bubblesOptional, Optional<SplitScreenController> splitScreenOptional, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 2f0f56cfdfb0..f3130d358ec1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -171,7 +171,7 @@ public abstract class WMShellModule { BubblePositioner positioner, DisplayController displayController, @DynamicOverride Optional<OneHandedController> oneHandedOptional, - DragAndDropController dragAndDropController, + Optional<DragAndDropController> dragAndDropController, @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler, @ShellBackgroundThread ShellExecutor bgExecutor, @@ -320,7 +320,7 @@ public abstract class WMShellModule { DisplayController displayController, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, - DragAndDropController dragAndDropController, + Optional<DragAndDropController> dragAndDropController, Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java index 86ea72582a52..b9d2be280efb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java @@ -264,10 +264,10 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll /** * Show apps on desktop */ - void showDesktopApps() { + void showDesktopApps(int displayId) { // Bring apps to front, ignoring their visibility status to always ensure they are on top. WindowContainerTransaction wct = new WindowContainerTransaction(); - bringDesktopAppsToFront(wct); + bringDesktopAppsToFront(displayId, wct); if (!wct.isEmpty()) { if (Transitions.ENABLE_SHELL_TRANSITIONS) { @@ -280,12 +280,12 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll } /** Get number of tasks that are marked as visible */ - int getVisibleTaskCount() { - return mDesktopModeTaskRepository.getVisibleTaskCount(); + int getVisibleTaskCount(int displayId) { + return mDesktopModeTaskRepository.getVisibleTaskCount(displayId); } - private void bringDesktopAppsToFront(WindowContainerTransaction wct) { - final ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks(); + private void bringDesktopAppsToFront(int displayId, WindowContainerTransaction wct) { + final ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks(displayId); ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size()); final List<RunningTaskInfo> taskInfos = new ArrayList<>(); @@ -386,6 +386,7 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request) { + RunningTaskInfo triggerTask = request.getTriggerTask(); // Only do anything if we are in desktop mode and opening/moving-to-front a task/app in // freeform if (!DesktopModeStatus.isActive(mContext)) { @@ -399,16 +400,15 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll WindowManager.transitTypeToString(request.getType())); return null; } - if (request.getTriggerTask() == null - || request.getTriggerTask().getWindowingMode() != WINDOWING_MODE_FREEFORM) { + if (triggerTask == null || triggerTask.getWindowingMode() != WINDOWING_MODE_FREEFORM) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "skip shell transition request: not freeform task"); return null; } ProtoLog.d(WM_SHELL_DESKTOP_MODE, "handle shell transition request: %s", request); WindowContainerTransaction wct = new WindowContainerTransaction(); - bringDesktopAppsToFront(wct); - wct.reorder(request.getTriggerTask().token, true /* onTop */); + bringDesktopAppsToFront(triggerTask.displayId, wct); + wct.reorder(triggerTask.token, true /* onTop */); return wct; } @@ -493,16 +493,17 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll mController = null; } - public void showDesktopApps() { + @Override + public void showDesktopApps(int displayId) { executeRemoteCallWithTaskPermission(mController, "showDesktopApps", - DesktopModeController::showDesktopApps); + controller -> controller.showDesktopApps(displayId)); } @Override - public int getVisibleTaskCount() throws RemoteException { + public int getVisibleTaskCount(int displayId) throws RemoteException { int[] result = new int[1]; executeRemoteCallWithTaskPermission(mController, "getVisibleTaskCount", - controller -> result[0] = controller.getVisibleTaskCount(), + controller -> result[0] = controller.getVisibleTaskCount(displayId), true /* blocking */ ); return result[0]; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt index 12f8ea23ac8f..00cc57f0b99c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt @@ -20,6 +20,8 @@ import android.graphics.Region import android.util.ArrayMap import android.util.ArraySet import android.util.SparseArray +import androidx.core.util.forEach +import androidx.core.util.keyIterator import androidx.core.util.valueIterator import java.util.concurrent.Executor import java.util.function.Consumer @@ -29,14 +31,18 @@ import java.util.function.Consumer */ class DesktopModeTaskRepository { - /** - * Set of task ids that are marked as active in desktop mode. - * Active tasks in desktop mode are freeform tasks that are visible or have been visible after - * desktop mode was activated. - * Task gets removed from this list when it vanishes. Or when desktop mode is turned off. - */ - private val activeTasks = ArraySet<Int>() - private val visibleTasks = ArraySet<Int>() + /** Task data that is tracked per display */ + private data class DisplayData( + /** + * Set of task ids that are marked as active in desktop mode. Active tasks in desktop mode + * are freeform tasks that are visible or have been visible after desktop mode was + * activated. Task gets removed from this list when it vanishes. Or when desktop mode is + * turned off. + */ + val activeTasks: ArraySet<Int> = ArraySet(), + val visibleTasks: ArraySet<Int> = ArraySet(), + ) + // Tasks currently in freeform mode, ordered from top to bottom (top is at index 0). private val freeformTasksInZOrder = mutableListOf<Int>() private val activeTasksListeners = ArraySet<ActiveTasksListener>() @@ -47,9 +53,22 @@ class DesktopModeTaskRepository { private var desktopGestureExclusionListener: Consumer<Region>? = null private var desktopGestureExclusionExecutor: Executor? = null - /** - * Add a [ActiveTasksListener] to be notified of updates to active tasks in the repository. - */ + private val displayData = + object : SparseArray<DisplayData>() { + /** + * Get the [DisplayData] associated with this [displayId] + * + * Creates a new instance if one does not exist + */ + fun getOrCreate(displayId: Int): DisplayData { + if (!contains(displayId)) { + put(displayId, DisplayData()) + } + return get(displayId) + } + } + + /** Add a [ActiveTasksListener] to be notified of updates to active tasks in the repository. */ fun addActiveTaskListener(activeTasksListener: ActiveTasksListener) { activeTasksListeners.add(activeTasksListener) } @@ -57,10 +76,17 @@ class DesktopModeTaskRepository { /** * Add a [VisibleTasksListener] to be notified when freeform tasks are visible or not. */ - fun addVisibleTasksListener(visibleTasksListener: VisibleTasksListener, executor: Executor) { - visibleTasksListeners.put(visibleTasksListener, executor) - executor.execute( - Runnable { visibleTasksListener.onVisibilityChanged(visibleTasks.size > 0) }) + fun addVisibleTasksListener( + visibleTasksListener: VisibleTasksListener, + executor: Executor + ) { + visibleTasksListeners[visibleTasksListener] = executor + displayData.keyIterator().forEach { displayId -> + val visibleTasks = getVisibleTaskCount(displayId) + executor.execute { + visibleTasksListener.onVisibilityChanged(displayId, visibleTasks > 0) + } + } } /** @@ -100,14 +126,21 @@ class DesktopModeTaskRepository { } /** - * Mark a task with given [taskId] as active. + * Mark a task with given [taskId] as active on given [displayId] * - * @return `true` if the task was not active + * @return `true` if the task was not active on given [displayId] */ - fun addActiveTask(taskId: Int): Boolean { - val added = activeTasks.add(taskId) + fun addActiveTask(displayId: Int, taskId: Int): Boolean { + // Check if task is active on another display, if so, remove it + displayData.forEach { id, data -> + if (id != displayId && data.activeTasks.remove(taskId)) { + activeTasksListeners.onEach { it.onActiveTasksChanged(id) } + } + } + + val added = displayData.getOrCreate(displayId).activeTasks.add(taskId) if (added) { - activeTasksListeners.onEach { it.onActiveTasksChanged() } + activeTasksListeners.onEach { it.onActiveTasksChanged(displayId) } } return added } @@ -118,65 +151,93 @@ class DesktopModeTaskRepository { * @return `true` if the task was active */ fun removeActiveTask(taskId: Int): Boolean { - val removed = activeTasks.remove(taskId) - if (removed) { - activeTasksListeners.onEach { it.onActiveTasksChanged() } + var result = false + displayData.forEach { displayId, data -> + if (data.activeTasks.remove(taskId)) { + activeTasksListeners.onEach { it.onActiveTasksChanged(displayId) } + result = true + } } - return removed + return result } /** * Check if a task with the given [taskId] was marked as an active task */ fun isActiveTask(taskId: Int): Boolean { - return activeTasks.contains(taskId) + return displayData.valueIterator().asSequence().any { data -> + data.activeTasks.contains(taskId) + } } /** * Whether a task is visible. */ fun isVisibleTask(taskId: Int): Boolean { - return visibleTasks.contains(taskId) + return displayData.valueIterator().asSequence().any { data -> + data.visibleTasks.contains(taskId) + } } /** - * Get a set of the active tasks + * Get a set of the active tasks for given [displayId] */ - fun getActiveTasks(): ArraySet<Int> { - return ArraySet(activeTasks) + fun getActiveTasks(displayId: Int): ArraySet<Int> { + return ArraySet(displayData[displayId]?.activeTasks) } /** * Get a list of freeform tasks, ordered from top-bottom (top at index 0). */ + // TODO(b/278084491): pass in display id fun getFreeformTasksInZOrder(): List<Int> { return freeformTasksInZOrder } /** * Updates whether a freeform task with this id is visible or not and notifies listeners. + * + * If the task was visible on a different display with a different displayId, it is removed from + * the set of visible tasks on that display. Listeners will be notified. */ - fun updateVisibleFreeformTasks(taskId: Int, visible: Boolean) { - val prevCount: Int = visibleTasks.size + fun updateVisibleFreeformTasks(displayId: Int, taskId: Int, visible: Boolean) { if (visible) { - visibleTasks.add(taskId) + // Task is visible. Check if we need to remove it from any other display. + val otherDisplays = displayData.keyIterator().asSequence().filter { it != displayId } + for (otherDisplayId in otherDisplays) { + if (displayData[otherDisplayId].visibleTasks.remove(taskId)) { + // Task removed from other display, check if we should notify listeners + if (displayData[otherDisplayId].visibleTasks.isEmpty()) { + notifyVisibleTaskListeners(otherDisplayId, hasVisibleFreeformTasks = false) + } + } + } + } + + val prevCount = getVisibleTaskCount(displayId) + if (visible) { + displayData.getOrCreate(displayId).visibleTasks.add(taskId) } else { - visibleTasks.remove(taskId) + displayData[displayId]?.visibleTasks?.remove(taskId) } - if (prevCount == 0 && visibleTasks.size == 1 || - prevCount > 0 && visibleTasks.size == 0) { - for ((listener, executor) in visibleTasksListeners) { - executor.execute( - Runnable { listener.onVisibilityChanged(visibleTasks.size > 0) }) - } + val newCount = getVisibleTaskCount(displayId) + // Check if count changed and if there was no tasks or this is the first task + if (prevCount != newCount && (prevCount == 0 || newCount == 0)) { + notifyVisibleTaskListeners(displayId, newCount > 0) + } + } + + private fun notifyVisibleTaskListeners(displayId: Int, hasVisibleFreeformTasks: Boolean) { + visibleTasksListeners.forEach { (listener, executor) -> + executor.execute { listener.onVisibilityChanged(displayId, hasVisibleFreeformTasks) } } } /** - * Get number of tasks that are marked as visible + * Get number of tasks that are marked as visible on given [displayId] */ - fun getVisibleTaskCount(): Int { - return visibleTasks.size + fun getVisibleTaskCount(displayId: Int): Int { + return displayData[displayId]?.visibleTasks?.size ?: 0 } /** @@ -226,7 +287,7 @@ class DesktopModeTaskRepository { * Called when the active tasks change in desktop mode. */ @JvmDefault - fun onActiveTasksChanged() {} + fun onActiveTasksChanged(displayId: Int) {} } /** @@ -237,6 +298,6 @@ class DesktopModeTaskRepository { * Called when the desktop starts or stops showing freeform tasks. */ @JvmDefault - fun onVisibilityChanged(hasVisibleFreeformTasks: Boolean) {} + fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {} } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 0d5602365578..c814fe575e81 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -97,10 +97,11 @@ class DesktopTasksController( } /** Show all tasks, that are part of the desktop, on top of launcher */ - fun showDesktopApps() { + fun showDesktopApps(displayId: Int) { ProtoLog.v(WM_SHELL_DESKTOP_MODE, "showDesktopApps") val wct = WindowContainerTransaction() - bringDesktopAppsToFront(wct) + // TODO(b/278084491): pass in display id + bringDesktopAppsToFront(displayId, wct) // Execute transaction if there are pending operations if (!wct.isEmpty) { @@ -114,8 +115,8 @@ class DesktopTasksController( } /** Get number of tasks that are marked as visible */ - fun getVisibleTaskCount(): Int { - return desktopModeTaskRepository.getVisibleTaskCount() + fun getVisibleTaskCount(displayId: Int): Int { + return desktopModeTaskRepository.getVisibleTaskCount(displayId) } /** Move a task with given `taskId` to desktop */ @@ -129,7 +130,7 @@ class DesktopTasksController( val wct = WindowContainerTransaction() // Bring other apps to front first - bringDesktopAppsToFront(wct) + bringDesktopAppsToFront(task.displayId, wct) addMoveToDesktopChanges(wct, task.token) if (Transitions.ENABLE_SHELL_TRANSITIONS) { transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */) @@ -165,7 +166,7 @@ class DesktopTasksController( freeformBounds: Rect ) { val wct = WindowContainerTransaction() - bringDesktopAppsToFront(wct) + bringDesktopAppsToFront(taskInfo.displayId, wct) addMoveToDesktopChanges(wct, taskInfo.getToken()) wct.setBounds(taskInfo.token, freeformBounds) @@ -244,9 +245,9 @@ class DesktopTasksController( ?: WINDOWING_MODE_UNDEFINED } - private fun bringDesktopAppsToFront(wct: WindowContainerTransaction) { + private fun bringDesktopAppsToFront(displayId: Int, wct: WindowContainerTransaction) { ProtoLog.v(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront") - val activeTasks = desktopModeTaskRepository.getActiveTasks() + val activeTasks = desktopModeTaskRepository.getActiveTasks(displayId) // First move home to front and then other tasks on top of it moveHomeTaskToFront(wct) @@ -290,18 +291,17 @@ class DesktopTasksController( request: TransitionRequestInfo ): WindowContainerTransaction? { // Check if we should skip handling this transition - val task: RunningTaskInfo? = request.triggerTask val shouldHandleRequest = when { // Only handle open or to front transitions request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> false // Only handle when it is a task transition - task == null -> false + request.triggerTask == null -> false // Only handle standard type tasks - task.activityType != ACTIVITY_TYPE_STANDARD -> false + request.triggerTask.activityType != ACTIVITY_TYPE_STANDARD -> false // Only handle fullscreen or freeform tasks - task.windowingMode != WINDOWING_MODE_FULLSCREEN && - task.windowingMode != WINDOWING_MODE_FREEFORM -> false + request.triggerTask.windowingMode != WINDOWING_MODE_FULLSCREEN && + request.triggerTask.windowingMode != WINDOWING_MODE_FREEFORM -> false // Otherwise process it else -> true } @@ -310,10 +310,11 @@ class DesktopTasksController( return null } - val activeTasks = desktopModeTaskRepository.getActiveTasks() + val task: RunningTaskInfo = request.triggerTask + val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId) // Check if we should switch a fullscreen task to freeform - if (task?.windowingMode == WINDOWING_MODE_FULLSCREEN) { + if (task.windowingMode == WINDOWING_MODE_FULLSCREEN) { // If there are any visible desktop tasks, switch the task to freeform if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) { ProtoLog.d( @@ -329,7 +330,7 @@ class DesktopTasksController( } // CHeck if we should switch a freeform task to fullscreen - if (task?.windowingMode == WINDOWING_MODE_FREEFORM) { + if (task.windowingMode == WINDOWING_MODE_FREEFORM) { // If no visible desktop tasks, switch this task to freeform as the transition came // outside of this controller if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) { @@ -559,20 +560,19 @@ class DesktopTasksController( controller = null } - override fun showDesktopApps() { + override fun showDesktopApps(displayId: Int) { ExecutorUtils.executeRemoteCallWithTaskPermission( controller, - "showDesktopApps", - Consumer(DesktopTasksController::showDesktopApps) - ) + "showDesktopApps" + ) { c -> c.showDesktopApps(displayId) } } - override fun getVisibleTaskCount(): Int { + override fun getVisibleTaskCount(displayId: Int): Int { val result = IntArray(1) ExecutorUtils.executeRemoteCallWithTaskPermission( controller, "getVisibleTaskCount", - { controller -> result[0] = controller.getVisibleTaskCount() }, + { controller -> result[0] = controller.getVisibleTaskCount(displayId) }, true /* blocking */ ) return result[0] diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl index d0739e14675f..899d67267e69 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl @@ -21,9 +21,9 @@ package com.android.wm.shell.desktopmode; */ interface IDesktopMode { - /** Show apps on the desktop */ - void showDesktopApps(); + /** Show apps on the desktop on the given display */ + void showDesktopApps(int displayId); - /** Get count of visible desktop tasks */ - int getVisibleTaskCount(); + /** Get count of visible desktop tasks on the given display */ + int getVisibleTaskCount(int displayId); }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java index 4cfaae6e51c7..091de3a86461 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java @@ -94,7 +94,24 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange void onDragStarted(); } - public DragAndDropController(Context context, + /** + * Creates {@link DragAndDropController}. Returns {@code null} if the feature is disabled. + */ + public static DragAndDropController create(Context context, + ShellInit shellInit, + ShellController shellController, + DisplayController displayController, + UiEventLogger uiEventLogger, + IconProvider iconProvider, + ShellExecutor mainExecutor) { + if (!context.getResources().getBoolean(R.bool.config_enableShellDragDrop)) { + return null; + } + return new DragAndDropController(context, shellInit, shellController, displayController, + uiEventLogger, iconProvider, mainExecutor); + } + + DragAndDropController(Context context, ShellInit shellInit, ShellController shellController, DisplayController displayController, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java index 48487bc4a3d6..22541bbd892a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java @@ -94,11 +94,12 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, mDesktopModeTaskRepository.ifPresent(repository -> { repository.addOrMoveFreeformTaskToTop(taskInfo.taskId); if (taskInfo.isVisible) { - if (repository.addActiveTask(taskInfo.taskId)) { + if (repository.addActiveTask(taskInfo.displayId, taskInfo.taskId)) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "Adding active freeform task: #%d", taskInfo.taskId); } - repository.updateVisibleFreeformTasks(taskInfo.taskId, true); + repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId, + true); } }); } @@ -117,7 +118,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "Removing active freeform task: #%d", taskInfo.taskId); } - repository.updateVisibleFreeformTasks(taskInfo.taskId, false); + repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId, false); }); } @@ -137,12 +138,13 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, if (DesktopModeStatus.isAnyEnabled()) { mDesktopModeTaskRepository.ifPresent(repository -> { if (taskInfo.isVisible) { - if (repository.addActiveTask(taskInfo.taskId)) { + if (repository.addActiveTask(taskInfo.displayId, taskInfo.taskId)) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "Adding active freeform task: #%d", taskInfo.taskId); } } - repository.updateVisibleFreeformTasks(taskInfo.taskId, taskInfo.isVisible); + repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId, + taskInfo.isVisible); }); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java index e1a56a1a5a7a..6b6a7bc42046 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java @@ -153,6 +153,8 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs @Override public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) { + mWindowDecorViewModel.onTransitionMerged(merged, playing); + final List<ActivityManager.RunningTaskInfo> infoOfMerged = mTransitionToTaskInfo.get(merged); if (infoOfMerged == null) { @@ -169,8 +171,6 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs } else { mTransitionToTaskInfo.put(playing, infoOfMerged); } - - mWindowDecorViewModel.onTransitionMerged(merged, playing); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 566c130c7573..d3e7f9ca4227 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -72,7 +72,6 @@ import android.window.TaskOrganizer; import android.window.TaskSnapshot; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; -import android.window.WindowContainerTransactionCallback; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; @@ -139,17 +138,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // the runnable to execute after WindowContainerTransactions is applied to finish resizing pip private Runnable mPipFinishResizeWCTRunnable; - private final WindowContainerTransactionCallback mPipFinishResizeWCTCallback = - new WindowContainerTransactionCallback() { - @Override - public void onTransactionReady(int id, SurfaceControl.Transaction t) { - t.apply(); - - // execute the runnable if non-null after WCT is applied to finish resizing pip - maybePerformFinishResizeCallback(); - } - }; - private void maybePerformFinishResizeCallback() { if (mPipFinishResizeWCTRunnable != null) { mPipFinishResizeWCTRunnable.run(); @@ -175,6 +163,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final int direction = animator.getTransitionDirection(); if (mIsCancelled) { sendOnPipTransitionFinished(direction); + maybePerformFinishResizeCallback(); return; } final int animationType = animator.getAnimationType(); @@ -199,6 +188,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, || isRemovePipDirection(direction); if (mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP || isExitPipDirection) { + // execute the finish resize callback if needed after the transaction is committed + tx.addTransactionCommittedListener(mMainExecutor, + PipTaskOrganizer.this::maybePerformFinishResizeCallback); + // Finish resize as long as we're not exiting PIP, or, if we are, only if this is // the end of an exit PIP animation. // This is necessary in case there was a resize animation ongoing when exit PIP @@ -1614,12 +1607,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) { mSplitScreenOptional.ifPresent(splitScreenController -> splitScreenController.enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct)); - } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) { - // when leaving PiP we can call the callback without sync - maybePerformFinishResizeCallback(); - mTaskOrganizer.applyTransaction(wct); } else { - mTaskOrganizer.applySyncTransaction(wct, mPipFinishResizeWCTCallback); + mTaskOrganizer.applyTransaction(wct); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index b0bb14b49db6..f8e143575583 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -1060,13 +1060,22 @@ public class PipController implements PipTransitionController.PipTransitionCallb /** Save the state to restore to on re-entry. */ public void saveReentryState(Rect pipBounds) { float snapFraction = mPipBoundsAlgorithm.getSnapFraction(pipBounds); - if (mPipBoundsState.hasUserResizedPip()) { - final Rect reentryBounds = mTouchHandler.getUserResizeBounds(); - final Size reentrySize = new Size(reentryBounds.width(), reentryBounds.height()); - mPipBoundsState.saveReentryState(reentrySize, snapFraction); - } else { + + if (!mPipBoundsState.hasUserResizedPip()) { mPipBoundsState.saveReentryState(null /* bounds */, snapFraction); + return; } + + Size reentrySize = new Size(pipBounds.width(), pipBounds.height()); + + // TODO: b/279937014 Investigate why userResizeBounds are empty with shell transitions on + // fallback to using the userResizeBounds if userResizeBounds are not empty + if (!mTouchHandler.getUserResizeBounds().isEmpty()) { + Rect userResizeBounds = mTouchHandler.getUserResizeBounds(); + reentrySize = new Size(userResizeBounds.width(), userResizeBounds.height()); + } + + mPipBoundsState.saveReentryState(reentrySize, snapFraction); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index 956af709f156..281cae5e4ffa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -104,6 +104,7 @@ public class PipResizeGestureHandler { private boolean mAllowGesture; private boolean mIsAttached; private boolean mIsEnabled; + private boolean mEnableTouch; private boolean mEnablePinchResize; private boolean mEnableDragCornerResize; private boolean mIsSysUiStateValid; @@ -138,6 +139,7 @@ public class PipResizeGestureHandler { mPhonePipMenuController = menuActivityController; mPipUiEventLogger = pipUiEventLogger; mPinchResizingAlgorithm = new PipPinchResizingAlgorithm(); + mEnableTouch = true; mUpdateResizeBoundsCallback = (rect) -> { mUserResizeBounds.set(rect); @@ -248,6 +250,11 @@ public class PipResizeGestureHandler { return; } + if (!mEnableTouch) { + // No need to handle anything if touches are not enabled for resizing. + return; + } + // Don't allow resize when PiP is stashed. if (mPipBoundsState.isStashed()) { return; @@ -581,14 +588,13 @@ public class PipResizeGestureHandler { mLastResizeBounds, movementBounds); mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction); - // disable the pinch resizing until the final bounds are updated - final boolean prevEnablePinchResize = mEnablePinchResize; - mEnablePinchResize = false; + // disable the resizing until the final bounds are updated + mEnableTouch = false; mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds, PINCH_RESIZE_SNAP_DURATION, mAngle, mUpdateResizeBoundsCallback, () -> { // reset the pinch resizing to its default state - mEnablePinchResize = prevEnablePinchResize; + mEnableTouch = true; }); } else { mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java index c5bfd8753994..5c9709c756f7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java @@ -245,7 +245,7 @@ public class RecentTasksController implements TaskStackListenerCallback, } @Override - public void onActiveTasksChanged() { + public void onActiveTasksChanged(int displayId) { notifyRecentTasksChanged(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index eb4d2a16c522..bffc51c6b22f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -549,6 +549,14 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { // are in mOpening. for (int i = 0; i < closingTasks.size(); ++i) { final TransitionInfo.Change change = closingTasks.get(i); + final int pausingIdx = TaskState.indexOf(mPausingTasks, change); + if (pausingIdx >= 0) { + mPausingTasks.remove(pausingIdx); + didMergeThings = true; + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + " closing pausing taskId=%d", change.getTaskInfo().taskId); + continue; + } int openingIdx = TaskState.indexOf(mOpeningTasks, change); if (openingIdx < 0) { Slog.w(TAG, "Closing a task that wasn't opening, this may be split or" @@ -601,6 +609,11 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { didMergeThings = true; mState = STATE_NEW_TASK; } + if (mPausingTasks.isEmpty()) { + // The pausing tasks may be removed by the incoming closing tasks. + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.merge: empty pausing tasks", mInstanceId); + } if (!hasTaskChange) { // Activity only transition, so consume the merge as it doesn't affect the rest of // recents. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 643245943916..af52350f5b48 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -165,7 +165,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, private final DisplayController mDisplayController; private final DisplayImeController mDisplayImeController; private final DisplayInsetsController mDisplayInsetsController; - private final DragAndDropController mDragAndDropController; + private final Optional<DragAndDropController> mDragAndDropController; private final Transitions mTransitions; private final TransactionPool mTransactionPool; private final IconProvider mIconProvider; @@ -191,7 +191,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, DisplayController displayController, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, - DragAndDropController dragAndDropController, + Optional<DragAndDropController> dragAndDropController, Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider, @@ -253,7 +253,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mDisplayController = displayController; mDisplayImeController = displayImeController; mDisplayInsetsController = displayInsetsController; - mDragAndDropController = dragAndDropController; + mDragAndDropController = Optional.of(dragAndDropController); mTransitions = transitions; mTransactionPool = transactionPool; mIconProvider = iconProvider; @@ -289,7 +289,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, // TODO: Multi-display mStageCoordinator = createStageCoordinator(); } - mDragAndDropController.setSplitScreenController(this); + mDragAndDropController.ifPresent(controller -> controller.setSplitScreenController(this)); } protected StageCoordinator createStageCoordinator() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index 51b8000f14bb..a2af93fc42c6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -30,8 +30,6 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP; -import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE; -import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -165,7 +163,7 @@ class SplitScreenTransitions { t.setLayer(leash, Integer.MAX_VALUE); t.show(leash); } - boolean isOpening = isOpeningTransition(info); + boolean isOpening = TransitionUtil.isOpeningType(info.getType()); if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { // fade in startExampleAnimation(leash, true /* show */); @@ -295,14 +293,16 @@ class SplitScreenTransitions { @Nullable RemoteTransition remoteTransition, Transitions.TransitionHandler handler, @Nullable TransitionConsumedCallback consumedCallback, - @Nullable TransitionFinishedCallback finishedCallback) { + @Nullable TransitionFinishedCallback finishedCallback, + int extraTransitType) { if (mPendingEnter != null) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " + " skip to start enter split transition since it already exist. "); return null; } final IBinder transition = mTransitions.startTransition(transitType, wct, handler); - setEnterTransition(transition, remoteTransition, consumedCallback, finishedCallback); + setEnterTransition(transition, remoteTransition, consumedCallback, finishedCallback, + extraTransitType); return transition; } @@ -310,9 +310,10 @@ class SplitScreenTransitions { void setEnterTransition(@NonNull IBinder transition, @Nullable RemoteTransition remoteTransition, @Nullable TransitionConsumedCallback consumedCallback, - @Nullable TransitionFinishedCallback finishedCallback) { + @Nullable TransitionFinishedCallback finishedCallback, + int extraTransitType) { mPendingEnter = new TransitSession( - transition, consumedCallback, finishedCallback, remoteTransition); + transition, consumedCallback, finishedCallback, remoteTransition, extraTransitType); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " + " deduced Enter split screen"); @@ -513,12 +514,6 @@ class SplitScreenTransitions { mTransitions.getAnimExecutor().execute(va::start); } - private boolean isOpeningTransition(TransitionInfo info) { - return TransitionUtil.isOpeningType(info.getType()) - || info.getType() == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE - || info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN; - } - /** Calls when the transition got consumed. */ interface TransitionConsumedCallback { void onConsumed(boolean aborted); @@ -539,16 +534,19 @@ class SplitScreenTransitions { /** Whether the transition was canceled. */ boolean mCanceled; + /** A note for extra transit type, to help indicate custom transition. */ + final int mExtraTransitType; + TransitSession(IBinder transition, @Nullable TransitionConsumedCallback consumedCallback, @Nullable TransitionFinishedCallback finishedCallback) { - this(transition, consumedCallback, finishedCallback, null /* remoteTransition */); + this(transition, consumedCallback, finishedCallback, null /* remoteTransition */, 0); } TransitSession(IBinder transition, @Nullable TransitionConsumedCallback consumedCallback, @Nullable TransitionFinishedCallback finishedCallback, - @Nullable RemoteTransition remoteTransition) { + @Nullable RemoteTransition remoteTransition, int extraTransitType) { mTransition = transition; mConsumedCallback = consumedCallback; mFinishedCallback = finishedCallback; @@ -560,6 +558,7 @@ class SplitScreenTransitions { mTransitions.getMainExecutor(), remoteTransition); mRemoteHandler.setTransition(transition); } + mExtraTransitType = extraTransitType; } /** Sets transition consumed callback. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 4c903f500651..0ef26fcff284 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -446,26 +446,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, final IRemoteAnimationFinishedCallback finishedCallback) { - boolean openingToSide = false; - if (apps != null) { - for (int i = 0; i < apps.length; ++i) { - if (apps[i].mode == MODE_OPENING - && mSideStage.containsTask(apps[i].taskId)) { - openingToSide = true; - break; - } - } - } else if (mSideStage.getChildCount() != 0) { - // There are chances the entering app transition got canceled by performing - // rotation transition. Checks if there is any child task existed in split - // screen before fallback to cancel entering flow. - openingToSide = true; - } - - if (isEnteringSplit && !openingToSide) { + if (isEnteringSplit && mSideStage.getChildCount() == 0) { mMainExecutor.execute(() -> exitSplitScreen( - mSideStage.getChildCount() == 0 ? mMainStage : mSideStage, - EXIT_REASON_UNKNOWN)); + null /* childrenToTop */, EXIT_REASON_UNKNOWN)); + mSplitUnsupportedToast.show(); } if (finishedCallback != null) { @@ -526,17 +510,17 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, wct.sendPendingIntent(intent, fillInIntent, options); // If split screen is not activated, we're expecting to open a pair of apps to split. - final int transitType = mMainStage.isActive() + final int extraTransitType = mMainStage.isActive() ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN; prepareEnterSplitScreen(wct, null /* taskInfo */, position); - mSplitTransitions.startEnterTransition(transitType, wct, null, this, + mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this, null /* consumedCallback */, (finishWct, finishT) -> { if (!evictWct.isEmpty()) { finishWct.merge(evictWct, true); } - } /* finishedCallback */); + } /* finishedCallback */, extraTransitType); } /** Launches an activity into split by legacy transition. */ @@ -550,26 +534,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback, SurfaceControl.Transaction t) { - boolean openingToSide = false; - if (apps != null) { - for (int i = 0; i < apps.length; ++i) { - if (apps[i].mode == MODE_OPENING - && mSideStage.containsTask(apps[i].taskId)) { - openingToSide = true; - break; - } - } - } else if (mSideStage.getChildCount() != 0) { - // There are chances the entering app transition got canceled by performing - // rotation transition. Checks if there is any child task existed in split - // screen before fallback to cancel entering flow. - openingToSide = true; - } - - if (isEnteringSplit && !openingToSide && apps != null) { + if (isEnteringSplit && mSideStage.getChildCount() == 0) { mMainExecutor.execute(() -> exitSplitScreen( - mSideStage.getChildCount() == 0 ? mMainStage : mSideStage, - EXIT_REASON_UNKNOWN)); + null /* childrenToTop */, EXIT_REASON_UNKNOWN)); + mSplitUnsupportedToast.show(); } if (apps != null) { @@ -709,7 +677,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, wct.startTask(mainTaskId, mainOptions); mSplitTransitions.startEnterTransition( - TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null, null); + TRANSIT_TO_FRONT, wct, remoteTransition, this, null, null, + TRANSIT_SPLIT_SCREEN_PAIR_OPEN); setEnterInstanceId(instanceId); } @@ -760,7 +729,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } mSplitTransitions.startEnterTransition( - TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null, null); + TRANSIT_TO_FRONT, wct, remoteTransition, this, null, null, + TRANSIT_SPLIT_SCREEN_PAIR_OPEN); setEnterInstanceId(instanceId); } @@ -1088,7 +1058,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void onRemoteAnimationFinishedOrCancelled(WindowContainerTransaction evictWct) { mIsDividerRemoteAnimating = false; mShouldUpdateRecents = true; - mSplitRequest = null; + clearRequestIfPresented(); // If any stage has no child after animation finished, it means that split will display // nothing, such status will happen if task and intent is same app but not support // multi-instance, we should exit split and expand that app as full screen. @@ -1108,7 +1078,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void onRemoteAnimationFinished(RemoteAnimationTarget[] apps) { mIsDividerRemoteAnimating = false; mShouldUpdateRecents = true; - mSplitRequest = null; + clearRequestIfPresented(); // If any stage has no child after finished animation, that side of the split will display // nothing. This might happen if starting the same app on the both sides while not // supporting multi-instance. Exit the split screen and expand that app to full screen. @@ -1381,6 +1351,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, }); mShouldUpdateRecents = false; mIsDividerRemoteAnimating = false; + mSplitRequest = null; mSplitLayout.getInvisibleBounds(mTempRect1); if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) { @@ -1473,6 +1444,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } + private void clearRequestIfPresented() { + if (mSideStageListener.mVisible && mSideStageListener.mHasChildren + && mMainStageListener.mVisible && mSideStageListener.mHasChildren) { + mSplitRequest = null; + } + } + /** * Returns whether the split pair in the recent tasks list should be broken. */ @@ -1851,6 +1829,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, true /* setReparentLeafTaskIfRelaunch */); setRootForceTranslucent(true, wct); } else { + clearRequestIfPresented(); wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token, false /* setReparentLeafTaskIfRelaunch */); setRootForceTranslucent(false, wct); @@ -2010,7 +1989,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) { mShouldUpdateRecents = true; - mSplitRequest = null; + clearRequestIfPresented(); updateRecentTasksSplitPair(); if (!mLogger.hasStartedSession()) { @@ -2334,7 +2313,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, out = new WindowContainerTransaction(); prepareEnterSplitScreen(out); mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(), - null /* consumedCallback */, null /* finishedCallback */); + null /* consumedCallback */, null /* finishedCallback */, + 0 /* extraTransitType */); } } return out; @@ -2573,7 +2553,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - if (info.getType() == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) { + if (mSplitTransitions.mPendingEnter.mExtraTransitType + == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) { if (mainChild == null && sideChild == null) { Log.w(TAG, "Launched a task in split, but didn't receive any task in transition."); mSplitTransitions.mPendingEnter.cancel(null /* finishedCb */); @@ -2716,6 +2697,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } }); mShouldUpdateRecents = false; + mSplitRequest = null; // Update local states. setSplitsVisible(false); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index ead0bcd15c73..a841b7f96d3c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -220,20 +220,12 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { mCallbacks.onNoLongerSupportMultiWindow(); return; } - if (taskInfo.topActivity == null && mChildrenTaskInfo.contains(taskInfo.taskId) - && mChildrenTaskInfo.get(taskInfo.taskId).topActivity != null) { - // If top activity become null, it means the task is about to vanish, we use this - // signal to remove it from children list earlier for smooth dismiss transition. - mChildrenTaskInfo.remove(taskInfo.taskId); - mChildrenLeashes.remove(taskInfo.taskId); - } else { - mChildrenTaskInfo.put(taskInfo.taskId, taskInfo); - } + mChildrenTaskInfo.put(taskInfo.taskId, taskInfo); mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */, taskInfo.isVisible); - if (!ENABLE_SHELL_TRANSITIONS && mChildrenLeashes.contains(taskInfo.taskId)) { - updateChildTaskSurface(taskInfo, mChildrenLeashes.get(taskInfo.taskId), - false /* firstAppeared */); + if (!ENABLE_SHELL_TRANSITIONS) { + updateChildTaskSurface( + taskInfo, mChildrenLeashes.get(taskInfo.taskId), false /* firstAppeared */); } } else { throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo @@ -267,6 +259,9 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { return; } sendStatusChanged(); + } else { + throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo + + "\n mRootTaskInfo: " + mRootTaskInfo); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java index 46d2a5a11671..27d520d81c41 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java @@ -72,7 +72,7 @@ public class TvSplitScreenController extends SplitScreenController { DisplayController displayController, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, - DragAndDropController dragAndDropController, + Optional<DragAndDropController> dragAndDropController, Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java index 7991c529aa49..2ab4c751d399 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java @@ -82,6 +82,10 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { mGuard.open("release"); } + SurfaceControl getSurfaceControl() { + return mSurfaceControl; + } + /** * Sets the provided {@link TaskViewBase}, which is used to notify the client part about the * task related changes and getting the current bounds. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java index 81d69a4fa611..c7e534a2bcf5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java @@ -336,6 +336,19 @@ public class TaskViewTransitions implements Transitions.TransitionHandler { tv.prepareOpenAnimation(taskIsNew, startTransaction, finishTransaction, chg.getTaskInfo(), chg.getLeash(), wct); changesHandled++; + } else if (chg.getMode() == TRANSIT_CHANGE) { + TaskViewTaskController tv = findTaskView(chg.getTaskInfo()); + if (tv == null) { + if (pending != null) { + Slog.w(TAG, "Found a non-TaskView task in a TaskView Transition. This " + + "shouldn't happen, so there may be a visual artifact: " + + chg.getTaskInfo().taskId); + } + continue; + } + startTransaction.reparent(chg.getLeash(), tv.getSurfaceControl()); + finishTransaction.reparent(chg.getLeash(), tv.getSurfaceControl()); + changesHandled++; } } if (stillNeedsMatchingLaunch) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java index d25318df6b6a..9ce22094d56b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java @@ -144,6 +144,7 @@ class ScreenRotationAnimation { .setCaptureSecureLayers(true) .setAllowProtected(true) .setSourceCrop(new Rect(0, 0, mStartWidth, mStartHeight)) + .setHintForSeamlessTransition(true) .build(); ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = ScreenCapture.captureLayers(args); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java new file mode 100644 index 000000000000..ba364f8a6e59 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.transition; + +import static android.os.Build.IS_USER; + +import static com.android.wm.shell.WmShellTransitionTraceProto.MAGIC_NUMBER; +import static com.android.wm.shell.WmShellTransitionTraceProto.MAGIC_NUMBER_H; +import static com.android.wm.shell.WmShellTransitionTraceProto.MAGIC_NUMBER_L; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.SystemClock; +import android.os.Trace; +import android.util.Log; +import android.util.proto.ProtoOutputStream; + +import com.android.internal.util.TraceBuffer; +import com.android.wm.shell.sysui.ShellCommandHandler; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; + +/** + * Helper class to collect and dump transition traces. + */ +public class Tracer implements ShellCommandHandler.ShellCommandActionHandler { + private static final int ALWAYS_ON_TRACING_CAPACITY = 15 * 1024; // 15 KB + private static final int ACTIVE_TRACING_BUFFER_CAPACITY = 5000 * 1024; // 5 MB + + private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; + + static final String WINSCOPE_EXT = ".winscope"; + private static final String TRACE_FILE = + "/data/misc/wmtrace/shell_transition_trace" + WINSCOPE_EXT; + + private final Object mEnabledLock = new Object(); + private boolean mActiveTracingEnabled = false; + + private final TraceBuffer mTraceBuffer = new TraceBuffer(ALWAYS_ON_TRACING_CAPACITY, + (proto) -> handleOnEntryRemovedFromTrace(proto)); + private final Map<Object, Runnable> mRemovedFromTraceCallbacks = new HashMap<>(); + + private final Map<Transitions.TransitionHandler, Integer> mHandlerIds = new HashMap<>(); + private final Map<Transitions.TransitionHandler, Integer> mHandlerUseCountInTrace = + new HashMap<>(); + + /** + * Adds an entry in the trace to log that a transition has been dispatched to a handler. + * + * @param transitionId The id of the transition being dispatched. + * @param handler The handler the transition is being dispatched to. + */ + public void logDispatched(int transitionId, Transitions.TransitionHandler handler) { + final int handlerId; + if (mHandlerIds.containsKey(handler)) { + handlerId = mHandlerIds.get(handler); + } else { + // + 1 to avoid 0 ids which can be confused with missing value when dumped to proto + handlerId = mHandlerIds.size() + 1; + mHandlerIds.put(handler, handlerId); + } + + ProtoOutputStream outputStream = new ProtoOutputStream(); + final long protoToken = + outputStream.start(com.android.wm.shell.WmShellTransitionTraceProto.TRANSITIONS); + + outputStream.write(com.android.wm.shell.Transition.ID, transitionId); + outputStream.write(com.android.wm.shell.Transition.DISPATCH_TIME_NS, + SystemClock.elapsedRealtimeNanos()); + outputStream.write(com.android.wm.shell.Transition.HANDLER, handlerId); + + outputStream.end(protoToken); + + final int useCountAfterAdd = mHandlerUseCountInTrace.getOrDefault(handler, 0) + 1; + mHandlerUseCountInTrace.put(handler, useCountAfterAdd); + + mRemovedFromTraceCallbacks.put(outputStream, () -> { + final int useCountAfterRemove = mHandlerUseCountInTrace.get(handler) - 1; + mHandlerUseCountInTrace.put(handler, useCountAfterRemove); + }); + + mTraceBuffer.add(outputStream); + } + + /** + * Adds an entry in the trace to log that a request to merge a transition was made. + * + * @param mergeRequestedTransitionId The id of the transition we are requesting to be merged. + * @param playingTransitionId The id of the transition we was to merge the transition into. + */ + public void logMergeRequested(int mergeRequestedTransitionId, int playingTransitionId) { + ProtoOutputStream outputStream = new ProtoOutputStream(); + final long protoToken = + outputStream.start(com.android.wm.shell.WmShellTransitionTraceProto.TRANSITIONS); + + outputStream.write(com.android.wm.shell.Transition.ID, mergeRequestedTransitionId); + outputStream.write(com.android.wm.shell.Transition.MERGE_REQUEST_TIME_NS, + SystemClock.elapsedRealtimeNanos()); + outputStream.write(com.android.wm.shell.Transition.MERGED_INTO, playingTransitionId); + + outputStream.end(protoToken); + + mTraceBuffer.add(outputStream); + } + + /** + * Adds an entry in the trace to log that a transition was merged by the handler. + * + * @param mergedTransitionId The id of the transition that was merged. + * @param playingTransitionId The id of the transition the transition was merged into. + */ + public void logMerged(int mergedTransitionId, int playingTransitionId) { + ProtoOutputStream outputStream = new ProtoOutputStream(); + final long protoToken = + outputStream.start(com.android.wm.shell.WmShellTransitionTraceProto.TRANSITIONS); + + outputStream.write(com.android.wm.shell.Transition.ID, mergedTransitionId); + outputStream.write( + com.android.wm.shell.Transition.MERGE_TIME_NS, SystemClock.elapsedRealtimeNanos()); + outputStream.write(com.android.wm.shell.Transition.MERGED_INTO, playingTransitionId); + + outputStream.end(protoToken); + + mTraceBuffer.add(outputStream); + } + + /** + * Adds an entry in the trace to log that a transition was aborted. + * + * @param transitionId The id of the transition that was aborted. + */ + public void logAborted(int transitionId) { + ProtoOutputStream outputStream = new ProtoOutputStream(); + final long protoToken = + outputStream.start(com.android.wm.shell.WmShellTransitionTraceProto.TRANSITIONS); + + outputStream.write(com.android.wm.shell.Transition.ID, transitionId); + outputStream.write( + com.android.wm.shell.Transition.ABORT_TIME_NS, SystemClock.elapsedRealtimeNanos()); + + outputStream.end(protoToken); + + mTraceBuffer.add(outputStream); + } + + /** + * Starts collecting transitions for the trace. + * If called while a trace is already running, this will reset the trace. + */ + public void startTrace(@Nullable PrintWriter pw) { + if (IS_USER) { + LogAndPrintln.e(pw, "Tracing is not supported on user builds."); + return; + } + Trace.beginSection("Tracer#startTrace"); + LogAndPrintln.i(pw, "Starting shell transition trace."); + synchronized (mEnabledLock) { + mActiveTracingEnabled = true; + mTraceBuffer.resetBuffer(); + mTraceBuffer.setCapacity(ACTIVE_TRACING_BUFFER_CAPACITY); + } + Trace.endSection(); + } + + /** + * Stops collecting the transition trace and dump to trace to file. + * + * Dumps the trace to @link{TRACE_FILE}. + */ + public void stopTrace(@Nullable PrintWriter pw) { + stopTrace(pw, new File(TRACE_FILE)); + } + + /** + * Stops collecting the transition trace and dump to trace to file. + * @param outputFile The file to dump the transition trace to. + */ + public void stopTrace(@Nullable PrintWriter pw, File outputFile) { + if (IS_USER) { + LogAndPrintln.e(pw, "Tracing is not supported on user builds."); + return; + } + Trace.beginSection("Tracer#stopTrace"); + LogAndPrintln.i(pw, "Stopping shell transition trace."); + synchronized (mEnabledLock) { + mActiveTracingEnabled = false; + writeTraceToFileLocked(pw, outputFile); + mTraceBuffer.resetBuffer(); + mTraceBuffer.setCapacity(ALWAYS_ON_TRACING_CAPACITY); + } + Trace.endSection(); + } + + /** + * Being called while taking a bugreport so that tracing files can be included in the bugreport. + * + * @param pw Print writer + */ + public void saveForBugreport(@Nullable PrintWriter pw) { + if (IS_USER) { + LogAndPrintln.e(pw, "Tracing is not supported on user builds."); + return; + } + Trace.beginSection("TransitionTracer#saveForBugreport"); + synchronized (mEnabledLock) { + final File outputFile = new File(TRACE_FILE); + writeTraceToFileLocked(pw, outputFile); + } + Trace.endSection(); + } + + private void writeTraceToFileLocked(@Nullable PrintWriter pw, File file) { + Trace.beginSection("TransitionTracer#writeTraceToFileLocked"); + try { + ProtoOutputStream proto = new ProtoOutputStream(); + proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); + writeHandlerMappingToProto(proto); + int pid = android.os.Process.myPid(); + LogAndPrintln.i(pw, "Writing file to " + file.getAbsolutePath() + + " from process " + pid); + mTraceBuffer.writeTraceToFile(file, proto); + } catch (IOException e) { + LogAndPrintln.e(pw, "Unable to write buffer to file", e); + } + Trace.endSection(); + } + + private void writeHandlerMappingToProto(ProtoOutputStream outputStream) { + for (Transitions.TransitionHandler handler : mHandlerUseCountInTrace.keySet()) { + final int count = mHandlerUseCountInTrace.get(handler); + if (count > 0) { + final long protoToken = outputStream.start( + com.android.wm.shell.WmShellTransitionTraceProto.HANDLER_MAPPINGS); + outputStream.write(com.android.wm.shell.HandlerMapping.ID, + mHandlerIds.get(handler)); + outputStream.write(com.android.wm.shell.HandlerMapping.NAME, + handler.getClass().getName()); + outputStream.end(protoToken); + } + } + } + + private void handleOnEntryRemovedFromTrace(Object proto) { + if (mRemovedFromTraceCallbacks.containsKey(proto)) { + mRemovedFromTraceCallbacks.get(proto).run(); + mRemovedFromTraceCallbacks.remove(proto); + } + } + + @Override + public boolean onShellCommand(String[] args, PrintWriter pw) { + switch (args[0]) { + case "start": { + startTrace(pw); + return true; + } + case "stop": { + stopTrace(pw); + return true; + } + case "save-for-bugreport": { + saveForBugreport(pw); + return true; + } + default: { + pw.println("Invalid command: " + args[0]); + printShellCommandHelp(pw, ""); + return false; + } + } + } + + @Override + public void printShellCommandHelp(PrintWriter pw, String prefix) { + pw.println(prefix + "start"); + pw.println(prefix + " Start tracing the transitions."); + pw.println(prefix + "stop"); + pw.println(prefix + " Stop tracing the transitions."); + pw.println(prefix + "save-for-bugreport"); + pw.println(prefix + " Flush in memory transition trace to file."); + } + + private static class LogAndPrintln { + private static final String LOG_TAG = "ShellTransitionTracer"; + + private static void i(@Nullable PrintWriter pw, String msg) { + Log.i(LOG_TAG, msg); + if (pw != null) { + pw.println(msg); + pw.flush(); + } + } + + private static void e(@Nullable PrintWriter pw, String msg) { + Log.e(LOG_TAG, msg); + if (pw != null) { + pw.println("ERROR: " + msg); + pw.flush(); + } + } + + private static void e(@Nullable PrintWriter pw, String msg, @NonNull Exception e) { + Log.e(LOG_TAG, msg, e); + if (pw != null) { + pw.println("ERROR: " + msg + " ::\n " + e); + pw.flush(); + } + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index a4057b1328f4..f79f1f7a27b1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -74,10 +74,12 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.util.TransitionUtil; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -106,7 +108,8 @@ import java.util.Arrays; * track, it will be marked as SYNC. This means that all currently active tracks must be flushed * before the SYNC transition can play. */ -public class Transitions implements RemoteCallable<Transitions> { +public class Transitions implements RemoteCallable<Transitions>, + ShellCommandHandler.ShellCommandActionHandler { static final String TAG = "ShellTransitions"; /** Set to {@code true} to enable shell transitions. */ @@ -165,12 +168,15 @@ public class Transitions implements RemoteCallable<Transitions> { private final ShellController mShellController; private final ShellTransitionImpl mImpl = new ShellTransitionImpl(); private final SleepHandler mSleepHandler = new SleepHandler(); - + private final Tracer mTracer = new Tracer(); private boolean mIsRegistered = false; /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */ private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>(); + @Nullable + private final ShellCommandHandler mShellCommandHandler; + private final ArrayList<TransitionObserver> mObservers = new ArrayList<>(); /** List of {@link Runnable} instances to run when the last active transition has finished. */ @@ -246,8 +252,23 @@ public class Transitions implements RemoteCallable<Transitions> { @NonNull WindowOrganizer organizer, @NonNull TransactionPool pool, @NonNull DisplayController displayController, - @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler, + @NonNull ShellExecutor mainExecutor, + @NonNull Handler mainHandler, @NonNull ShellExecutor animExecutor) { + this(context, shellInit, shellController, organizer, pool, displayController, mainExecutor, + mainHandler, animExecutor, null); + } + + public Transitions(@NonNull Context context, + @NonNull ShellInit shellInit, + @NonNull ShellController shellController, + @NonNull WindowOrganizer organizer, + @NonNull TransactionPool pool, + @NonNull DisplayController displayController, + @NonNull ShellExecutor mainExecutor, + @NonNull Handler mainHandler, + @NonNull ShellExecutor animExecutor, + @Nullable ShellCommandHandler shellCommandHandler) { mOrganizer = organizer; mContext = context; mMainExecutor = mainExecutor; @@ -263,6 +284,7 @@ public class Transitions implements RemoteCallable<Transitions> { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Default"); // Next lowest priority is remote transitions. mHandlers.add(mRemoteTransitionHandler); + mShellCommandHandler = shellCommandHandler; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote"); shellInit.addInitCallback(this::onInit, this); } @@ -294,6 +316,10 @@ public class Transitions implements RemoteCallable<Transitions> { // Pre-load the instance. TransitionMetrics.getInstance(); } + + if (mShellCommandHandler != null) { + mShellCommandHandler.addCommandCallback("transitions", this, this); + } } public boolean isRegistered() { @@ -766,6 +792,7 @@ public class Transitions implements RemoteCallable<Transitions> { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while" + " %s is still animating. Notify the animating transition" + " in case they can be merged", ready, playing); + mTracer.logMergeRequested(ready.mInfo.getDebugId(), playing.mInfo.getDebugId()); playing.mHandler.mergeAnimation(ready.mToken, ready.mInfo, ready.mStartT, playing.mToken, (wct, cb) -> onMerged(playing, ready)); } @@ -799,6 +826,7 @@ public class Transitions implements RemoteCallable<Transitions> { for (int i = 0; i < mObservers.size(); ++i) { mObservers.get(i).onTransitionMerged(merged.mToken, playing.mToken); } + mTracer.logMerged(merged.mInfo.getDebugId(), playing.mInfo.getDebugId()); // See if we should merge another transition. processReadyQueue(track); } @@ -825,6 +853,8 @@ public class Transitions implements RemoteCallable<Transitions> { // Otherwise give every other handler a chance active.mHandler = dispatchTransition(active.mToken, active.mInfo, active.mStartT, active.mFinishT, (wct, cb) -> onFinish(active, wct, cb), active.mHandler); + + mTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler); } /** @@ -876,6 +906,8 @@ public class Transitions implements RemoteCallable<Transitions> { transition.mFinishT.apply(); transition.mAborted = true; + mTracer.logAborted(transition.mInfo.getDebugId()); + if (transition.mHandler != null) { // Notifies to clean-up the aborted transition. transition.mHandler.onTransitionConsumed( @@ -1350,4 +1382,26 @@ public class Transitions implements RemoteCallable<Transitions> { mMainExecutor.execute(() -> dispatchAnimScaleSetting(mTransitionAnimationScaleSetting)); } } + + + @Override + public boolean onShellCommand(String[] args, PrintWriter pw) { + switch (args[0]) { + case "tracing": { + mTracer.onShellCommand(Arrays.copyOfRange(args, 1, args.length), pw); + return true; + } + default: { + pw.println("Invalid command: " + args[0]); + printShellCommandHelp(pw, ""); + return false; + } + } + } + + @Override + public void printShellCommandHelp(PrintWriter pw, String prefix) { + pw.println(prefix + "tracing"); + mTracer.printShellCommandHelp(pw, prefix + " "); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 4ef3350d06a4..4cda5715ac1f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -113,11 +113,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private boolean mDragToDesktopAnimationStarted; private float mCaptionDragStartX; - // These values keep track of any transitions to freeform to stop relayout from running on - // changing task so that shellTransitions has a chance to animate the transition - private int mPauseRelayoutForTask = -1; - private IBinder mTransitionPausingRelayout; - public DesktopModeWindowDecorViewModel( Context context, Handler mainHandler, @@ -195,22 +190,25 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @NonNull TransitionInfo info, @NonNull TransitionInfo.Change change) { if (change.getMode() == WindowManager.TRANSIT_CHANGE - && info.getType() == Transitions.TRANSIT_ENTER_DESKTOP_MODE) { - mTransitionPausingRelayout = transition; + && (info.getType() == Transitions.TRANSIT_ENTER_DESKTOP_MODE)) { + mWindowDecorByTaskId.get(change.getTaskInfo().taskId) + .addTransitionPausingRelayout(transition); } } @Override public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) { - if (merged.equals(mTransitionPausingRelayout)) { - mTransitionPausingRelayout = playing; + for (int i = 0; i < mWindowDecorByTaskId.size(); i++) { + final DesktopModeWindowDecoration decor = mWindowDecorByTaskId.valueAt(i); + decor.mergeTransitionPausingRelayout(merged, playing); } } @Override public void onTransitionFinished(@NonNull IBinder transition) { - if (transition.equals(mTransitionPausingRelayout)) { - mPauseRelayoutForTask = -1; + for (int i = 0; i < mWindowDecorByTaskId.size(); i++) { + final DesktopModeWindowDecoration decor = mWindowDecorByTaskId.valueAt(i); + decor.removeTransitionPausingRelayout(transition); } } @@ -225,12 +223,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { incrementEventReceiverTasks(taskInfo.displayId); } - // TaskListener callbacks and shell transitions aren't synchronized, so starting a shell - // transition can trigger an onTaskInfoChanged call that updates the task's SurfaceControl - // and interferes with the transition animation that is playing at the same time. - if (taskInfo.taskId != mPauseRelayoutForTask) { - decoration.relayout(taskInfo); - } + decoration.relayout(taskInfo); } @Override @@ -555,8 +548,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { relevantDecor.mTaskInfo.displayId); if (ev.getY() > 2 * statusBarHeight) { if (DesktopModeStatus.isProto2Enabled()) { - mPauseRelayoutForTask = relevantDecor.mTaskInfo.taskId; - centerAndMoveToDesktopWithAnimation(relevantDecor, ev); + animateToDesktop(relevantDecor, ev); } else if (DesktopModeStatus.isProto1Enabled()) { mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true)); } @@ -632,6 +624,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } /** + * Blocks relayout until transition is finished and transitions to Desktop + */ + private void animateToDesktop(DesktopModeWindowDecoration relevantDecor, + MotionEvent ev) { + relevantDecor.incrementRelayoutBlock(); + centerAndMoveToDesktopWithAnimation(relevantDecor, ev); + } + + /** * Animates a window to the center, grows to freeform size, and transitions to Desktop Mode. * @param relevantDecor the window decor of the task to be animated * @param ev the motion event that triggers the animation @@ -658,10 +659,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - mDesktopTasksController.ifPresent( - c -> c.onDragPositioningEndThroughStatusBar( - relevantDecor.mTaskInfo, - calculateFreeformBounds(FINAL_FREEFORM_SCALE))); + mDesktopTasksController.ifPresent(c -> + c.onDragPositioningEndThroughStatusBar(relevantDecor.mTaskInfo, + calculateFreeformBounds(FINAL_FREEFORM_SCALE))); } }); animator.start(); 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 b1c3791ad15a..95ed42a222b8 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 @@ -30,6 +30,7 @@ import android.graphics.Rect; import android.graphics.Region; import android.graphics.drawable.Drawable; import android.os.Handler; +import android.os.IBinder; import android.util.Log; import android.view.Choreographer; import android.view.MotionEvent; @@ -49,6 +50,9 @@ import com.android.wm.shell.windowdecor.viewholder.DesktopModeAppControlsWindowD import com.android.wm.shell.windowdecor.viewholder.DesktopModeFocusedWindowDecorationViewHolder; import com.android.wm.shell.windowdecor.viewholder.DesktopModeWindowDecorationViewHolder; +import java.util.HashSet; +import java.util.Set; + /** * Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with * {@link DesktopModeWindowDecorViewModel}. @@ -83,6 +87,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private TaskCornersListener mCornersListener; + private final Set<IBinder> mTransitionsPausingRelayout = new HashSet<>(); + private int mRelayoutBlock; + DesktopModeWindowDecoration( Context context, DisplayController displayController, @@ -134,6 +141,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin @Override void relayout(ActivityManager.RunningTaskInfo taskInfo) { + // TaskListener callbacks and shell transitions aren't synchronized, so starting a shell + // transition can trigger an onTaskInfoChanged call that updates the task's SurfaceControl + // and interferes with the transition animation that is playing at the same time. + if (mRelayoutBlock > 0) { + return; + } + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); // Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is // synced with the buffer transaction (that draws the View). Both will be shown on screen @@ -455,6 +469,40 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin return cornersRegion; } + /** + * If transition exists in mTransitionsPausingRelayout, remove the transition and decrement + * mRelayoutBlock + */ + void removeTransitionPausingRelayout(IBinder transition) { + if (mTransitionsPausingRelayout.remove(transition)) { + mRelayoutBlock--; + } + } + + /** + * Add transition to mTransitionsPausingRelayout + */ + void addTransitionPausingRelayout(IBinder transition) { + mTransitionsPausingRelayout.add(transition); + } + + /** + * If two transitions merge and the merged transition is in mTransitionsPausingRelayout, + * remove the merged transition from the set and add the transition it was merged into. + */ + public void mergeTransitionPausingRelayout(IBinder merged, IBinder playing) { + if (mTransitionsPausingRelayout.remove(merged)) { + mTransitionsPausingRelayout.add(playing); + } + } + + /** + * Increase mRelayoutBlock, stopping relayout if mRelayoutBlock is now greater than 0. + */ + public void incrementRelayoutBlock() { + mRelayoutBlock++; + } + static class Factory { DesktopModeWindowDecoration create( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java index ef8332f5e347..1d416c65851b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java @@ -86,9 +86,10 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback { public void onDragPositioningEnd(float x, float y) { PointF delta = DragPositioningCallbackUtility.calculateDelta(x, y, mRepositionStartPoint); - if (mHasMoved && DragPositioningCallbackUtility.changeBounds(mCtrlType, mHasMoved, - mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta, - mDisplayController, mDesktopWindowDecoration)) { + if (mHasMoved) { + DragPositioningCallbackUtility.changeBounds(mCtrlType, mHasMoved, + mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta, + mDisplayController, mDesktopWindowDecoration); DragPositioningCallbackUtility.applyTaskBoundsChange( new WindowContainerTransaction(), mDesktopWindowDecoration, mRepositionTaskBounds, mTaskOrganizer); diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt new file mode 100644 index 000000000000..e06e074ee98a --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker + +import android.app.Instrumentation +import android.tools.device.flicker.junit.FlickerBuilderProvider +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.tapl.LauncherInstrumentation + +abstract class BaseBenchmarkTest +@JvmOverloads +constructor( + protected open val flicker: FlickerTest, + protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(), + protected val tapl: LauncherInstrumentation = LauncherInstrumentation() +) { + /** Specification of the test transition to execute */ + abstract val transition: FlickerBuilder.() -> Unit + + /** + * Entry point for the test runner. It will use this method to initialize and cache flicker + * executions + */ + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + setup { flicker.scenario.setIsTablet(tapl.isTablet) } + transition() + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt index 86edc25b64d3..c98c5a0ad1a6 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt @@ -17,24 +17,10 @@ package com.android.wm.shell.flicker import android.app.Instrumentation -import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher -import android.tools.device.flicker.junit.FlickerBuilderProvider -import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.server.wm.flicker.entireScreenCovered -import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd -import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerIsVisibleAtStartAndEnd -import com.android.server.wm.flicker.statusBarLayerPositionAtStartAndEnd -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.taskBarLayerIsVisibleAtStartAndEnd -import com.android.server.wm.flicker.taskBarWindowIsAlwaysVisible -import org.junit.Assume -import org.junit.Test /** * Base test class containing common assertions for [ComponentNameMatcher.NAV_BAR], @@ -44,124 +30,7 @@ import org.junit.Test abstract class BaseTest @JvmOverloads constructor( - protected val flicker: FlickerTest, - protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(), - protected val tapl: LauncherInstrumentation = LauncherInstrumentation() -) { - /** Specification of the test transition to execute */ - abstract val transition: FlickerBuilder.() -> Unit - - /** - * Entry point for the test runner. It will use this method to initialize and cache flicker - * executions - */ - @FlickerBuilderProvider - fun buildFlicker(): FlickerBuilder { - return FlickerBuilder(instrumentation).apply { - setup { flicker.scenario.setIsTablet(tapl.isTablet) } - transition() - } - } - - /** Checks that all parts of the screen are covered during the transition */ - @Presubmit @Test open fun entireScreenCovered() = flicker.entireScreenCovered() - - /** - * Checks that the [ComponentNameMatcher.NAV_BAR] layer is visible during the whole transition - */ - @Presubmit - @Test - open fun navBarLayerIsVisibleAtStartAndEnd() { - Assume.assumeFalse(flicker.scenario.isTablet) - flicker.navBarLayerIsVisibleAtStartAndEnd() - } - - /** - * Checks the position of the [ComponentNameMatcher.NAV_BAR] at the start and end of the - * transition - */ - @Presubmit - @Test - open fun navBarLayerPositionAtStartAndEnd() { - Assume.assumeFalse(flicker.scenario.isTablet) - flicker.navBarLayerPositionAtStartAndEnd() - } - - /** - * Checks that the [ComponentNameMatcher.NAV_BAR] window is visible during the whole transition - * - * Note: Phones only - */ - @Presubmit - @Test - open fun navBarWindowIsAlwaysVisible() { - Assume.assumeFalse(flicker.scenario.isTablet) - flicker.navBarWindowIsAlwaysVisible() - } - - /** - * Checks that the [ComponentNameMatcher.TASK_BAR] layer is visible during the whole transition - */ - @Presubmit - @Test - open fun taskBarLayerIsVisibleAtStartAndEnd() { - Assume.assumeTrue(flicker.scenario.isTablet) - flicker.taskBarLayerIsVisibleAtStartAndEnd() - } - - /** - * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible during the whole transition - * - * Note: Large screen only - */ - @Presubmit - @Test - open fun taskBarWindowIsAlwaysVisible() { - Assume.assumeTrue(flicker.scenario.isTablet) - flicker.taskBarWindowIsAlwaysVisible() - } - - /** - * Checks that the [ComponentNameMatcher.STATUS_BAR] layer is visible during the whole - * transition - */ - @Presubmit - @Test - open fun statusBarLayerIsVisibleAtStartAndEnd() = flicker.statusBarLayerIsVisibleAtStartAndEnd() - - /** - * Checks the position of the [ComponentNameMatcher.STATUS_BAR] at the start and end of the - * transition - */ - @Presubmit - @Test - open fun statusBarLayerPositionAtStartAndEnd() = flicker.statusBarLayerPositionAtStartAndEnd() - - /** - * Checks that the [ComponentNameMatcher.STATUS_BAR] window is visible during the whole - * transition - */ - @Presubmit - @Test - open fun statusBarWindowIsAlwaysVisible() = flicker.statusBarWindowIsAlwaysVisible() - - /** - * Checks that all layers that are visible on the trace, are visible for at least 2 consecutive - * entries. - */ - @Presubmit - @Test - open fun visibleLayersShownMoreThanOneConsecutiveEntry() { - flicker.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry() } - } - - /** - * Checks that all windows that are visible on the trace, are visible for at least 2 consecutive - * entries. - */ - @Presubmit - @Test - open fun visibleWindowsShownMoreThanOneConsecutiveEntry() { - flicker.assertWm { this.visibleWindowsShownMoreThanOneConsecutiveEntry() } - } -} + override val flicker: FlickerTest, + instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(), + tapl: LauncherInstrumentation = LauncherInstrumentation() +) : BaseBenchmarkTest(flicker, instrumentation, tapl), ICommonAssertions diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt new file mode 100644 index 000000000000..02d9a056afbf --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker + +import android.platform.test.annotations.Presubmit +import android.tools.common.traces.component.ComponentNameMatcher +import android.tools.device.flicker.legacy.FlickerTest +import com.android.server.wm.flicker.entireScreenCovered +import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd +import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerIsVisibleAtStartAndEnd +import com.android.server.wm.flicker.statusBarLayerPositionAtStartAndEnd +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.taskBarLayerIsVisibleAtStartAndEnd +import com.android.server.wm.flicker.taskBarWindowIsAlwaysVisible +import org.junit.Assume +import org.junit.Test + +interface ICommonAssertions { + val flicker: FlickerTest + + /** Checks that all parts of the screen are covered during the transition */ + @Presubmit @Test fun entireScreenCovered() = flicker.entireScreenCovered() + + /** + * Checks that the [ComponentNameMatcher.NAV_BAR] layer is visible during the whole transition + */ + @Presubmit + @Test + fun navBarLayerIsVisibleAtStartAndEnd() { + Assume.assumeFalse(flicker.scenario.isTablet) + flicker.navBarLayerIsVisibleAtStartAndEnd() + } + + /** + * Checks the position of the [ComponentNameMatcher.NAV_BAR] at the start and end of the + * transition + */ + @Presubmit + @Test + fun navBarLayerPositionAtStartAndEnd() { + Assume.assumeFalse(flicker.scenario.isTablet) + flicker.navBarLayerPositionAtStartAndEnd() + } + + /** + * Checks that the [ComponentNameMatcher.NAV_BAR] window is visible during the whole transition + * + * Note: Phones only + */ + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() { + Assume.assumeFalse(flicker.scenario.isTablet) + flicker.navBarWindowIsAlwaysVisible() + } + + /** + * Checks that the [ComponentNameMatcher.TASK_BAR] layer is visible during the whole transition + */ + @Presubmit + @Test + fun taskBarLayerIsVisibleAtStartAndEnd() { + Assume.assumeTrue(flicker.scenario.isTablet) + flicker.taskBarLayerIsVisibleAtStartAndEnd() + } + + /** + * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible during the whole transition + * + * Note: Large screen only + */ + @Presubmit + @Test + fun taskBarWindowIsAlwaysVisible() { + Assume.assumeTrue(flicker.scenario.isTablet) + flicker.taskBarWindowIsAlwaysVisible() + } + + /** + * Checks that the [ComponentNameMatcher.STATUS_BAR] layer is visible during the whole + * transition + */ + @Presubmit + @Test + fun statusBarLayerIsVisibleAtStartAndEnd() = flicker.statusBarLayerIsVisibleAtStartAndEnd() + + /** + * Checks the position of the [ComponentNameMatcher.STATUS_BAR] at the start and end of the + * transition + */ + @Presubmit + @Test + fun statusBarLayerPositionAtStartAndEnd() = flicker.statusBarLayerPositionAtStartAndEnd() + + /** + * Checks that the [ComponentNameMatcher.STATUS_BAR] window is visible during the whole + * transition + */ + @Presubmit @Test fun statusBarWindowIsAlwaysVisible() = flicker.statusBarWindowIsAlwaysVisible() + + /** + * Checks that all layers that are visible on the trace, are visible for at least 2 consecutive + * entries. + */ + @Presubmit + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() { + flicker.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry() } + } + + /** + * Checks that all windows that are visible on the trace, are visible for at least 2 consecutive + * entries. + */ + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + flicker.assertWm { this.visibleWindowsShownMoreThanOneConsecutiveEntry() } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt index d53eac073e6b..b7e73ad11c9f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt @@ -26,6 +26,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd import com.android.wm.shell.flicker.appWindowIsVisibleAtStart @@ -34,6 +35,7 @@ import com.android.wm.shell.flicker.layerKeepVisible import com.android.wm.shell.flicker.splitAppLayerBoundsKeepVisible import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart +import com.android.wm.shell.flicker.splitscreen.benchmark.CopyContentInSplitBenchmark import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -49,29 +51,19 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CopyContentInSplit(flicker: FlickerTest) : SplitScreenBase(flicker) { - private val textEditApp = SplitScreenUtils.getIme(instrumentation) - private val MagnifierLayer = ComponentNameMatcher("", "magnifier surface bbq wrapper#") - private val PopupWindowLayer = ComponentNameMatcher("", "PopupWindow:") - +class CopyContentInSplit(override val flicker: FlickerTest) : + CopyContentInSplitBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { - super.transition(this) - setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp) } - transitions { - SplitScreenUtils.copyContentInSplit( - instrumentation, - device, - primaryApp, - textEditApp - ) - } + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) } @IwTest(focusArea = "sysui") @Presubmit @Test - fun cujCompleted() { + override fun cujCompleted() { flicker.appWindowIsVisibleAtStart(primaryApp) flicker.appWindowIsVisibleAtStart(textEditApp) flicker.splitScreenDividerIsVisibleAtStart() @@ -128,8 +120,8 @@ class CopyContentInSplit(flicker: FlickerTest) : SplitScreenBase(flicker) { ComponentNameMatcher.SNAPSHOT, ComponentNameMatcher.IME_SNAPSHOT, EdgeExtensionComponentMatcher(), - MagnifierLayer, - PopupWindowLayer + magnifierLayer, + popupWindowLayer ) ) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt index 1b55f3975e1c..3fd6d17f27cb 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt @@ -17,22 +17,21 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest -import android.platform.test.annotations.IwTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory import android.tools.device.helpers.WindowUtils import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.appWindowBecomesInvisible import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd import com.android.wm.shell.flicker.layerBecomesInvisible import com.android.wm.shell.flicker.layerIsVisibleAtEnd import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesInvisible -import com.android.wm.shell.flicker.splitScreenDismissed import com.android.wm.shell.flicker.splitScreenDividerBecomesInvisible +import com.android.wm.shell.flicker.splitscreen.benchmark.DismissSplitScreenByDividerBenchmark import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -48,37 +47,16 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class DismissSplitScreenByDivider(flicker: FlickerTest) : SplitScreenBase(flicker) { +class DismissSplitScreenByDivider(override val flicker: FlickerTest) : + DismissSplitScreenByDividerBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { - super.transition(this) - setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) } - transitions { - if (tapl.isTablet) { - SplitScreenUtils.dragDividerToDismissSplit( - device, - wmHelper, - dragToRight = false, - dragToBottom = true - ) - } else { - SplitScreenUtils.dragDividerToDismissSplit( - device, - wmHelper, - dragToRight = true, - dragToBottom = true - ) - } - wmHelper.StateSyncBuilder().withFullScreenApp(secondaryApp).waitForAndVerify() - } + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) } - @IwTest(focusArea = "sysui") - @Presubmit - @Test - fun cujCompleted() = flicker.splitScreenDismissed(primaryApp, secondaryApp, toHome = false) - @Presubmit @Test fun splitScreenDividerBecomesInvisible() = flicker.splitScreenDividerBecomesInvisible() @@ -176,12 +154,4 @@ class DismissSplitScreenByDivider(flicker: FlickerTest) : SplitScreenBase(flicke @Test override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } - } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt index 2e81b30d2e9a..e05b22141e4d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt @@ -17,18 +17,18 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest -import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.appWindowBecomesInvisible import com.android.wm.shell.flicker.layerBecomesInvisible import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesInvisible -import com.android.wm.shell.flicker.splitScreenDismissed import com.android.wm.shell.flicker.splitScreenDividerBecomesInvisible +import com.android.wm.shell.flicker.splitscreen.benchmark.DismissSplitScreenByGoHomeBenchmark import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -44,21 +44,14 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class DismissSplitScreenByGoHome(flicker: FlickerTest) : SplitScreenBase(flicker) { - +class DismissSplitScreenByGoHome(override val flicker: FlickerTest) : + DismissSplitScreenByGoHomeBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { - super.transition(this) - setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) } - transitions { - tapl.goHome() - wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() - } + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) } - @IwTest(focusArea = "sysui") - @Presubmit - @Test - fun cujCompleted() = flicker.splitScreenDismissed(primaryApp, secondaryApp, toHome = true) @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt index 5180791276a2..0b0a3dad320b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt @@ -24,6 +24,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd import com.android.wm.shell.flicker.appWindowIsVisibleAtStart @@ -32,8 +33,7 @@ import com.android.wm.shell.flicker.layerKeepVisible import com.android.wm.shell.flicker.splitAppLayerBoundsChanges import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart -import org.junit.Assume -import org.junit.Before +import com.android.wm.shell.flicker.splitscreen.benchmark.DragDividerToResizeBenchmark import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -49,24 +49,19 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class DragDividerToResize(flicker: FlickerTest) : SplitScreenBase(flicker) { - +class DragDividerToResize(override val flicker: FlickerTest) : + DragDividerToResizeBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { - super.transition(this) - setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) } - transitions { SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper) } + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) } - @Before - fun before() { - Assume.assumeTrue(tapl.isTablet || !flicker.scenario.isLandscapeOrSeascapeAtStart) - } - @IwTest(focusArea = "sysui") @Presubmit @Test - fun cujCompleted() { + override fun cujCompleted() { flicker.appWindowIsVisibleAtStart(primaryApp) flicker.appWindowIsVisibleAtStart(secondaryApp) flicker.splitScreenDividerIsVisibleAtStart() @@ -74,9 +69,6 @@ class DragDividerToResize(flicker: FlickerTest) : SplitScreenBase(flicker) { flicker.appWindowIsVisibleAtEnd(primaryApp) flicker.appWindowIsVisibleAtEnd(secondaryApp) flicker.splitScreenDividerIsVisibleAtEnd() - - // TODO(b/246490534): Add validation for resized app after withAppTransitionIdle is - // robust enough to get the correct end state. } @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt index 69da1e29a19c..e55868675da7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest -import android.platform.test.annotations.IwTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.common.NavBar @@ -26,6 +25,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT import com.android.wm.shell.flicker.appWindowBecomesVisible import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd @@ -34,9 +34,7 @@ import com.android.wm.shell.flicker.layerIsVisibleAtEnd import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible -import com.android.wm.shell.flicker.splitScreenEntered -import org.junit.Assume -import org.junit.Before +import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromAllAppsBenchmark import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -53,40 +51,15 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterSplitScreenByDragFromAllApps(flicker: FlickerTest) : SplitScreenBase(flicker) { - - @Before - fun before() { - Assume.assumeTrue(tapl.isTablet) - } - +class EnterSplitScreenByDragFromAllApps(override val flicker: FlickerTest) : + EnterSplitScreenByDragFromAllAppsBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { - super.transition(this) - setup { - tapl.goHome() - primaryApp.launchViaIntent(wmHelper) - } - transitions { - tapl.launchedAppState.taskbar - .openAllApps() - .getAppIcon(secondaryApp.appName) - .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`) - SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) - } + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) } - @IwTest(focusArea = "sysui") - @Presubmit - @Test - fun cujCompleted() = - flicker.splitScreenEntered( - primaryApp, - secondaryApp, - fromOtherApp = false, - appExistAtStart = false - ) - @FlakyTest(bugId = 245472831) @Test fun splitScreenDividerBecomesVisible() { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt index 1773846c18e9..ab8ecc54e71c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest -import android.platform.test.annotations.IwTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.common.NavBar @@ -26,6 +25,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd import com.android.wm.shell.flicker.layerBecomesVisible @@ -33,9 +33,7 @@ import com.android.wm.shell.flicker.layerIsVisibleAtEnd import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible -import com.android.wm.shell.flicker.splitScreenEntered -import org.junit.Assume -import org.junit.Before +import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromNotificationBenchmark import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -52,39 +50,16 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterSplitScreenByDragFromNotification(flicker: FlickerTest) : SplitScreenBase(flicker) { - - private val sendNotificationApp = SplitScreenUtils.getSendNotification(instrumentation) - - @Before - fun before() { - Assume.assumeTrue(tapl.isTablet) - } - +class EnterSplitScreenByDragFromNotification(override val flicker: FlickerTest) : + EnterSplitScreenByDragFromNotificationBenchmark(flicker), ICommonAssertions { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { - super.transition(this) - setup { - // Send a notification - sendNotificationApp.launchViaIntent(wmHelper) - sendNotificationApp.postNotification(wmHelper) - tapl.goHome() - primaryApp.launchViaIntent(wmHelper) - } - transitions { - SplitScreenUtils.dragFromNotificationToSplit(instrumentation, device, wmHelper) - SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, sendNotificationApp) - } - teardown { sendNotificationApp.exit(wmHelper) } + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) } - @IwTest(focusArea = "sysui") - @Presubmit - @Test - fun cujCompleted() = - flicker.splitScreenEntered(primaryApp, sendNotificationApp, fromOtherApp = false) - @FlakyTest(bugId = 245472831) @Test fun splitScreenDividerBecomesVisible() { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt index c1977e9e82f7..516ca97bc531 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest -import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory @@ -25,15 +24,14 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd import com.android.wm.shell.flicker.layerBecomesVisible import com.android.wm.shell.flicker.layerIsVisibleAtEnd import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible -import com.android.wm.shell.flicker.splitScreenEntered -import org.junit.Assume -import org.junit.Before +import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromShortcutBenchmark import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -49,42 +47,16 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterSplitScreenByDragFromShortcut(flicker: FlickerTest) : SplitScreenBase(flicker) { - - @Before - fun before() { - Assume.assumeTrue(tapl.isTablet) - } +class EnterSplitScreenByDragFromShortcut(override val flicker: FlickerTest) : + EnterSplitScreenByDragFromShortcutBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { - super.transition(this) - setup { - tapl.goHome() - SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName) - primaryApp.launchViaIntent(wmHelper) - } - transitions { - tapl.launchedAppState.taskbar - .getAppIcon(secondaryApp.appName) - .openDeepShortcutMenu() - .getMenuItem("Split Screen Secondary Activity") - .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`) - SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) - } + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) } - @IwTest(focusArea = "sysui") - @Presubmit - @Test - fun cujCompleted() = - flicker.splitScreenEntered( - primaryApp, - secondaryApp, - fromOtherApp = false, - appExistAtStart = false - ) - @Presubmit @Test fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt index 3bea66ef0a27..4af7e248b660 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest -import android.platform.test.annotations.IwTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.common.NavBar @@ -26,6 +25,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT import com.android.wm.shell.flicker.appWindowBecomesVisible import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd @@ -34,9 +34,7 @@ import com.android.wm.shell.flicker.layerIsVisibleAtEnd import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible -import com.android.wm.shell.flicker.splitScreenEntered -import org.junit.Assume -import org.junit.Before +import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromTaskbarBenchmark import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -53,41 +51,16 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterSplitScreenByDragFromTaskbar(flicker: FlickerTest) : SplitScreenBase(flicker) { - - @Before - fun before() { - Assume.assumeTrue(tapl.isTablet) - } - +class EnterSplitScreenByDragFromTaskbar(override val flicker: FlickerTest) : + EnterSplitScreenByDragFromTaskbarBenchmark(flicker), ICommonAssertions { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { - super.transition(this) - setup { - tapl.goHome() - SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName) - primaryApp.launchViaIntent(wmHelper) - } - transitions { - tapl.launchedAppState.taskbar - .getAppIcon(secondaryApp.appName) - .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`) - SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) - } + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) } - @IwTest(focusArea = "sysui") - @Presubmit - @Test - fun cujCompleted() = - flicker.splitScreenEntered( - primaryApp, - secondaryApp, - fromOtherApp = false, - appExistAtStart = false - ) - @FlakyTest(bugId = 245472831) @Test fun splitScreenDividerBecomesVisible() { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt index c45387722a49..faad9e82ffef 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt @@ -17,20 +17,20 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest -import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.appWindowBecomesVisible import com.android.wm.shell.flicker.layerBecomesVisible import com.android.wm.shell.flicker.layerIsVisibleAtEnd import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible -import com.android.wm.shell.flicker.splitScreenEntered +import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenFromOverviewBenchmark import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -46,31 +46,15 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterSplitScreenFromOverview(flicker: FlickerTest) : SplitScreenBase(flicker) { +class EnterSplitScreenFromOverview(override val flicker: FlickerTest) : + EnterSplitScreenFromOverviewBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { - super.transition(this) - setup { - primaryApp.launchViaIntent(wmHelper) - secondaryApp.launchViaIntent(wmHelper) - tapl.goHome() - wmHelper - .StateSyncBuilder() - .withAppTransitionIdle() - .withHomeActivityVisible() - .waitForAndVerify() - } - transitions { - SplitScreenUtils.splitFromOverview(tapl, device) - SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) - } + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) } - @IwTest(focusArea = "sysui") - @Presubmit - @Test - fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true) - @Presubmit @Test fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt index 7abdc06820d6..195b73a14a72 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt @@ -20,25 +20,33 @@ import android.content.Context import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest import com.android.server.wm.flicker.helpers.setRotation -import com.android.wm.shell.flicker.BaseTest +import com.android.wm.shell.flicker.BaseBenchmarkTest -abstract class SplitScreenBase(flicker: FlickerTest) : BaseTest(flicker) { +abstract class SplitScreenBase(flicker: FlickerTest) : BaseBenchmarkTest(flicker) { protected val context: Context = instrumentation.context protected val primaryApp = SplitScreenUtils.getPrimary(instrumentation) protected val secondaryApp = SplitScreenUtils.getSecondary(instrumentation) - /** {@inheritDoc} */ - override val transition: FlickerBuilder.() -> Unit - get() = { - setup { - tapl.setEnableRotation(true) - setRotation(flicker.scenario.startRotation) - tapl.setExpectedRotation(flicker.scenario.startRotation.value) - tapl.workspace.switchToOverview().dismissAllTasks() - } - teardown { - primaryApp.exit(wmHelper) - secondaryApp.exit(wmHelper) - } + protected open val defaultSetup: FlickerBuilder.() -> Unit = { + setup { + tapl.setEnableRotation(true) + setRotation(flicker.scenario.startRotation) + tapl.setExpectedRotation(flicker.scenario.startRotation.value) + tapl.workspace.switchToOverview().dismissAllTasks() } + } + + protected open val defaultTeardown: FlickerBuilder.() -> Unit = { + teardown { + primaryApp.exit(wmHelper) + secondaryApp.exit(wmHelper) + } + } + + protected open val withoutTracing: FlickerBuilder.() -> Unit = { + withoutLayerTracing() + withoutWindowManagerTracing() + withoutTransitionTracing() + withoutTransactionsTracing() + } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt index fbb7c7159234..8cf871f88314 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt @@ -20,14 +20,12 @@ import android.platform.test.annotations.IwTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.common.NavBar -import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest import android.tools.device.flicker.legacy.FlickerTestFactory -import android.tools.device.helpers.WindowUtils -import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd import com.android.wm.shell.flicker.appWindowIsVisibleAtStart @@ -36,6 +34,7 @@ import com.android.wm.shell.flicker.layerKeepVisible import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart +import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchAppByDoubleTapDividerBenchmark import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -51,98 +50,19 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class SwitchAppByDoubleTapDivider(flicker: FlickerTest) : SplitScreenBase(flicker) { - +class SwitchAppByDoubleTapDivider(override val flicker: FlickerTest) : + SwitchAppByDoubleTapDividerBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { - super.transition(this) - setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) } - transitions { - SplitScreenUtils.doubleTapDividerToSwitch(device) - wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() - - waitForLayersToSwitch(wmHelper) - waitForWindowsToSwitch(wmHelper) - } + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) } - private fun waitForWindowsToSwitch(wmHelper: WindowManagerStateHelper) { - wmHelper - .StateSyncBuilder() - .add("appWindowsSwitched") { - val primaryAppWindow = - it.wmState.visibleWindows.firstOrNull { window -> - primaryApp.windowMatchesAnyOf(window) - } - ?: return@add false - val secondaryAppWindow = - it.wmState.visibleWindows.firstOrNull { window -> - secondaryApp.windowMatchesAnyOf(window) - } - ?: return@add false - - if (isLandscape(flicker.scenario.endRotation)) { - return@add if (flicker.scenario.isTablet) { - secondaryAppWindow.frame.right <= primaryAppWindow.frame.left - } else { - primaryAppWindow.frame.right <= secondaryAppWindow.frame.left - } - } else { - return@add if (flicker.scenario.isTablet) { - primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top - } else { - primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top - } - } - } - .waitForAndVerify() - } - - private fun waitForLayersToSwitch(wmHelper: WindowManagerStateHelper) { - wmHelper - .StateSyncBuilder() - .add("appLayersSwitched") { - val primaryAppLayer = - it.layerState.visibleLayers.firstOrNull { window -> - primaryApp.layerMatchesAnyOf(window) - } - ?: return@add false - val secondaryAppLayer = - it.layerState.visibleLayers.firstOrNull { window -> - secondaryApp.layerMatchesAnyOf(window) - } - ?: return@add false - - val primaryVisibleRegion = primaryAppLayer.visibleRegion?.bounds ?: return@add false - val secondaryVisibleRegion = - secondaryAppLayer.visibleRegion?.bounds ?: return@add false - - if (isLandscape(flicker.scenario.endRotation)) { - return@add if (flicker.scenario.isTablet) { - secondaryVisibleRegion.right <= primaryVisibleRegion.left - } else { - primaryVisibleRegion.right <= secondaryVisibleRegion.left - } - } else { - return@add if (flicker.scenario.isTablet) { - primaryVisibleRegion.bottom <= secondaryVisibleRegion.top - } else { - primaryVisibleRegion.bottom <= secondaryVisibleRegion.top - } - } - } - .waitForAndVerify() - } - - private fun isLandscape(rotation: Rotation): Boolean { - val displayBounds = WindowUtils.getDisplayBounds(rotation) - return displayBounds.width > displayBounds.height - } - @IwTest(focusArea = "sysui") @Presubmit @Test - fun cujCompleted() { + override fun cujCompleted() { flicker.appWindowIsVisibleAtStart(primaryApp) flicker.appWindowIsVisibleAtStart(secondaryApp) flicker.splitScreenDividerIsVisibleAtStart() @@ -150,9 +70,6 @@ class SwitchAppByDoubleTapDivider(flicker: FlickerTest) : SplitScreenBase(flicke flicker.appWindowIsVisibleAtEnd(primaryApp) flicker.appWindowIsVisibleAtEnd(secondaryApp) flicker.splitScreenDividerIsVisibleAtEnd() - - // TODO(b/246490534): Add validation for switched app after withAppTransitionIdle is - // robust enough to get the correct end state. } @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt index d675bfb0119d..078d95de1dd0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest -import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory @@ -25,11 +24,12 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.appWindowBecomesVisible import com.android.wm.shell.flicker.layerBecomesVisible import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible -import com.android.wm.shell.flicker.splitScreenEntered +import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBackToSplitFromAnotherAppBenchmark import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -45,29 +45,15 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class SwitchBackToSplitFromAnotherApp(flicker: FlickerTest) : SplitScreenBase(flicker) { - val thirdApp = SplitScreenUtils.getNonResizeable(instrumentation) - +class SwitchBackToSplitFromAnotherApp(override val flicker: FlickerTest) : + SwitchBackToSplitFromAnotherAppBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { - super.transition(this) - setup { - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) - - thirdApp.launchViaIntent(wmHelper) - wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify() - } - transitions { - tapl.launchedAppState.quickSwitchToPreviousApp() - SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) - } + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) } - @IwTest(focusArea = "sysui") - @Presubmit - @Test - fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true) - @Presubmit @Test fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt index 9f4cb8c381fc..7c84243e00d7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest -import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory @@ -25,11 +24,12 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.appWindowBecomesVisible import com.android.wm.shell.flicker.layerBecomesVisible import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible -import com.android.wm.shell.flicker.splitScreenEntered +import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBackToSplitFromHomeBenchmark import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -45,28 +45,15 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class SwitchBackToSplitFromHome(flicker: FlickerTest) : SplitScreenBase(flicker) { - +class SwitchBackToSplitFromHome(override val flicker: FlickerTest) : + SwitchBackToSplitFromHomeBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { - super.transition(this) - setup { - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) - - tapl.goHome() - wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() - } - transitions { - tapl.workspace.quickSwitchToPreviousApp() - SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) - } + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) } - @IwTest(focusArea = "sysui") - @Presubmit - @Test - fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true) - @Presubmit @Test fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt index a33d8cab9fbd..7c46d3e099a2 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest -import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory @@ -25,11 +24,12 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.appWindowBecomesVisible import com.android.wm.shell.flicker.layerBecomesVisible import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible -import com.android.wm.shell.flicker.splitScreenEntered +import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBackToSplitFromRecentBenchmark import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -45,28 +45,15 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class SwitchBackToSplitFromRecent(flicker: FlickerTest) : SplitScreenBase(flicker) { - +class SwitchBackToSplitFromRecent(override val flicker: FlickerTest) : + SwitchBackToSplitFromRecentBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { - super.transition(this) - setup { - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) - - tapl.goHome() - wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() - } - transitions { - tapl.workspace.switchToOverview().currentTask.open() - SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) - } + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) } - @IwTest(focusArea = "sysui") - @Presubmit - @Test - fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true) - @Presubmit @Test fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt index 4c96b3a319d5..3b2da8dbcf9f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt @@ -24,6 +24,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT import com.android.wm.shell.flicker.appWindowBecomesInvisible import com.android.wm.shell.flicker.appWindowBecomesVisible @@ -36,6 +37,7 @@ import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd import com.android.wm.shell.flicker.splitAppLayerBoundsSnapToDivider import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart +import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBetweenSplitPairsBenchmark import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -51,32 +53,19 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class SwitchBetweenSplitPairs(flicker: FlickerTest) : SplitScreenBase(flicker) { - private val thirdApp = SplitScreenUtils.getIme(instrumentation) - private val fourthApp = SplitScreenUtils.getSendNotification(instrumentation) - +class SwitchBetweenSplitPairs(override val flicker: FlickerTest) : + SwitchBetweenSplitPairsBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { - super.transition(this) - setup { - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) - SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp) - SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp) - } - transitions { - tapl.launchedAppState.quickSwitchToPreviousApp() - SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) - } - teardown { - thirdApp.exit(wmHelper) - fourthApp.exit(wmHelper) - } + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) } @IwTest(focusArea = "sysui") @Presubmit @Test - fun cujCompleted() { + override fun cujCompleted() { flicker.appWindowIsVisibleAtStart(thirdApp) flicker.appWindowIsVisibleAtStart(fourthApp) flicker.splitScreenDividerIsVisibleAtStart() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt new file mode 100644 index 000000000000..a189a3f67eca --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen.benchmark + +import android.platform.test.annotations.IwTest +import android.platform.test.annotations.Presubmit +import android.tools.common.traces.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.splitscreen.SplitScreenBase +import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class CopyContentInSplitBenchmark(override val flicker: FlickerTest) : + SplitScreenBase(flicker) { + protected val textEditApp = SplitScreenUtils.getIme(instrumentation) + protected val magnifierLayer = ComponentNameMatcher("", "magnifier surface bbq wrapper#") + protected val popupWindowLayer = ComponentNameMatcher("", "PopupWindow:") + protected val thisTransition: FlickerBuilder.() -> Unit + get() = { + setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp) } + transitions { + SplitScreenUtils.copyContentInSplit( + instrumentation, + device, + primaryApp, + textEditApp + ) + } + } + + override val transition: FlickerBuilder.() -> Unit + get() = { + withoutTracing(this) + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + @IwTest(focusArea = "sysui") + @Presubmit + @Test + open fun cujCompleted() { + // The validation of copied text is already done in SplitScreenUtils.copyContentInSplit() + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTest> { + return FlickerTestFactory.nonRotationTests() + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt new file mode 100644 index 000000000000..55ab7b3a30e4 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen.benchmark + +import android.platform.test.annotations.IwTest +import android.platform.test.annotations.Presubmit +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.splitScreenDismissed +import com.android.wm.shell.flicker.splitscreen.SplitScreenBase +import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class DismissSplitScreenByDividerBenchmark(flicker: FlickerTest) : SplitScreenBase(flicker) { + protected val thisTransition: FlickerBuilder.() -> Unit + get() = { + setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) } + transitions { + if (tapl.isTablet) { + SplitScreenUtils.dragDividerToDismissSplit( + device, + wmHelper, + dragToRight = false, + dragToBottom = true + ) + } else { + SplitScreenUtils.dragDividerToDismissSplit( + device, + wmHelper, + dragToRight = true, + dragToBottom = true + ) + } + wmHelper.StateSyncBuilder().withFullScreenApp(secondaryApp).waitForAndVerify() + } + } + + override val transition: FlickerBuilder.() -> Unit + get() = { + withoutTracing(this) + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + @IwTest(focusArea = "sysui") + @Presubmit + @Test + fun cujCompleted() = flicker.splitScreenDismissed(primaryApp, secondaryApp, toHome = false) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTest> { + return FlickerTestFactory.nonRotationTests() + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt new file mode 100644 index 000000000000..c4cfd1add25c --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen.benchmark + +import android.platform.test.annotations.IwTest +import android.platform.test.annotations.Presubmit +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.splitScreenDismissed +import com.android.wm.shell.flicker.splitscreen.SplitScreenBase +import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class DismissSplitScreenByGoHomeBenchmark(override val flicker: FlickerTest) : + SplitScreenBase(flicker) { + protected val thisTransition: FlickerBuilder.() -> Unit + get() = { + setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) } + transitions { + tapl.goHome() + wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() + } + } + + override val transition: FlickerBuilder.() -> Unit + get() = { + withoutTracing(this) + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + @IwTest(focusArea = "sysui") + @Presubmit + @Test + fun cujCompleted() = flicker.splitScreenDismissed(primaryApp, secondaryApp, toHome = true) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTest> { + return FlickerTestFactory.nonRotationTests() + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt new file mode 100644 index 000000000000..146287c21c75 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen.benchmark + +import android.platform.test.annotations.IwTest +import android.platform.test.annotations.Presubmit +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.splitscreen.SplitScreenBase +import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils +import org.junit.Assume +import org.junit.Before +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class DragDividerToResizeBenchmark(override val flicker: FlickerTest) : + SplitScreenBase(flicker) { + protected val thisTransition: FlickerBuilder.() -> Unit + get() = { + setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) } + transitions { SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper) } + } + + override val transition: FlickerBuilder.() -> Unit + get() = { + withoutTracing(this) + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + @Before + fun before() { + Assume.assumeTrue(tapl.isTablet || !flicker.scenario.isLandscapeOrSeascapeAtStart) + } + + @IwTest(focusArea = "sysui") + @Presubmit + @Test + open fun cujCompleted() { + // TODO(b/246490534): Add validation for resized app after withAppTransitionIdle is + // robust enough to get the correct end state. + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTest> { + return FlickerTestFactory.nonRotationTests() + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt new file mode 100644 index 000000000000..cc715021adf4 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen.benchmark + +import android.platform.test.annotations.IwTest +import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.splitScreenEntered +import com.android.wm.shell.flicker.splitscreen.SplitScreenBase +import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils +import org.junit.Assume +import org.junit.Before +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class EnterSplitScreenByDragFromAllAppsBenchmark(override val flicker: FlickerTest) : + SplitScreenBase(flicker) { + + protected val thisTransition: FlickerBuilder.() -> Unit + get() = { + setup { + tapl.goHome() + primaryApp.launchViaIntent(wmHelper) + } + transitions { + tapl.launchedAppState.taskbar + .openAllApps() + .getAppIcon(secondaryApp.appName) + .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`) + SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) + } + } + + override val transition: FlickerBuilder.() -> Unit + get() = { + withoutTracing(this) + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + @Before + fun before() { + Assume.assumeTrue(tapl.isTablet) + } + + @IwTest(focusArea = "sysui") + @Presubmit + @Test + fun cujCompleted() = + flicker.splitScreenEntered( + primaryApp, + secondaryApp, + fromOtherApp = false, + appExistAtStart = false + ) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTest> { + return FlickerTestFactory.nonRotationTests( + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + ) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt new file mode 100644 index 000000000000..de78f09d3ef4 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen.benchmark + +import android.platform.test.annotations.IwTest +import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.splitScreenEntered +import com.android.wm.shell.flicker.splitscreen.SplitScreenBase +import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils +import org.junit.Assume +import org.junit.Before +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class EnterSplitScreenByDragFromNotificationBenchmark(override val flicker: FlickerTest) : + SplitScreenBase(flicker) { + protected val sendNotificationApp = SplitScreenUtils.getSendNotification(instrumentation) + protected val thisTransition: FlickerBuilder.() -> Unit + get() = { + setup { + // Send a notification + sendNotificationApp.launchViaIntent(wmHelper) + sendNotificationApp.postNotification(wmHelper) + tapl.goHome() + primaryApp.launchViaIntent(wmHelper) + } + transitions { + SplitScreenUtils.dragFromNotificationToSplit(instrumentation, device, wmHelper) + SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, sendNotificationApp) + } + teardown { sendNotificationApp.exit(wmHelper) } + } + + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit + get() = { + withoutTracing(this) + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + @IwTest(focusArea = "sysui") + @Presubmit + @Test + fun cujCompleted() = + flicker.splitScreenEntered(primaryApp, sendNotificationApp, fromOtherApp = false) + + @Before + fun before() { + Assume.assumeTrue(tapl.isTablet) + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTest> { + return FlickerTestFactory.nonRotationTests( + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + ) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt new file mode 100644 index 000000000000..a29eb4085e54 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen.benchmark + +import android.platform.test.annotations.IwTest +import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.splitScreenEntered +import com.android.wm.shell.flicker.splitscreen.SplitScreenBase +import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils +import org.junit.Assume +import org.junit.Before +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class EnterSplitScreenByDragFromShortcutBenchmark(flicker: FlickerTest) : + SplitScreenBase(flicker) { + @Before + fun before() { + Assume.assumeTrue(tapl.isTablet) + } + + protected val thisTransition: FlickerBuilder.() -> Unit = { + setup { + tapl.goHome() + SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName) + primaryApp.launchViaIntent(wmHelper) + } + transitions { + tapl.launchedAppState.taskbar + .getAppIcon(secondaryApp.appName) + .openDeepShortcutMenu() + .getMenuItem("Split Screen Secondary Activity") + .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`) + SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) + } + } + + override val transition: FlickerBuilder.() -> Unit + get() = { + withoutTracing(this) + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + @IwTest(focusArea = "sysui") + @Presubmit + @Test + fun cujCompleted() = + flicker.splitScreenEntered( + primaryApp, + secondaryApp, + fromOtherApp = false, + appExistAtStart = false + ) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTest> { + return FlickerTestFactory.nonRotationTests( + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + ) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt new file mode 100644 index 000000000000..b2395cafafc1 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen.benchmark + +import android.platform.test.annotations.IwTest +import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.splitScreenEntered +import com.android.wm.shell.flicker.splitscreen.SplitScreenBase +import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils +import org.junit.Assume +import org.junit.Before +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class EnterSplitScreenByDragFromTaskbarBenchmark(override val flicker: FlickerTest) : + SplitScreenBase(flicker) { + protected val thisTransition: FlickerBuilder.() -> Unit + get() = { + setup { + tapl.goHome() + SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName) + primaryApp.launchViaIntent(wmHelper) + } + transitions { + tapl.launchedAppState.taskbar + .getAppIcon(secondaryApp.appName) + .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`) + SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) + } + } + + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit + get() = { + withoutTracing(this) + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + @IwTest(focusArea = "sysui") + @Presubmit + @Test + fun cujCompleted() = + flicker.splitScreenEntered( + primaryApp, + secondaryApp, + fromOtherApp = false, + appExistAtStart = false + ) + + @Before + fun before() { + Assume.assumeTrue(tapl.isTablet) + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTest> { + return FlickerTestFactory.nonRotationTests( + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + ) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt new file mode 100644 index 000000000000..e1d85d0a4371 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen.benchmark + +import android.platform.test.annotations.IwTest +import android.platform.test.annotations.Presubmit +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.splitScreenEntered +import com.android.wm.shell.flicker.splitscreen.SplitScreenBase +import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class EnterSplitScreenFromOverviewBenchmark(override val flicker: FlickerTest) : + SplitScreenBase(flicker) { + protected val thisTransition: FlickerBuilder.() -> Unit + get() = { + setup { + primaryApp.launchViaIntent(wmHelper) + secondaryApp.launchViaIntent(wmHelper) + tapl.goHome() + wmHelper + .StateSyncBuilder() + .withAppTransitionIdle() + .withHomeActivityVisible() + .waitForAndVerify() + } + transitions { + SplitScreenUtils.splitFromOverview(tapl, device) + SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) + } + } + + override val transition: FlickerBuilder.() -> Unit + get() = { + withoutTracing(this) + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + @IwTest(focusArea = "sysui") + @Presubmit + @Test + fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTest> { + return FlickerTestFactory.nonRotationTests() + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt new file mode 100644 index 000000000000..ba8c46091808 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen.benchmark + +import android.platform.test.annotations.IwTest +import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.helpers.WindowUtils +import android.tools.device.traces.parsers.WindowManagerStateHelper +import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.splitscreen.SplitScreenBase +import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class SwitchAppByDoubleTapDividerBenchmark(override val flicker: FlickerTest) : + SplitScreenBase(flicker) { + protected val thisTransition: FlickerBuilder.() -> Unit + get() = { + setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) } + transitions { + SplitScreenUtils.doubleTapDividerToSwitch(device) + wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() + + waitForLayersToSwitch(wmHelper) + waitForWindowsToSwitch(wmHelper) + } + } + + override val transition: FlickerBuilder.() -> Unit + get() = { + withoutTracing(this) + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + private fun waitForWindowsToSwitch(wmHelper: WindowManagerStateHelper) { + wmHelper + .StateSyncBuilder() + .add("appWindowsSwitched") { + val primaryAppWindow = + it.wmState.visibleWindows.firstOrNull { window -> + primaryApp.windowMatchesAnyOf(window) + } + ?: return@add false + val secondaryAppWindow = + it.wmState.visibleWindows.firstOrNull { window -> + secondaryApp.windowMatchesAnyOf(window) + } + ?: return@add false + + if (isLandscape(flicker.scenario.endRotation)) { + return@add if (flicker.scenario.isTablet) { + secondaryAppWindow.frame.right <= primaryAppWindow.frame.left + } else { + primaryAppWindow.frame.right <= secondaryAppWindow.frame.left + } + } else { + return@add if (flicker.scenario.isTablet) { + primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top + } else { + primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top + } + } + } + .waitForAndVerify() + } + + private fun waitForLayersToSwitch(wmHelper: WindowManagerStateHelper) { + wmHelper + .StateSyncBuilder() + .add("appLayersSwitched") { + val primaryAppLayer = + it.layerState.visibleLayers.firstOrNull { window -> + primaryApp.layerMatchesAnyOf(window) + } + ?: return@add false + val secondaryAppLayer = + it.layerState.visibleLayers.firstOrNull { window -> + secondaryApp.layerMatchesAnyOf(window) + } + ?: return@add false + + val primaryVisibleRegion = primaryAppLayer.visibleRegion?.bounds ?: return@add false + val secondaryVisibleRegion = + secondaryAppLayer.visibleRegion?.bounds ?: return@add false + + if (isLandscape(flicker.scenario.endRotation)) { + return@add if (flicker.scenario.isTablet) { + secondaryVisibleRegion.right <= primaryVisibleRegion.left + } else { + primaryVisibleRegion.right <= secondaryVisibleRegion.left + } + } else { + return@add if (flicker.scenario.isTablet) { + primaryVisibleRegion.bottom <= secondaryVisibleRegion.top + } else { + primaryVisibleRegion.bottom <= secondaryVisibleRegion.top + } + } + } + .waitForAndVerify() + } + + private fun isLandscape(rotation: Rotation): Boolean { + val displayBounds = WindowUtils.getDisplayBounds(rotation) + return displayBounds.width > displayBounds.height + } + + @IwTest(focusArea = "sysui") + @Presubmit + @Test + open fun cujCompleted() { + // TODO(b/246490534): Add validation for switched app after withAppTransitionIdle is + // robust enough to get the correct end state. + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTest> { + return FlickerTestFactory.nonRotationTests( + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + ) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt new file mode 100644 index 000000000000..bbb2edc621e2 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen.benchmark + +import android.platform.test.annotations.IwTest +import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.splitScreenEntered +import com.android.wm.shell.flicker.splitscreen.SplitScreenBase +import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class SwitchBackToSplitFromAnotherAppBenchmark(override val flicker: FlickerTest) : + SplitScreenBase(flicker) { + private val thirdApp = SplitScreenUtils.getNonResizeable(instrumentation) + + protected val thisTransition: FlickerBuilder.() -> Unit + get() = { + setup { + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) + + thirdApp.launchViaIntent(wmHelper) + wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify() + } + transitions { + tapl.launchedAppState.quickSwitchToPreviousApp() + SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) + } + } + + override val transition: FlickerBuilder.() -> Unit + get() = { + withoutTracing(this) + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + @IwTest(focusArea = "sysui") + @Presubmit + @Test + fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTest> { + return FlickerTestFactory.nonRotationTests( + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + ) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt new file mode 100644 index 000000000000..fa382932eb88 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen.benchmark + +import android.platform.test.annotations.IwTest +import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.splitScreenEntered +import com.android.wm.shell.flicker.splitscreen.SplitScreenBase +import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class SwitchBackToSplitFromHomeBenchmark(override val flicker: FlickerTest) : + SplitScreenBase(flicker) { + protected val thisTransition: FlickerBuilder.() -> Unit + get() = { + setup { + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) + + tapl.goHome() + wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() + } + transitions { + tapl.workspace.quickSwitchToPreviousApp() + SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) + } + } + + override val transition: FlickerBuilder.() -> Unit + get() = { + withoutTracing(this) + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + @IwTest(focusArea = "sysui") + @Presubmit + @Test + fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTest> { + return FlickerTestFactory.nonRotationTests( + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + ) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt new file mode 100644 index 000000000000..1064bd93e3c1 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen.benchmark + +import android.platform.test.annotations.IwTest +import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.splitScreenEntered +import com.android.wm.shell.flicker.splitscreen.SplitScreenBase +import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class SwitchBackToSplitFromRecentBenchmark(override val flicker: FlickerTest) : + SplitScreenBase(flicker) { + protected val thisTransition: FlickerBuilder.() -> Unit + get() = { + setup { + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) + + tapl.goHome() + wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() + } + transitions { + tapl.workspace.switchToOverview().currentTask.open() + SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) + } + } + + override val transition: FlickerBuilder.() -> Unit + get() = { + withoutTracing(this) + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + @IwTest(focusArea = "sysui") + @Presubmit + @Test + fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTest> { + return FlickerTestFactory.nonRotationTests( + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + ) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt new file mode 100644 index 000000000000..8f4393f8d20d --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen.benchmark + +import android.platform.test.annotations.IwTest +import android.platform.test.annotations.Presubmit +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.splitscreen.SplitScreenBase +import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class SwitchBetweenSplitPairsBenchmark(override val flicker: FlickerTest) : + SplitScreenBase(flicker) { + protected val thirdApp = SplitScreenUtils.getIme(instrumentation) + protected val fourthApp = SplitScreenUtils.getSendNotification(instrumentation) + + protected val thisTransition: FlickerBuilder.() -> Unit + get() = { + setup { + SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) + SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp) + SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp) + } + transitions { + tapl.launchedAppState.quickSwitchToPreviousApp() + SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) + } + teardown { + thirdApp.exit(wmHelper) + fourthApp.exit(wmHelper) + } + } + + override val transition: FlickerBuilder.() -> Unit + get() = { + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + @IwTest(focusArea = "sysui") @Presubmit @Test open fun cujCompleted() {} + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTest> { + return FlickerTestFactory.nonRotationTests() + } + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java index de967bfa288b..afec1ee12341 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java @@ -33,6 +33,7 @@ import android.content.pm.ShortcutInfo; import android.content.res.Resources; import android.graphics.drawable.Icon; import android.os.Bundle; +import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -42,6 +43,7 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.R; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.bubbles.BubbleInfo; import org.junit.Before; import org.junit.Test; @@ -77,7 +79,7 @@ public class BubbleTest extends ShellTestCase { Intent target = new Intent(mContext, BubblesTestActivity.class); Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder( PendingIntent.getActivity(mContext, 0, target, PendingIntent.FLAG_MUTABLE), - Icon.createWithResource(mContext, R.drawable.bubble_ic_create_bubble)) + Icon.createWithResource(mContext, R.drawable.bubble_ic_create_bubble)) .build(); when(mSbn.getNotification()).thenReturn(mNotif); when(mNotif.getBubbleMetadata()).thenReturn(metadata); @@ -179,6 +181,34 @@ public class BubbleTest extends ShellTestCase { assertThat(bubble.isConversation()).isFalse(); } + @Test + public void testBubbleAsBubbleBarBubble_withShortcut() { + Bubble bubble = createBubbleWithShortcut(); + BubbleInfo bubbleInfo = bubble.asBubbleBarBubble(); + + assertThat(bubble.getShortcutInfo()).isNotNull(); + assertThat(bubbleInfo.getShortcutId()).isNotNull(); + assertThat(bubbleInfo.getShortcutId()).isEqualTo(bubble.getShortcutId()); + assertThat(bubbleInfo.getKey()).isEqualTo(bubble.getKey()); + assertThat(bubbleInfo.getUserId()).isEqualTo(bubble.getUser().getIdentifier()); + assertThat(bubbleInfo.getPackageName()).isEqualTo(bubble.getPackageName()); + } + + @Test + public void testBubbleAsBubbleBarBubble_withoutShortcut() { + Intent intent = new Intent(mContext, BubblesTestActivity.class); + intent.setPackage(mContext.getPackageName()); + Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1 /* userId */), + null /* icon */, mMainExecutor); + BubbleInfo bubbleInfo = bubble.asBubbleBarBubble(); + + assertThat(bubble.getShortcutInfo()).isNull(); + assertThat(bubbleInfo.getShortcutId()).isNull(); + assertThat(bubbleInfo.getKey()).isEqualTo(bubble.getKey()); + assertThat(bubbleInfo.getUserId()).isEqualTo(bubble.getUser().getIdentifier()); + assertThat(bubbleInfo.getPackageName()).isEqualTo(bubble.getPackageName()); + } + private Bubble createBubbleWithShortcut() { ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext) .setId("mockShortcutId") diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java index 1347e061eb45..60ee918ee545 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java @@ -112,9 +112,9 @@ public class TaskStackListenerImplTest extends ShellTestCase { @Test public void testOnTaskProfileLocked() { ActivityManager.RunningTaskInfo info = mock(ActivityManager.RunningTaskInfo.class); - mImpl.onTaskProfileLocked(info); - verify(mCallback).onTaskProfileLocked(eq(info)); - verify(mOtherCallback).onTaskProfileLocked(eq(info)); + mImpl.onTaskProfileLocked(info, 0); + verify(mCallback).onTaskProfileLocked(eq(info), eq(0)); + verify(mOtherCallback).onTaskProfileLocked(eq(info), eq(0)); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java index 63de74fa3b05..d6387ee5ae13 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS; +import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_NONE; @@ -82,6 +83,8 @@ import java.util.Arrays; @RunWith(AndroidTestingRunner.class) public class DesktopModeControllerTest extends ShellTestCase { + private static final int SECOND_DISPLAY = 2; + @Mock private ShellController mShellController; @Mock @@ -248,22 +251,22 @@ public class DesktopModeControllerTest extends ShellTestCase { public void testShowDesktopApps_allAppsInvisible_bringsToFront() { // Set up two active tasks on desktop, task2 is on top of task1. RunningTaskInfo freeformTask1 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(freeformTask1.taskId); + mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, freeformTask1.taskId); mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask1.taskId); mDesktopModeTaskRepository.updateVisibleFreeformTasks( - freeformTask1.taskId, false /* visible */); + DEFAULT_DISPLAY, freeformTask1.taskId, false /* visible */); RunningTaskInfo freeformTask2 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(freeformTask2.taskId); + mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, freeformTask2.taskId); mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask2.taskId); mDesktopModeTaskRepository.updateVisibleFreeformTasks( - freeformTask2.taskId, false /* visible */); + DEFAULT_DISPLAY, freeformTask2.taskId, false /* visible */); when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask1.taskId)).thenReturn( freeformTask1); when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask2.taskId)).thenReturn( freeformTask2); // Run show desktop apps logic - mController.showDesktopApps(); + mController.showDesktopApps(DEFAULT_DISPLAY); final WindowContainerTransaction wct = getBringAppsToFrontTransaction(); // Check wct has reorder calls @@ -283,17 +286,19 @@ public class DesktopModeControllerTest extends ShellTestCase { @Test public void testShowDesktopApps_appsAlreadyVisible_bringsToFront() { final RunningTaskInfo task1 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(task1.taskId); + mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId); mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, true /* visible */); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId, + true /* visible */); when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1); final RunningTaskInfo task2 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(task2.taskId); + mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId); mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, true /* visible */); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId, + true /* visible */); when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2); - mController.showDesktopApps(); + mController.showDesktopApps(DEFAULT_DISPLAY); final WindowContainerTransaction wct = getBringAppsToFrontTransaction(); // Check wct has reorder calls @@ -312,17 +317,19 @@ public class DesktopModeControllerTest extends ShellTestCase { @Test public void testShowDesktopApps_someAppsInvisible_reordersAll() { final RunningTaskInfo task1 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(task1.taskId); + mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId); mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, false /* visible */); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId, + false /* visible */); when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1); final RunningTaskInfo task2 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(task2.taskId); + mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId); mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, true /* visible */); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId, + true /* visible */); when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2); - mController.showDesktopApps(); + mController.showDesktopApps(DEFAULT_DISPLAY); final WindowContainerTransaction wct = getBringAppsToFrontTransaction(); // Both tasks should be reordered to top, even if one was already visible. @@ -336,38 +343,87 @@ public class DesktopModeControllerTest extends ShellTestCase { } @Test + public void testShowDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay() { + RunningTaskInfo taskDefaultDisplay = createFreeformTask(DEFAULT_DISPLAY); + mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, taskDefaultDisplay.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskDefaultDisplay.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks( + DEFAULT_DISPLAY, taskDefaultDisplay.taskId, false /* visible */); + when(mShellTaskOrganizer.getRunningTaskInfo(taskDefaultDisplay.taskId)).thenReturn( + taskDefaultDisplay); + + RunningTaskInfo taskSecondDisplay = createFreeformTask(SECOND_DISPLAY); + mDesktopModeTaskRepository.addActiveTask(SECOND_DISPLAY, taskSecondDisplay.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskSecondDisplay.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks( + SECOND_DISPLAY, taskSecondDisplay.taskId, false /* visible */); + when(mShellTaskOrganizer.getRunningTaskInfo(taskSecondDisplay.taskId)).thenReturn( + taskSecondDisplay); + + mController.showDesktopApps(DEFAULT_DISPLAY); + + WindowContainerTransaction wct = getBringAppsToFrontTransaction(); + assertThat(wct.getHierarchyOps()).hasSize(1); + HierarchyOp op = wct.getHierarchyOps().get(0); + assertThat(op.getContainer()).isEqualTo(taskDefaultDisplay.token.asBinder()); + } + + @Test public void testGetVisibleTaskCount_noTasks_returnsZero() { - assertThat(mController.getVisibleTaskCount()).isEqualTo(0); + assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0); } @Test public void testGetVisibleTaskCount_twoTasks_bothVisible_returnsTwo() { RunningTaskInfo task1 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(task1.taskId); + mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId); mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, true /* visible */); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId, + true /* visible */); RunningTaskInfo task2 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(task2.taskId); + mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId); mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, true /* visible */); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId, + true /* visible */); - assertThat(mController.getVisibleTaskCount()).isEqualTo(2); + assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2); } @Test public void testGetVisibleTaskCount_twoTasks_oneVisible_returnsOne() { RunningTaskInfo task1 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(task1.taskId); + mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId); mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, true /* visible */); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId, + true /* visible */); RunningTaskInfo task2 = createFreeformTask(); - mDesktopModeTaskRepository.addActiveTask(task2.taskId); + mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId); mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId); - mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, false /* visible */); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId, + false /* visible */); - assertThat(mController.getVisibleTaskCount()).isEqualTo(1); + assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1); + } + + @Test + public void testGetVisibleTaskCount_twoTasksVisibleOnDifferentDisplays_returnsOne() { + RunningTaskInfo taskDefaultDisplay = createFreeformTask(); + mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, taskDefaultDisplay.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskDefaultDisplay.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, + taskDefaultDisplay.taskId, + true /* visible */); + + RunningTaskInfo taskSecondDisplay = createFreeformTask(); + mDesktopModeTaskRepository.addActiveTask(SECOND_DISPLAY, taskSecondDisplay.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskSecondDisplay.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(SECOND_DISPLAY, + taskSecondDisplay.taskId, + true /* visible */); + + assertThat(mController.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt index 45cb3a062cc5..3bc2f0e8674e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt @@ -17,10 +17,12 @@ package com.android.wm.shell.desktopmode import android.testing.AndroidTestingRunner +import android.view.Display.DEFAULT_DISPLAY import androidx.test.filters.SmallTest import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestShellExecutor import com.google.common.truth.Truth.assertThat +import junit.framework.Assert.fail import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -41,8 +43,8 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() { val listener = TestListener() repo.addActiveTaskListener(listener) - repo.addActiveTask(1) - assertThat(listener.activeTaskChangedCalls).isEqualTo(1) + repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1) + assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(1) assertThat(repo.isActiveTask(1)).isTrue() } @@ -51,9 +53,9 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() { val listener = TestListener() repo.addActiveTaskListener(listener) - repo.addActiveTask(1) - repo.addActiveTask(1) - assertThat(listener.activeTaskChangedCalls).isEqualTo(1) + repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1) + repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1) + assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(1) } @Test @@ -61,9 +63,22 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() { val listener = TestListener() repo.addActiveTaskListener(listener) - repo.addActiveTask(1) - repo.addActiveTask(2) - assertThat(listener.activeTaskChangedCalls).isEqualTo(2) + repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1) + repo.addActiveTask(DEFAULT_DISPLAY, taskId = 2) + assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(2) + } + + @Test + fun addActiveTask_multipleDisplays_notifiesCorrectListener() { + val listener = TestListener() + repo.addActiveTaskListener(listener) + + repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1) + repo.addActiveTask(DEFAULT_DISPLAY, taskId = 2) + repo.addActiveTask(SECOND_DISPLAY, taskId = 3) + + assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(2) + assertThat(listener.activeChangesOnSecondaryDisplay).isEqualTo(1) } @Test @@ -71,10 +86,10 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() { val listener = TestListener() repo.addActiveTaskListener(listener) - repo.addActiveTask(1) + repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1) repo.removeActiveTask(1) // Notify once for add and once for remove - assertThat(listener.activeTaskChangedCalls).isEqualTo(2) + assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(2) assertThat(repo.isActiveTask(1)).isFalse() } @@ -83,7 +98,17 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() { val listener = TestListener() repo.addActiveTaskListener(listener) repo.removeActiveTask(99) - assertThat(listener.activeTaskChangedCalls).isEqualTo(0) + assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(0) + } + + @Test + fun remoteActiveTask_listenerForOtherDisplayNotNotified() { + val listener = TestListener() + repo.addActiveTaskListener(listener) + repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1) + repo.removeActiveTask(1) + assertThat(listener.activeChangesOnSecondaryDisplay).isEqualTo(0) + assertThat(repo.isActiveTask(1)).isFalse() } @Test @@ -93,14 +118,27 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() { @Test fun addListener_notifiesVisibleFreeformTask() { - repo.updateVisibleFreeformTasks(1, true) + repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true) val listener = TestVisibilityListener() val executor = TestShellExecutor() repo.addVisibleTasksListener(listener, executor) executor.flushAll() - assertThat(listener.hasVisibleFreeformTasks).isTrue() - assertThat(listener.visibleFreeformTaskChangedCalls).isEqualTo(1) + assertThat(listener.hasVisibleTasksOnDefaultDisplay).isTrue() + assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1) + } + + @Test + fun addListener_tasksOnDifferentDisplay_doesNotNotify() { + repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 1, visible = true) + val listener = TestVisibilityListener() + val executor = TestShellExecutor() + repo.addVisibleTasksListener(listener, executor) + executor.flushAll() + + assertThat(listener.hasVisibleTasksOnDefaultDisplay).isFalse() + // One call as adding listener notifies it + assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(0) } @Test @@ -108,13 +146,61 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() { val listener = TestVisibilityListener() val executor = TestShellExecutor() repo.addVisibleTasksListener(listener, executor) - repo.updateVisibleFreeformTasks(1, true) - repo.updateVisibleFreeformTasks(2, true) + repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true) + repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = true) + executor.flushAll() + + assertThat(listener.hasVisibleTasksOnDefaultDisplay).isTrue() + assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1) + } + + @Test + fun updateVisibleFreeformTasks_addVisibleTaskNotifiesListenerForThatDisplay() { + val listener = TestVisibilityListener() + val executor = TestShellExecutor() + repo.addVisibleTasksListener(listener, executor) + + repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true) + executor.flushAll() + + assertThat(listener.hasVisibleTasksOnDefaultDisplay).isTrue() + assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1) + assertThat(listener.hasVisibleTasksOnSecondaryDisplay).isFalse() + assertThat(listener.visibleChangesOnSecondaryDisplay).isEqualTo(0) + + repo.updateVisibleFreeformTasks(displayId = 1, taskId = 2, visible = true) + executor.flushAll() + + // Listener for secondary display is notified + assertThat(listener.hasVisibleTasksOnSecondaryDisplay).isTrue() + assertThat(listener.visibleChangesOnSecondaryDisplay).isEqualTo(1) + // No changes to listener for default display + assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1) + } + + @Test + fun updateVisibleFreeformTasks_taskOnDefaultBecomesVisibleOnSecondDisplay_listenersNotified() { + val listener = TestVisibilityListener() + val executor = TestShellExecutor() + repo.addVisibleTasksListener(listener, executor) + + repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true) executor.flushAll() + assertThat(listener.hasVisibleTasksOnDefaultDisplay).isTrue() + + // Mark task 1 visible on secondary display + repo.updateVisibleFreeformTasks(displayId = 1, taskId = 1, visible = true) + executor.flushAll() + + // Default display should have 2 calls + // 1 - visible task added + // 2 - visible task removed + assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(2) + assertThat(listener.hasVisibleTasksOnDefaultDisplay).isFalse() - assertThat(listener.hasVisibleFreeformTasks).isTrue() - // Equal to 2 because adding the listener notifies the current state - assertThat(listener.visibleFreeformTaskChangedCalls).isEqualTo(2) + // Secondary display should have 1 call for visible task added + assertThat(listener.visibleChangesOnSecondaryDisplay).isEqualTo(1) + assertThat(listener.hasVisibleTasksOnSecondaryDisplay).isTrue() } @Test @@ -122,52 +208,83 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() { val listener = TestVisibilityListener() val executor = TestShellExecutor() repo.addVisibleTasksListener(listener, executor) - repo.updateVisibleFreeformTasks(1, true) - repo.updateVisibleFreeformTasks(2, true) + repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true) + repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = true) executor.flushAll() - assertThat(listener.hasVisibleFreeformTasks).isTrue() - repo.updateVisibleFreeformTasks(1, false) + assertThat(listener.hasVisibleTasksOnDefaultDisplay).isTrue() + repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = false) executor.flushAll() - // Equal to 2 because adding the listener notifies the current state - assertThat(listener.visibleFreeformTaskChangedCalls).isEqualTo(2) + assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1) - repo.updateVisibleFreeformTasks(2, false) + repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = false) executor.flushAll() - assertThat(listener.hasVisibleFreeformTasks).isFalse() - assertThat(listener.visibleFreeformTaskChangedCalls).isEqualTo(3) + assertThat(listener.hasVisibleTasksOnDefaultDisplay).isFalse() + assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(2) } @Test fun getVisibleTaskCount() { // No tasks, count is 0 - assertThat(repo.getVisibleTaskCount()).isEqualTo(0) + assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0) // New task increments count to 1 - repo.updateVisibleFreeformTasks(taskId = 1, visible = true) - assertThat(repo.getVisibleTaskCount()).isEqualTo(1) + repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true) + assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1) // Visibility update to same task does not increase count - repo.updateVisibleFreeformTasks(taskId = 1, visible = true) - assertThat(repo.getVisibleTaskCount()).isEqualTo(1) + repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true) + assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1) // Second task visible increments count - repo.updateVisibleFreeformTasks(taskId = 2, visible = true) - assertThat(repo.getVisibleTaskCount()).isEqualTo(2) + repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = true) + assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2) // Hiding a task decrements count - repo.updateVisibleFreeformTasks(taskId = 1, visible = false) - assertThat(repo.getVisibleTaskCount()).isEqualTo(1) + repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = false) + assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1) // Hiding all tasks leaves count at 0 - repo.updateVisibleFreeformTasks(taskId = 2, visible = false) - assertThat(repo.getVisibleTaskCount()).isEqualTo(0) + repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = false) + assertThat(repo.getVisibleTaskCount(displayId = 9)).isEqualTo(0) // Hiding a not existing task, count remains at 0 - repo.updateVisibleFreeformTasks(taskId = 999, visible = false) - assertThat(repo.getVisibleTaskCount()).isEqualTo(0) + repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 999, visible = false) + assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0) + } + + @Test + fun getVisibleTaskCount_multipleDisplays() { + assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0) + assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(0) + + // New task on default display increments count for that display only + repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true) + assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1) + assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(0) + + // New task on secondary display, increments count for that display only + repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 2, visible = true) + assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1) + assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1) + + // Marking task visible on another display, updates counts for both displays + repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 1, visible = true) + assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0) + assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(2) + + // Marking task that is on secondary display, hidden on default display, does not affect + // secondary display + repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = false) + assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0) + assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(2) + + // Hiding a task on that display, decrements count + repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 1, visible = false) + assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0) + assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1) } @Test @@ -197,19 +314,40 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() { } class TestListener : DesktopModeTaskRepository.ActiveTasksListener { - var activeTaskChangedCalls = 0 - override fun onActiveTasksChanged() { - activeTaskChangedCalls++ + var activeChangesOnDefaultDisplay = 0 + var activeChangesOnSecondaryDisplay = 0 + override fun onActiveTasksChanged(displayId: Int) { + when (displayId) { + DEFAULT_DISPLAY -> activeChangesOnDefaultDisplay++ + SECOND_DISPLAY -> activeChangesOnSecondaryDisplay++ + else -> fail("Active task listener received unexpected display id: $displayId") + } } } class TestVisibilityListener : DesktopModeTaskRepository.VisibleTasksListener { - var hasVisibleFreeformTasks = false - var visibleFreeformTaskChangedCalls = 0 - - override fun onVisibilityChanged(hasVisibleTasks: Boolean) { - hasVisibleFreeformTasks = hasVisibleTasks - visibleFreeformTaskChangedCalls++ + var hasVisibleTasksOnDefaultDisplay = false + var hasVisibleTasksOnSecondaryDisplay = false + + var visibleChangesOnDefaultDisplay = 0 + var visibleChangesOnSecondaryDisplay = 0 + + override fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) { + when (displayId) { + DEFAULT_DISPLAY -> { + hasVisibleTasksOnDefaultDisplay = hasVisibleFreeformTasks + visibleChangesOnDefaultDisplay++ + } + SECOND_DISPLAY -> { + hasVisibleTasksOnSecondaryDisplay = hasVisibleFreeformTasks + visibleChangesOnSecondaryDisplay++ + } + else -> fail("Visible task listener received unexpected display id: $displayId") + } } } + + companion object { + const val SECOND_DISPLAY = 1 + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index c9bd695ffb33..f506969f51df 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -25,6 +25,7 @@ import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED import android.os.Binder import android.testing.AndroidTestingRunner +import android.view.Display.DEFAULT_DISPLAY import android.view.WindowManager import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_NONE @@ -84,10 +85,10 @@ class DesktopTasksControllerTest : ShellTestCase() { @Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler @Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler - lateinit var mockitoSession: StaticMockitoSession - lateinit var controller: DesktopTasksController - lateinit var shellInit: ShellInit - lateinit var desktopModeTaskRepository: DesktopModeTaskRepository + private lateinit var mockitoSession: StaticMockitoSession + private lateinit var controller: DesktopTasksController + private lateinit var shellInit: ShellInit + private lateinit var desktopModeTaskRepository: DesktopModeTaskRepository // Mock running tasks are registered here so we can get the list from mock shell task organizer private val runningTasks = mutableListOf<RunningTaskInfo>() @@ -155,7 +156,7 @@ class DesktopTasksControllerTest : ShellTestCase() { markTaskHidden(task1) markTaskHidden(task2) - controller.showDesktopApps() + controller.showDesktopApps(DEFAULT_DISPLAY) val wct = getLatestWct(expectTransition = TRANSIT_NONE) assertThat(wct.hierarchyOps).hasSize(3) @@ -173,7 +174,7 @@ class DesktopTasksControllerTest : ShellTestCase() { markTaskVisible(task1) markTaskVisible(task2) - controller.showDesktopApps() + controller.showDesktopApps(DEFAULT_DISPLAY) val wct = getLatestWct(expectTransition = TRANSIT_NONE) assertThat(wct.hierarchyOps).hasSize(3) @@ -191,7 +192,7 @@ class DesktopTasksControllerTest : ShellTestCase() { markTaskHidden(task1) markTaskVisible(task2) - controller.showDesktopApps() + controller.showDesktopApps(DEFAULT_DISPLAY) val wct = getLatestWct(expectTransition = TRANSIT_NONE) assertThat(wct.hierarchyOps).hasSize(3) @@ -205,7 +206,7 @@ class DesktopTasksControllerTest : ShellTestCase() { fun showDesktopApps_noActiveTasks_reorderHomeToTop() { val homeTask = setUpHomeTask() - controller.showDesktopApps() + controller.showDesktopApps(DEFAULT_DISPLAY) val wct = getLatestWct(expectTransition = TRANSIT_NONE) assertThat(wct.hierarchyOps).hasSize(1) @@ -213,8 +214,26 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay() { + val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY) + val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY) + setUpHomeTask(SECOND_DISPLAY) + val taskSecondDisplay = setUpFreeformTask(SECOND_DISPLAY) + markTaskHidden(taskDefaultDisplay) + markTaskHidden(taskSecondDisplay) + + controller.showDesktopApps(DEFAULT_DISPLAY) + + val wct = getLatestWct(expectTransition = TRANSIT_NONE) + assertThat(wct.hierarchyOps).hasSize(2) + // Expect order to be from bottom: home, task + wct.assertReorderAt(index = 0, homeTaskDefaultDisplay) + wct.assertReorderAt(index = 1, taskDefaultDisplay) + } + + @Test fun getVisibleTaskCount_noTasks_returnsZero() { - assertThat(controller.getVisibleTaskCount()).isEqualTo(0) + assertThat(controller.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0) } @Test @@ -222,7 +241,7 @@ class DesktopTasksControllerTest : ShellTestCase() { setUpHomeTask() setUpFreeformTask().also(::markTaskVisible) setUpFreeformTask().also(::markTaskVisible) - assertThat(controller.getVisibleTaskCount()).isEqualTo(2) + assertThat(controller.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2) } @Test @@ -230,7 +249,15 @@ class DesktopTasksControllerTest : ShellTestCase() { setUpHomeTask() setUpFreeformTask().also(::markTaskVisible) setUpFreeformTask().also(::markTaskHidden) - assertThat(controller.getVisibleTaskCount()).isEqualTo(1) + assertThat(controller.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1) + } + + @Test + fun getVisibleTaskCount_twoTasksVisibleOnDifferentDisplays_returnsOne() { + setUpHomeTask() + setUpFreeformTask(DEFAULT_DISPLAY).also(::markTaskVisible) + setUpFreeformTask(SECOND_DISPLAY).also(::markTaskVisible) + assertThat(controller.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1) } @Test @@ -258,6 +285,7 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.moveToDesktop(fullscreenTask) with(getLatestWct(expectTransition = TRANSIT_CHANGE)) { + // Operations should include home task, freeform task assertThat(hierarchyOps).hasSize(3) assertReorderSequence(homeTask, freeformTask, fullscreenTask) assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode) @@ -266,6 +294,28 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun moveToDesktop_onlyFreeformTasksFromCurrentDisplayBroughtToFront() { + setUpHomeTask(displayId = DEFAULT_DISPLAY) + val freeformTaskDefault = setUpFreeformTask(displayId = DEFAULT_DISPLAY) + val fullscreenTaskDefault = setUpFullscreenTask(displayId = DEFAULT_DISPLAY) + markTaskHidden(freeformTaskDefault) + + val homeTaskSecond = setUpHomeTask(displayId = SECOND_DISPLAY) + val freeformTaskSecond = setUpFreeformTask(displayId = SECOND_DISPLAY) + markTaskHidden(freeformTaskSecond) + + controller.moveToDesktop(fullscreenTaskDefault) + + with(getLatestWct(expectTransition = TRANSIT_CHANGE)) { + // Check that hierarchy operations do not include tasks from second display + assertThat(hierarchyOps.map { it.container }) + .doesNotContain(homeTaskSecond.token.asBinder()) + assertThat(hierarchyOps.map { it.container }) + .doesNotContain(freeformTaskSecond.token.asBinder()) + } + } + + @Test fun moveToFullscreen() { val task = setUpFreeformTask() controller.moveToFullscreen(task) @@ -281,6 +331,19 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun moveToFullscreen_secondDisplayTaskHasFreeform_secondDisplayNotAffected() { + val taskDefaultDisplay = setUpFreeformTask(displayId = DEFAULT_DISPLAY) + val taskSecondDisplay = setUpFreeformTask(displayId = SECOND_DISPLAY) + + controller.moveToFullscreen(taskDefaultDisplay) + + with(getLatestWct(expectTransition = TRANSIT_CHANGE)) { + assertThat(changes.keys).contains(taskDefaultDisplay.token.asBinder()) + assertThat(changes.keys).doesNotContain(taskSecondDisplay.token.asBinder()) + } + } + + @Test fun getTaskWindowingMode() { val fullscreenTask = setUpFullscreenTask() val freeformTask = setUpFreeformTask() @@ -324,6 +387,18 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun handleRequest_fullscreenTask_freeformTaskOnOtherDisplay_returnNull() { + assumeTrue(ENABLE_SHELL_TRANSITIONS) + + val fullscreenTaskDefaultDisplay = createFullscreenTask(displayId = DEFAULT_DISPLAY) + createFreeformTask(displayId = SECOND_DISPLAY) + + val result = + controller.handleRequest(Binder(), createTransition(fullscreenTaskDefaultDisplay)) + assertThat(result).isNull() + } + + @Test fun handleRequest_freeformTask_freeformVisible_returnNull() { assumeTrue(ENABLE_SHELL_TRANSITIONS) @@ -362,6 +437,18 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun handleRequest_freeformTask_freeformOnOtherDisplayOnly_returnSwitchToFullscreenWCT() { + assumeTrue(ENABLE_SHELL_TRANSITIONS) + + val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY) + createFreeformTask(displayId = SECOND_DISPLAY) + + val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay)) + assertThat(result?.changes?.get(taskDefaultDisplay.token.asBinder())?.windowingMode) + .isEqualTo(WINDOWING_MODE_FULLSCREEN) + } + + @Test fun handleRequest_notOpenOrToFrontTransition_returnNull() { assumeTrue(ENABLE_SHELL_TRANSITIONS) @@ -400,35 +487,43 @@ class DesktopTasksControllerTest : ShellTestCase() { assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull() } - private fun setUpFreeformTask(): RunningTaskInfo { - val task = createFreeformTask() + private fun setUpFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { + val task = createFreeformTask(displayId) whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) - desktopModeTaskRepository.addActiveTask(task.taskId) + desktopModeTaskRepository.addActiveTask(displayId, task.taskId) desktopModeTaskRepository.addOrMoveFreeformTaskToTop(task.taskId) runningTasks.add(task) return task } - private fun setUpHomeTask(): RunningTaskInfo { - val task = createHomeTask() + private fun setUpHomeTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { + val task = createHomeTask(displayId) whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) runningTasks.add(task) return task } - private fun setUpFullscreenTask(): RunningTaskInfo { - val task = createFullscreenTask() + private fun setUpFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { + val task = createFullscreenTask(displayId) whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) runningTasks.add(task) return task } private fun markTaskVisible(task: RunningTaskInfo) { - desktopModeTaskRepository.updateVisibleFreeformTasks(task.taskId, visible = true) + desktopModeTaskRepository.updateVisibleFreeformTasks( + task.displayId, + task.taskId, + visible = true + ) } private fun markTaskHidden(task: RunningTaskInfo) { - desktopModeTaskRepository.updateVisibleFreeformTasks(task.taskId, visible = false) + desktopModeTaskRepository.updateVisibleFreeformTasks( + task.displayId, + task.taskId, + visible = false + ) } private fun getLatestWct( @@ -457,6 +552,10 @@ class DesktopTasksControllerTest : ShellTestCase() { ): TransitionRequestInfo { return TransitionRequestInfo(type, task, null /* remoteTransition */) } + + companion object { + const val SECOND_DISPLAY = 2 + } } private fun WindowContainerTransaction.assertReorderAt(index: Int, task: RunningTaskInfo) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt index dc91d756842e..cf1ff3214d87 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt @@ -21,14 +21,17 @@ import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN +import android.view.Display.DEFAULT_DISPLAY import com.android.wm.shell.TestRunningTaskInfoBuilder class DesktopTestHelpers { companion object { /** Create a task that has windowing mode set to [WINDOWING_MODE_FREEFORM] */ @JvmStatic - fun createFreeformTask(): RunningTaskInfo { + @JvmOverloads + fun createFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { return TestRunningTaskInfoBuilder() + .setDisplayId(displayId) .setToken(MockToken().token()) .setActivityType(ACTIVITY_TYPE_STANDARD) .setWindowingMode(WINDOWING_MODE_FREEFORM) @@ -38,8 +41,10 @@ class DesktopTestHelpers { /** Create a task that has windowing mode set to [WINDOWING_MODE_FULLSCREEN] */ @JvmStatic - fun createFullscreenTask(): RunningTaskInfo { + @JvmOverloads + fun createFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { return TestRunningTaskInfoBuilder() + .setDisplayId(displayId) .setToken(MockToken().token()) .setActivityType(ACTIVITY_TYPE_STANDARD) .setWindowingMode(WINDOWING_MODE_FULLSCREEN) @@ -49,8 +54,10 @@ class DesktopTestHelpers { /** Create a new home task */ @JvmStatic - fun createHomeTask(): RunningTaskInfo { + @JvmOverloads + fun createHomeTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { return TestRunningTaskInfoBuilder() + .setDisplayId(displayId) .setToken(MockToken().token()) .setActivityType(ACTIVITY_TYPE_HOME) .setWindowingMode(WINDOWING_MODE_FULLSCREEN) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index 6995d10dd78d..04f2c99783da 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -268,7 +268,7 @@ public class PipControllerTest extends ShellTestCase { } @Test - public void saveReentryState_userHasResized_savesSize() { + public void saveReentryState_nonEmptyUserResizeBounds_savesSize() { final Rect bounds = new Rect(0, 0, 10, 10); final Rect resizedBounds = new Rect(0, 0, 30, 30); when(mMockPipBoundsAlgorithm.getSnapFraction(bounds)).thenReturn(1.0f); @@ -281,6 +281,19 @@ public class PipControllerTest extends ShellTestCase { } @Test + public void saveReentryState_emptyUserResizeBounds_savesSize() { + final Rect bounds = new Rect(0, 0, 10, 10); + final Rect resizedBounds = new Rect(0, 0, 0, 0); + when(mMockPipBoundsAlgorithm.getSnapFraction(bounds)).thenReturn(1.0f); + when(mMockPipTouchHandler.getUserResizeBounds()).thenReturn(resizedBounds); + when(mMockPipBoundsState.hasUserResizedPip()).thenReturn(true); + + mPipController.saveReentryState(bounds); + + verify(mMockPipBoundsState).saveReentryState(new Size(10, 10), 1.0f); + } + + @Test public void onDisplayConfigurationChanged_inPip_movePip() { final int displayId = 1; final Rect bounds = new Rect(0, 0, 10, 10); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index a9f311f9e9eb..92cbf7f068b4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -180,8 +180,9 @@ public class SplitTransitionTests extends ShellTestCase { TestRemoteTransition testRemote = new TestRemoteTransition(); IBinder transition = mSplitScreenTransitions.startEnterTransition( - TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(), - new RemoteTransition(testRemote, "Test"), mStageCoordinator, null, null); + TRANSIT_OPEN, new WindowContainerTransaction(), + new RemoteTransition(testRemote, "Test"), mStageCoordinator, null, null, + TRANSIT_SPLIT_SCREEN_PAIR_OPEN); mMainStage.onTaskAppeared(mMainChild, createMockSurface()); mSideStage.onTaskAppeared(mSideChild, createMockSurface()); boolean accepted = mStageCoordinator.startAnimation(transition, info, @@ -397,7 +398,7 @@ public class SplitTransitionTests extends ShellTestCase { } private TransitionInfo createEnterPairInfo() { - return new TransitionInfoBuilder(TRANSIT_SPLIT_SCREEN_PAIR_OPEN, 0) + return new TransitionInfoBuilder(TRANSIT_OPEN, 0) .addChange(TRANSIT_OPEN, mMainChild) .addChange(TRANSIT_OPEN, mSideChild) .build(); @@ -406,9 +407,9 @@ public class SplitTransitionTests extends ShellTestCase { private void enterSplit() { TransitionInfo enterInfo = createEnterPairInfo(); IBinder enterTransit = mSplitScreenTransitions.startEnterTransition( - TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(), + TRANSIT_OPEN, new WindowContainerTransaction(), new RemoteTransition(new TestRemoteTransition(), "Test"), - mStageCoordinator, null, null); + mStageCoordinator, null, null, TRANSIT_SPLIT_SCREEN_PAIR_OPEN); mMainStage.onTaskAppeared(mMainChild, createMockSurface()); mSideStage.onTaskAppeared(mSideChild, createMockSurface()); mStageCoordinator.startAnimation(enterTransit, enterInfo, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java index 784ad9b006b6..1a1bebd28aef 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java @@ -126,6 +126,12 @@ public final class StageTaskListenerTests extends ShellTestCase { verify(mCallbacks).onStatusChanged(eq(mRootTask.isVisible), eq(true)); } + @Test(expected = IllegalArgumentException.class) + public void testUnknownTaskVanished() { + final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build(); + mStageTaskListener.onTaskVanished(task); + } + @Test public void testTaskVanished() { // With shell transitions, the transition manages status changes, so skip this test. diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt new file mode 100644 index 000000000000..348b3659e864 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.windowdecor + +import android.app.ActivityManager +import android.graphics.PointF +import android.graphics.Rect +import android.os.IBinder +import android.testing.AndroidTestingRunner +import android.view.Display +import android.window.WindowContainerToken +import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.DisplayLayout +import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM +import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT +import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP +import com.google.common.truth.Truth.assertThat +import junit.framework.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.`when` as whenever +import org.mockito.Mockito.any +import org.mockito.MockitoAnnotations + +/** + * Tests for [DragPositioningCallbackUtility]. + * + * Build/Install/Run: + * atest WMShellUnitTests:DragPositioningCallbackUtilityTest + */ +@RunWith(AndroidTestingRunner::class) +class DragPositioningCallbackUtilityTest { + @Mock + private lateinit var mockWindowDecoration: WindowDecoration<*> + @Mock + private lateinit var taskToken: WindowContainerToken + @Mock + private lateinit var taskBinder: IBinder + @Mock + private lateinit var mockDisplayController: DisplayController + @Mock + private lateinit var mockDisplayLayout: DisplayLayout + @Mock + private lateinit var mockDisplay: Display + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + whenever(taskToken.asBinder()).thenReturn(taskBinder) + whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout) + whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI) + whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i -> + (i.arguments.first() as Rect).set(STABLE_BOUNDS) + } + + mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply { + taskId = TASK_ID + token = taskToken + minWidth = MIN_WIDTH + minHeight = MIN_HEIGHT + defaultMinSize = DEFAULT_MIN + displayId = DISPLAY_ID + configuration.windowConfiguration.bounds = STARTING_BOUNDS + } + mockWindowDecoration.mDisplay = mockDisplay + whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID } + } + + @Test + fun testChangeBoundsDoesNotChangeHeightWhenLessThanMin() { + val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat()) + val repositionTaskBounds = Rect() + + // Resize to width of 95px and height of 5px with min width of 10px + val newX = STARTING_BOUNDS.right.toFloat() - 5 + val newY = STARTING_BOUNDS.top.toFloat() + 95 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, + false /* hasMoved */, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration) + + assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 5) + assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) + } + + @Test + fun testChangeBoundsDoesNotChangeWidthWhenLessThanMin() { + val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat()) + val repositionTaskBounds = Rect() + + // Resize to height of 95px and width of 5px with min width of 10px + val newX = STARTING_BOUNDS.right.toFloat() - 95 + val newY = STARTING_BOUNDS.top.toFloat() + 5 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, + false /* hasMoved */, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration) + + assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top + 5) + assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right) + assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) + } + + @Test + fun testChangeBoundsDoesNotChangeHeightWhenNegative() { + val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat()) + val repositionTaskBounds = Rect() + + // Resize to width of 95px and width of -5px with minimum of 10px + val newX = STARTING_BOUNDS.right.toFloat() - 5 + val newY = STARTING_BOUNDS.top.toFloat() + 105 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, + false /* hasMoved */, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration) + + assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 5) + assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) + } + + @Test + fun testChangeBoundsRunsWhenResizeBoundsValid() { + val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat()) + val repositionTaskBounds = Rect() + + // Shrink to height 20px and width 20px with both min height/width equal to 10px + val newX = STARTING_BOUNDS.right.toFloat() - 80 + val newY = STARTING_BOUNDS.top.toFloat() + 80 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, + false /* hasMoved */, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration) + assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top + 80) + assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 80) + assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) + } + + @Test + fun testChangeBoundsDoesNotRunWithNegativeHeightAndWidth() { + val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat()) + val repositionTaskBounds = Rect() + // Shrink to height -5px and width -5px with both min height/width equal to 10px + val newX = STARTING_BOUNDS.right.toFloat() - 105 + val newY = STARTING_BOUNDS.top.toFloat() + 105 + + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, + false /* hasMoved */, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration) + assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right) + assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) + } + + @Test + fun testChangeBounds_toDisallowedBounds_freezesAtLimit() { + var hasMoved = false + val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), + STARTING_BOUNDS.bottom.toFloat()) + val repositionTaskBounds = Rect() + // Initial resize to width and height 110px. + var newX = STARTING_BOUNDS.right.toFloat() + 10 + var newY = STARTING_BOUNDS.bottom.toFloat() + 10 + var delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + assertTrue(DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, + hasMoved, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration)) + hasMoved = true + // Resize width to 120px, height to disallowed area which should not result in a change. + newX += 10 + newY = DISALLOWED_RESIZE_AREA.top.toFloat() + delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + assertTrue(DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, + hasMoved, repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration)) + assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right + 20) + assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom + 10) + } + + companion object { + private const val TASK_ID = 5 + private const val MIN_WIDTH = 10 + private const val MIN_HEIGHT = 10 + private const val DENSITY_DPI = 20 + private const val DEFAULT_MIN = 40 + private const val DISPLAY_ID = 1 + private const val NAVBAR_HEIGHT = 50 + private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600) + private val STARTING_BOUNDS = Rect(0, 0, 100, 100) + private val DISALLOWED_RESIZE_AREA = Rect( + DISPLAY_BOUNDS.left, + DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT, + DISPLAY_BOUNDS.right, + DISPLAY_BOUNDS.bottom) + private val STABLE_BOUNDS = Rect( + DISPLAY_BOUNDS.left, + DISPLAY_BOUNDS.top, + DISPLAY_BOUNDS.right, + DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT + ) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt index 84ccddeb6a76..5bea8f2d1c45 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt @@ -14,7 +14,6 @@ import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase -import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED @@ -22,7 +21,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.`when` +import org.mockito.Mockito.`when` as whenever import org.mockito.Mockito.any import org.mockito.Mockito.argThat import org.mockito.Mockito.never @@ -72,10 +71,10 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { mockDragStartListener ) - `when`(taskToken.asBinder()).thenReturn(taskBinder) - `when`(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout) - `when`(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI) - `when`(mockDisplayLayout.getStableBounds(any())).thenAnswer { i -> + whenever(taskToken.asBinder()).thenReturn(taskBinder) + whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout) + whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI) + whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i -> (i.arguments.first() as Rect).set(STABLE_BOUNDS) } @@ -89,7 +88,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { configuration.windowConfiguration.bounds = STARTING_BOUNDS } mockWindowDecoration.mDisplay = mockDisplay - `when`(mockDisplay.displayId).thenAnswer { DISPLAY_ID } + whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID } } @Test @@ -237,293 +236,6 @@ class FluidResizeTaskPositionerTest : ShellTestCase() { }) } - @Test - fun testDragResize_resize_setBoundsDoesNotChangeHeightWhenLessThanMin() { - taskPositioner.onDragPositioningStart( - CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top - STARTING_BOUNDS.right.toFloat(), - STARTING_BOUNDS.top.toFloat() - ) - - // Resize to width of 95px and height of 5px with min width of 10px - val newX = STARTING_BOUNDS.right.toFloat() - 5 - val newY = STARTING_BOUNDS.top.toFloat() + 95 - taskPositioner.onDragPositioningMove( - newX, - newY - ) - - taskPositioner.onDragPositioningEnd(newX, newY) - - verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> - return@argThat wct.changes.any { (token, change) -> - token == taskBinder && - ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) - != 0) && change.configuration.windowConfiguration.bounds.top == - STARTING_BOUNDS.top && - change.configuration.windowConfiguration.bounds.bottom == - STARTING_BOUNDS.bottom - } - }) - } - - @Test - fun testDragResize_resize_setBoundsDoesNotChangeWidthWhenLessThanMin() { - taskPositioner.onDragPositioningStart( - CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top - STARTING_BOUNDS.right.toFloat(), - STARTING_BOUNDS.top.toFloat() - ) - - // Resize to height of 95px and width of 5px with min width of 10px - val newX = STARTING_BOUNDS.right.toFloat() - 95 - val newY = STARTING_BOUNDS.top.toFloat() + 5 - taskPositioner.onDragPositioningMove( - newX, - newY - ) - - taskPositioner.onDragPositioningEnd(newX, newY) - - verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> - return@argThat wct.changes.any { (token, change) -> - token == taskBinder && - ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) - != 0) && change.configuration.windowConfiguration.bounds.right == - STARTING_BOUNDS.right && - change.configuration.windowConfiguration.bounds.left == - STARTING_BOUNDS.left - } - }) - } - - @Test - fun testDragResize_resize_setBoundsDoesNotChangeHeightWhenNegative() { - taskPositioner.onDragPositioningStart( - CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top - STARTING_BOUNDS.right.toFloat(), - STARTING_BOUNDS.top.toFloat() - ) - - // Resize to height of -5px and width of 95px - val newX = STARTING_BOUNDS.right.toFloat() - 5 - val newY = STARTING_BOUNDS.top.toFloat() + 105 - taskPositioner.onDragPositioningMove( - newX, - newY - ) - - taskPositioner.onDragPositioningEnd(newX, newY) - - verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> - return@argThat wct.changes.any { (token, change) -> - token == taskBinder && - ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) - != 0) && change.configuration.windowConfiguration.bounds.top == - STARTING_BOUNDS.top && - change.configuration.windowConfiguration.bounds.bottom == - STARTING_BOUNDS.bottom - } - }) - } - - @Test - fun testDragResize_resize_setBoundsDoesNotChangeWidthWhenNegative() { - taskPositioner.onDragPositioningStart( - CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top - STARTING_BOUNDS.right.toFloat(), - STARTING_BOUNDS.top.toFloat() - ) - - // Resize to width of -5px and height of 95px - val newX = STARTING_BOUNDS.right.toFloat() - 105 - val newY = STARTING_BOUNDS.top.toFloat() + 5 - taskPositioner.onDragPositioningMove( - newX, - newY - ) - - taskPositioner.onDragPositioningEnd(newX, newY) - - verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> - return@argThat wct.changes.any { (token, change) -> - token == taskBinder && - ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) - != 0) && change.configuration.windowConfiguration.bounds.right == - STARTING_BOUNDS.right && - change.configuration.windowConfiguration.bounds.left == - STARTING_BOUNDS.left - } - }) - } - - @Test - fun testDragResize_resize_setBoundsRunsWhenResizeBoundsValid() { - taskPositioner.onDragPositioningStart( - CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top - STARTING_BOUNDS.right.toFloat(), - STARTING_BOUNDS.top.toFloat() - ) - - // Shrink to height 20px and width 20px with both min height/width equal to 10px - val newX = STARTING_BOUNDS.right.toFloat() - 80 - val newY = STARTING_BOUNDS.top.toFloat() + 80 - taskPositioner.onDragPositioningMove( - newX, - newY - ) - - taskPositioner.onDragPositioningEnd(newX, newY) - - verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> - return@argThat wct.changes.any { (token, change) -> - token == taskBinder && - ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) - } - }) - } - - @Test - fun testDragResize_resize_setBoundsDoesNotRunWithNegativeHeightAndWidth() { - taskPositioner.onDragPositioningStart( - CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top - STARTING_BOUNDS.right.toFloat(), - STARTING_BOUNDS.top.toFloat() - ) - - // Shrink to height 5px and width 5px with both min height/width equal to 10px - val newX = STARTING_BOUNDS.right.toFloat() - 95 - val newY = STARTING_BOUNDS.top.toFloat() + 95 - taskPositioner.onDragPositioningMove( - newX, - newY - ) - - taskPositioner.onDragPositioningEnd(newX, newY) - - verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct -> - return@argThat wct.changes.any { (token, change) -> - token == taskBinder && - ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) - } - }) - } - - @Test - fun testDragResize_resize_useDefaultMinWhenMinWidthInvalid() { - mockWindowDecoration.mTaskInfo.minWidth = -1 - - taskPositioner.onDragPositioningStart( - CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top - STARTING_BOUNDS.right.toFloat(), - STARTING_BOUNDS.top.toFloat() - ) - - // Shrink to width and height of 3px with invalid minWidth = -1 and defaultMinSize = 5px - val newX = STARTING_BOUNDS.right.toFloat() - 97 - val newY = STARTING_BOUNDS.top.toFloat() + 97 - taskPositioner.onDragPositioningMove( - newX, - newY - ) - - taskPositioner.onDragPositioningEnd(newX, newY) - - verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct -> - return@argThat wct.changes.any { (token, change) -> - token == taskBinder && - ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) - } - }) - } - - @Test - fun testDragResize_resize_useMinWidthWhenValid() { - taskPositioner.onDragPositioningStart( - CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, // Resize right and top - STARTING_BOUNDS.right.toFloat(), - STARTING_BOUNDS.top.toFloat() - ) - - // Shrink to width and height of 7px with valid minWidth = 10px and defaultMinSize = 5px - val newX = STARTING_BOUNDS.right.toFloat() - 93 - val newY = STARTING_BOUNDS.top.toFloat() + 93 - taskPositioner.onDragPositioningMove( - newX, - newY - ) - - taskPositioner.onDragPositioningEnd(newX, newY) - - verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct -> - return@argThat wct.changes.any { (token, change) -> - token == taskBinder && - ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) - } - }) - } - - fun testDragResize_toDisallowedBounds_freezesAtLimit() { - taskPositioner.onDragPositioningStart( - CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, // Resize right-bottom corner - STARTING_BOUNDS.right.toFloat(), - STARTING_BOUNDS.bottom.toFloat() - ) - - // Resize the task by 10px to the right and bottom, a valid destination - val newBounds = Rect( - STARTING_BOUNDS.left, - STARTING_BOUNDS.top, - STARTING_BOUNDS.right + 10, - STARTING_BOUNDS.bottom + 10) - taskPositioner.onDragPositioningMove( - newBounds.right.toFloat(), - newBounds.bottom.toFloat() - ) - - // Resize the task by another 10px to the right (allowed) and to just in the disallowed - // area of the Y coordinate. - val newBounds2 = Rect( - newBounds.left, - newBounds.top, - newBounds.right + 10, - DISALLOWED_RESIZE_AREA.top - ) - taskPositioner.onDragPositioningMove( - newBounds2.right.toFloat(), - newBounds2.bottom.toFloat() - ) - - taskPositioner.onDragPositioningEnd(newBounds2.right.toFloat(), newBounds2.bottom.toFloat()) - - // The first resize falls in the allowed area, verify there's a change for it. - verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> - return@argThat wct.changes.any { (token, change) -> - token == taskBinder && change.ofBounds(newBounds) - } - }) - // The second resize falls in the disallowed area, verify there's no change for it. - verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct -> - return@argThat wct.changes.any { (token, change) -> - token == taskBinder && change.ofBounds(newBounds2) - } - }) - // Instead, there should be a change for its allowed portion (the X movement) with the Y - // staying frozen in the last valid resize position. - verify(mockShellTaskOrganizer).applyTransaction(argThat { wct -> - return@argThat wct.changes.any { (token, change) -> - token == taskBinder && change.ofBounds( - Rect( - newBounds2.left, - newBounds2.top, - newBounds2.right, - newBounds.bottom // Stayed at the first resize destination. - ) - ) - } - }) - } - private fun WindowContainerTransaction.Change.ofBounds(bounds: Rect): Boolean { return ((windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) && bounds == configuration.windowConfiguration.bounds diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt index bf365ca782ee..498082bd53e5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt @@ -34,7 +34,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.`when` +import org.mockito.Mockito.`when` as whenever import org.mockito.Mockito.any import org.mockito.Mockito.argThat import org.mockito.Mockito.never @@ -85,10 +85,10 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { mockDragStartListener ) - `when`(taskToken.asBinder()).thenReturn(taskBinder) - `when`(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout) - `when`(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI) - `when`(mockDisplayLayout.getStableBounds(any())).thenAnswer { i -> + whenever(taskToken.asBinder()).thenReturn(taskBinder) + whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout) + whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI) + whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i -> (i.arguments.first() as Rect).set(STABLE_BOUNDS) } @@ -102,7 +102,7 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() { configuration.windowConfiguration.bounds = STARTING_BOUNDS } mockDesktopWindowDecoration.mDisplay = mockDisplay - `when`(mockDisplay.displayId).thenAnswer { DISPLAY_ID } + whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID } } @Test diff --git a/libs/hwui/effects/GainmapRenderer.cpp b/libs/hwui/effects/GainmapRenderer.cpp index bfe4eaf39e21..613f52b32bea 100644 --- a/libs/hwui/effects/GainmapRenderer.cpp +++ b/libs/hwui/effects/GainmapRenderer.cpp @@ -38,7 +38,7 @@ namespace android::uirenderer { using namespace renderthread; -static float getTargetHdrSdrRatio(const SkColorSpace* destColorspace) { +float getTargetHdrSdrRatio(const SkColorSpace* destColorspace) { // We should always have a known destination colorspace. If we don't we must be in some // legacy mode where we're lost and also definitely not going to HDR if (destColorspace == nullptr) { diff --git a/libs/hwui/effects/GainmapRenderer.h b/libs/hwui/effects/GainmapRenderer.h index 4ed2445da17e..0ab03f0b571a 100644 --- a/libs/hwui/effects/GainmapRenderer.h +++ b/libs/hwui/effects/GainmapRenderer.h @@ -25,6 +25,8 @@ namespace android::uirenderer { +float getTargetHdrSdrRatio(const SkColorSpace* destColorspace); + void DrawGainmapBitmap(SkCanvas* c, const sk_sp<const SkImage>& image, const SkRect& src, const SkRect& dst, const SkSamplingOptions& sampling, const SkPaint* paint, SkCanvas::SrcRectConstraint constraint, diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp index a4960ea17c79..c58ba6868eb5 100644 --- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp @@ -26,6 +26,7 @@ #include "SkM44.h" #include "include/gpu/GpuTypes.h" // from Skia #include "utils/GLUtils.h" +#include <effects/GainmapRenderer.h> namespace android { namespace uirenderer { @@ -129,6 +130,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { info.height = fboSize.height(); mat4.getColMajor(&info.transform[0]); info.color_space_ptr = canvas->imageInfo().colorSpace(); + info.currentHdrSdrRatio = getTargetHdrSdrRatio(info.color_space_ptr); // ensure that the framebuffer that the webview will render into is bound before we clear // the stencil and/or draw the functor. diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp index e6ef95b9cf91..e299d12b1d67 100644 --- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp @@ -30,6 +30,7 @@ #include "renderthread/VulkanManager.h" #include "thread/ThreadBase.h" #include "utils/TimeUtils.h" +#include "effects/GainmapRenderer.h" namespace android { namespace uirenderer { @@ -73,6 +74,7 @@ void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) { .clip_right = mClip.fRight, .clip_bottom = mClip.fBottom, .is_layer = !vulkan_info.fFromSwapchainOrAndroidWindow, + .currentHdrSdrRatio = getTargetHdrSdrRatio(mImageInfo.colorSpace()), }; mat4.getColMajor(¶ms.transform[0]); params.secondary_command_buffer = vulkan_info.fSecondaryCommandBuffer; diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp index e168a7b9459a..adf3c06b8624 100644 --- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp @@ -32,6 +32,7 @@ #include "renderthread/EglManager.h" #include "thread/ThreadBase.h" #include "utils/TimeUtils.h" +#include "effects/GainmapRenderer.h" #include <SkBlendMode.h> @@ -139,6 +140,7 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { info.height = mFBInfo.height(); mat4.getColMajor(&info.transform[0]); info.color_space_ptr = canvas->imageInfo().colorSpace(); + info.currentHdrSdrRatio = getTargetHdrSdrRatio(info.color_space_ptr); glViewport(0, 0, info.width, info.height); diff --git a/libs/hwui/private/hwui/DrawGlInfo.h b/libs/hwui/private/hwui/DrawGlInfo.h index 501b8df9bc36..7888c8719e88 100644 --- a/libs/hwui/private/hwui/DrawGlInfo.h +++ b/libs/hwui/private/hwui/DrawGlInfo.h @@ -86,6 +86,11 @@ struct DrawGlInfo { // commands are issued. kStatusDrew = 0x4 }; + + // The current HDR/SDR ratio that we are rendering to. The transform to SDR will already + // be baked into the color_space_ptr, so this is just to indicate the amount of extended + // range is available if desired + float currentHdrSdrRatio; }; // struct DrawGlInfo } // namespace uirenderer diff --git a/libs/hwui/private/hwui/DrawVkInfo.h b/libs/hwui/private/hwui/DrawVkInfo.h index 5c596576df4e..8f7063d72314 100644 --- a/libs/hwui/private/hwui/DrawVkInfo.h +++ b/libs/hwui/private/hwui/DrawVkInfo.h @@ -71,6 +71,11 @@ struct VkFunctorDrawParams { // Input: Whether destination surface is offscreen surface. bool is_layer; + + // The current HDR/SDR ratio that we are rendering to. The transform to SDR will already + // be baked into the color_space_ptr, so this is just to indicate the amount of extended + // range is available if desired + float currentHdrSdrRatio; }; } // namespace uirenderer diff --git a/packages/CompanionDeviceManager/res/layout/list_item_permission.xml b/packages/CompanionDeviceManager/res/layout/list_item_permission.xml index 6bfcd82b0a4a..8c35da13e2c4 100644 --- a/packages/CompanionDeviceManager/res/layout/list_item_permission.xml +++ b/packages/CompanionDeviceManager/res/layout/list_item_permission.xml @@ -58,6 +58,7 @@ android:textSize="14sp" android:layout_marginTop="2dp" style="@style/TextAppearance" + android:focusable="true" android:textColor="?android:attr/textColorSecondary"/> </LinearLayout> diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml index 74072e9d4ec3..2502bbf7b40b 100644 --- a/packages/CompanionDeviceManager/res/values/strings.xml +++ b/packages/CompanionDeviceManager/res/values/strings.xml @@ -113,17 +113,11 @@ <!-- Back button for the helper consent dialog [CHAR LIMIT=30] --> <string name="consent_back">Back</string> - <!-- Action when permission list view is expanded CHAR LIMIT=30] --> - <string name="permission_expanded">Expanded</string> + <!-- Expand permission in the list CHAR LIMIT=30] --> + <string name="permission_expand">Expand <xliff:g id="permission_type" example="Notification">%1$s</xliff:g></string> - <!-- Expand action permission list CHAR LIMIT=30] --> - <string name="permission_expand">Expand</string> - - <!-- Action when permission list view is collapsed CHAR LIMIT=30] --> - <string name="permission_collapsed">Collapsed</string> - - <!-- Collapse action permission list CHAR LIMIT=30] --> - <string name="permission_collapse">Collapse</string> + <!-- Collapse permission int the list CHAR LIMIT=30] --> + <string name="permission_collapse">Collapse <xliff:g id="permission_type" example="Notification">%1$s</xliff:g></string> <!-- ================== System data transfer ==================== --> <!-- Title of the permission sync confirmation dialog. [CHAR LIMIT=NONE] --> diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java index b86ef649331a..f594bf270d29 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java @@ -124,7 +124,7 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V } setAccessibility(view, viewType, - AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_expand); + AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_expand, 0); // Add expand buttons if the permissions are more than PERMISSION_SIZE in this list also // make the summary invisible by default. @@ -137,18 +137,16 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V viewHolder.mExpandButton.setImageResource(R.drawable.btn_expand_less); viewHolder.mPermissionSummary.setVisibility(View.VISIBLE); viewHolder.mExpandButton.setTag(R.drawable.btn_expand_less); - view.setContentDescription(mContext.getString(R.string.permission_expanded)); setAccessibility(view, viewType, - AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_collapse); - viewHolder.mPermissionSummary.setFocusable(true); + AccessibilityNodeInfo.ACTION_CLICK, + R.string.permission_collapse, R.string.permission_expand); } else { viewHolder.mExpandButton.setImageResource(R.drawable.btn_expand_more); viewHolder.mPermissionSummary.setVisibility(View.GONE); viewHolder.mExpandButton.setTag(R.drawable.btn_expand_more); - view.setContentDescription(mContext.getString(R.string.permission_collapsed)); setAccessibility(view, viewType, - AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_expanded); - viewHolder.mPermissionSummary.setFocusable(false); + AccessibilityNodeInfo.ACTION_CLICK, + R.string.permission_expand, R.string.permission_collapse); } }); } else { @@ -200,14 +198,20 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V } } - private void setAccessibility(View view, int viewType, int action, int resourceId) { - final String actionString = mContext.getString(resourceId); + private void setAccessibility(View view, int viewType, int action, int statusResourceId, + int actionResourceId) { final String permission = mContext.getString(sTitleMap.get(viewType)); + + if (actionResourceId != 0) { + view.announceForAccessibility( + getHtmlFromResources(mContext, actionResourceId, permission)); + } + view.setAccessibilityDelegate(new View.AccessibilityDelegate() { public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(host, info); info.addAction(new AccessibilityNodeInfo.AccessibilityAction(action, - actionString + permission)); + getHtmlFromResources(mContext, statusResourceId, permission))); } }); } diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml index a3b27529d857..340285760cda 100644 --- a/packages/CredentialManager/res/values/strings.xml +++ b/packages/CredentialManager/res/values/strings.xml @@ -86,6 +86,8 @@ <string name="use_provider_for_all_description">This password manager for <xliff:g id="username" example="becket@gmail.com">%1$s</xliff:g> will store your passwords and passkeys to help you easily sign in</string> <!-- This is a label for a button that sets this password manager as the default. [CHAR LIMIT=20] --> <string name="set_as_default">Set as default</string> + <!-- This is a button text to navigate the user to their system settings. [CHAR LIMIT=30] --> + <string name="settings">Settings</string> <!-- This is a label for a button that makes this password manager be used just in this specific case. [CHAR LIMIT=20] --> <string name="use_once">Use once</string> <!-- Appears as an option row subtitle to show how many passwords and passkeys are saved in this option when there are passwords and passkeys. [CHAR LIMIT=80] --> diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java index 01f92c4fa7b1..19b7e8546805 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java @@ -17,6 +17,7 @@ package com.android.settingslib.collapsingtoolbar; import android.app.ActionBar; +import android.content.pm.PackageManager; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -59,7 +60,8 @@ public class CollapsingToolbarBaseActivity extends FragmentActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (mCustomizeLayoutResId > 0 && !BuildCompatUtils.isAtLeastS()) { + // for backward compatibility on R devices or wearable devices due to small device size. + if (mCustomizeLayoutResId > 0 && (!BuildCompatUtils.isAtLeastS() || isWatch())) { super.setContentView(mCustomizeLayoutResId); return; } @@ -157,6 +159,14 @@ public class CollapsingToolbarBaseActivity extends FragmentActivity { return getToolbarDelegate().getAppBarLayout(); } + private boolean isWatch() { + PackageManager packageManager = getPackageManager(); + if (packageManager == null) { + return false; + } + return packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH); + } + private CollapsingToolbarDelegate getToolbarDelegate() { if (mToolbardelegate == null) { mToolbardelegate = new CollapsingToolbarDelegate(new DelegateCallback()); diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt index 9c70be9c1f66..6a13eb8c3907 100644 --- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt +++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt @@ -19,6 +19,7 @@ package com.android.settingslib.devicestate import android.content.Context import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_FOLDED import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_HALF_FOLDED +import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNFOLDED import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNKNOWN import android.provider.Settings.Secure.DeviceStateRotationLockKey @@ -33,6 +34,8 @@ class PosturesHelper(context: Context) { context.resources.getIntArray(R.array.config_halfFoldedDeviceStates) private val unfoldedDeviceStates = context.resources.getIntArray(R.array.config_openDeviceStates) + private val rearDisplayDeviceStates = + context.resources.getIntArray(R.array.config_rearDisplayDeviceStates) @DeviceStateRotationLockKey fun deviceStateToPosture(deviceState: Int): Int { @@ -40,6 +43,7 @@ class PosturesHelper(context: Context) { in foldedDeviceStates -> DEVICE_STATE_ROTATION_KEY_FOLDED in halfFoldedDeviceStates -> DEVICE_STATE_ROTATION_KEY_HALF_FOLDED in unfoldedDeviceStates -> DEVICE_STATE_ROTATION_KEY_UNFOLDED + in rearDisplayDeviceStates -> DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY else -> DEVICE_STATE_ROTATION_KEY_UNKNOWN } } @@ -49,6 +53,7 @@ class PosturesHelper(context: Context) { DEVICE_STATE_ROTATION_KEY_FOLDED -> foldedDeviceStates.firstOrNull() DEVICE_STATE_ROTATION_KEY_HALF_FOLDED -> halfFoldedDeviceStates.firstOrNull() DEVICE_STATE_ROTATION_KEY_UNFOLDED -> unfoldedDeviceStates.firstOrNull() + DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY -> rearDisplayDeviceStates.firstOrNull() else -> null } } diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_ic_info.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_ic_info.xml deleted file mode 100644 index c8037c85c850..000000000000 --- a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_ic_info.xml +++ /dev/null @@ -1,26 +0,0 @@ -<!-- - Copyright (C) 2021 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<!-- copy from frameworks/base/core/res/res/drawable/ic_info.xml--> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - <path - android:fillColor="@android:color/white" - android:pathData="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/> -</vector>
\ No newline at end of file diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml index ca84db854bdd..b1c26e852cdc 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml @@ -46,16 +46,6 @@ android:textAppearance="?android:attr/textAppearanceListItem" style="@style/MainSwitchText.Settingslib" /> - <ImageView - android:id="@+id/restricted_icon" - android:layout_width="@dimen/settingslib_restricted_icon_size" - android:layout_height="@dimen/settingslib_restricted_icon_size" - android:tint="?android:attr/colorAccent" - android:layout_gravity="center_vertical" - android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end" - android:src="@drawable/settingslib_ic_info" - android:visibility="gone" /> - <Switch android:id="@android:id/switch_widget" android:layout_width="wrap_content" diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml index 2c2ad92a7830..ab0cf3138d9c 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml @@ -49,16 +49,6 @@ android:lineBreakWordStyle="phrase" style="@style/MainSwitchText.Settingslib" /> - <ImageView - android:id="@+id/restricted_icon" - android:layout_width="@dimen/settingslib_restricted_icon_size" - android:layout_height="@dimen/settingslib_restricted_icon_size" - android:tint="@color/settingslib_accent_primary_variant" - android:layout_gravity="center_vertical" - android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end" - android:src="@drawable/settingslib_ic_info" - android:visibility="gone" /> - <Switch android:id="@android:id/switch_widget" android:layout_width="wrap_content" diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml index b39d09f38b62..bf34db93298b 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml @@ -38,17 +38,6 @@ android:layout_marginStart="@dimen/settingslib_switchbar_subsettings_margin_start" android:textAlignment="viewStart"/> - <ImageView - android:id="@+id/restricted_icon" - android:layout_width="@dimen/settingslib_restricted_icon_size" - android:layout_height="@dimen/settingslib_restricted_icon_size" - android:tint="?android:attr/colorAccent" - android:theme="@android:style/Theme.Material" - android:layout_gravity="center_vertical" - android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end" - android:src="@drawable/settingslib_ic_info" - android:visibility="gone"/> - <Switch android:id="@android:id/switch_widget" android:layout_width="wrap_content" diff --git a/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml b/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml index a50fc7cc0028..ad888e538ffe 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml @@ -17,9 +17,8 @@ <resources> - <style name="MainSwitchText.Settingslib" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title"> + <style name="MainSwitchText.Settingslib" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title.Inverse"> <item name="android:textSize">20sp</item> <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item> - <item name="android:textColor">@android:color/black</item> </style> </resources> diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml index 88b2c8728495..0d9ffff9c9e2 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml @@ -17,16 +17,9 @@ <resources> - <!-- Restricted icon size in switch bar --> - <dimen name="settingslib_restricted_icon_size">@android:dimen/config_restrictedIconSize</dimen> - - <!-- Restricted icon in switch bar --> - <dimen name="settingslib_restricted_icon_margin_end">16dp</dimen> - <!-- Size of title margin --> <dimen name="settingslib_switch_title_margin">24dp</dimen> - <!-- SwitchBar sub settings margin start / end --> + <!-- SwitchBar sub settings margin start --> <dimen name="settingslib_switchbar_subsettings_margin_start">56dp</dimen> - <dimen name="settingslib_switchbar_subsettings_margin_end">16dp</dimen> </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java index 3c451127508d..dae48dbac0a4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java @@ -17,6 +17,7 @@ package com.android.settingslib.applications; import android.app.AppGlobals; +import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -94,6 +95,10 @@ public class DefaultAppInfo extends CandidateInfo { } + public @Nullable String getSummary() { + return this.summary; + } + @Override public Drawable loadIcon() { final IconDrawableFactory factory = IconDrawableFactory.newInstance(mContext); diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp index d463170fae8c..ff3eeec4171f 100644 --- a/packages/SettingsLib/tests/integ/Android.bp +++ b/packages/SettingsLib/tests/integ/Android.bp @@ -30,7 +30,10 @@ android_test { certificate: "platform", - srcs: ["src/**/*.java"], + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], libs: [ "android.test.runner", diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/PosturesHelperTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/PosturesHelperTest.kt new file mode 100644 index 000000000000..d91c2fa66ca8 --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/PosturesHelperTest.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.settingslib.devicestate + +import android.content.Context +import android.content.res.Resources +import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_FOLDED +import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_HALF_FOLDED +import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY +import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNFOLDED +import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNKNOWN +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.internal.R +import com.google.common.truth.Expect +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations + +private const val DEVICE_STATE_UNKNOWN = 0 +private const val DEVICE_STATE_CLOSED = 1 +private const val DEVICE_STATE_HALF_FOLDED = 2 +private const val DEVICE_STATE_OPEN = 3 +private const val DEVICE_STATE_REAR_DISPLAY = 4 + +@SmallTest +@RunWith(AndroidJUnit4::class) +class PosturesHelperTest { + + @get:Rule val expect: Expect = Expect.create() + + @Mock private lateinit var context: Context + + @Mock private lateinit var resources: Resources + + private lateinit var posturesHelper: PosturesHelper + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + whenever(context.resources).thenReturn(resources) + whenever(resources.getIntArray(R.array.config_foldedDeviceStates)) + .thenReturn(intArrayOf(DEVICE_STATE_CLOSED)) + whenever(resources.getIntArray(R.array.config_halfFoldedDeviceStates)) + .thenReturn(intArrayOf(DEVICE_STATE_HALF_FOLDED)) + whenever(resources.getIntArray(R.array.config_openDeviceStates)) + .thenReturn(intArrayOf(DEVICE_STATE_OPEN)) + whenever(resources.getIntArray(R.array.config_rearDisplayDeviceStates)) + .thenReturn(intArrayOf(DEVICE_STATE_REAR_DISPLAY)) + + posturesHelper = PosturesHelper(context) + } + + @Test + fun deviceStateToPosture_mapsCorrectly() { + expect + .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_CLOSED)) + .isEqualTo(DEVICE_STATE_ROTATION_KEY_FOLDED) + expect + .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_HALF_FOLDED)) + .isEqualTo(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED) + expect + .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_OPEN)) + .isEqualTo(DEVICE_STATE_ROTATION_KEY_UNFOLDED) + expect + .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_REAR_DISPLAY)) + .isEqualTo(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY) + expect + .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_UNKNOWN)) + .isEqualTo(DEVICE_STATE_ROTATION_KEY_UNKNOWN) + } + + @Test + fun postureToDeviceState_mapsCorrectly() { + expect + .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_FOLDED)) + .isEqualTo(DEVICE_STATE_CLOSED) + expect + .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED)) + .isEqualTo(DEVICE_STATE_HALF_FOLDED) + expect + .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_UNFOLDED)) + .isEqualTo(DEVICE_STATE_OPEN) + expect + .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY)) + .isEqualTo(DEVICE_STATE_REAR_DISPLAY) + expect.that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_UNKNOWN)).isNull() + } +} diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java index e4cc9f15aea1..6a5535d345db 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java @@ -100,6 +100,5 @@ public class SystemSettings { Settings.System.CAMERA_FLASH_NOTIFICATION, Settings.System.SCREEN_FLASH_NOTIFICATION, Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR, - Settings.System.SMOOTH_DISPLAY }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index 4b720636c1d4..85623b26c589 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -226,6 +226,5 @@ public class SystemSettingsValidators { VALIDATORS.put(System.CAMERA_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR); VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR); VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION_COLOR, ANY_INTEGER_VALIDATOR); - VALIDATORS.put(System.SMOOTH_DISPLAY, BOOLEAN_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index d1bd5e661072..46b45d12efc8 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -34,7 +34,6 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OV import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; import static com.android.internal.accessibility.util.AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM; -import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE; import static com.android.providers.settings.SettingsState.FALLBACK_FILE_SUFFIX; import static com.android.providers.settings.SettingsState.getTypeFromKey; import static com.android.providers.settings.SettingsState.getUserIdFromKey; @@ -5674,7 +5673,7 @@ public class SettingsProvider extends ContentProvider { providers.addAll(Arrays.asList(resources.getStringArray(resourceId))); } catch (Resources.NotFoundException e) { Slog.w(LOG_TAG, - "Get default array Cred Provider not found: " + e.toString()); + "Get default array Cred Provider not found: " + e.toString()); } try { final String storedValue = resources.getString(resourceId); @@ -5683,7 +5682,7 @@ public class SettingsProvider extends ContentProvider { } } catch (Resources.NotFoundException e) { Slog.w(LOG_TAG, - "Get default Cred Provider not found: " + e.toString()); + "Get default Cred Provider not found: " + e.toString()); } if (!providers.isEmpty()) { @@ -5732,8 +5731,8 @@ public class SettingsProvider extends ContentProvider { final Setting currentSetting = secureSettings .getSettingLocked(Settings.Secure.CREDENTIAL_SERVICE); if (currentSetting.isNull()) { - final int resourceId = com.android.internal.R.array - .config_defaultCredentialProviderService; + final int resourceId = + com.android.internal.R.array.config_defaultCredentialProviderService; final Resources resources = getContext().getResources(); // If the config has not be defined we might get an exception. final List<String> providers = new ArrayList<>(); @@ -5741,7 +5740,7 @@ public class SettingsProvider extends ContentProvider { providers.addAll(Arrays.asList(resources.getStringArray(resourceId))); } catch (Resources.NotFoundException e) { Slog.w(LOG_TAG, - "Get default array Cred Provider not found: " + e.toString()); + "Get default array Cred Provider not found: " + e.toString()); } if (!providers.isEmpty()) { @@ -5840,44 +5839,12 @@ public class SettingsProvider extends ContentProvider { currentVersion = 218; } - // v218: Convert Smooth Display and Force Peak Refresh Rate to a boolean if (currentVersion == 218) { - final String peakRefreshRateSettingName = "peak_refresh_rate"; - final String minRefreshRateSettingName = "min_refresh_rate"; - - final SettingsState systemSettings = getSystemSettingsLocked(userId); - final Setting peakRefreshRateSetting = - systemSettings.getSettingLocked(peakRefreshRateSettingName); - final Setting minRefreshRateSetting = - systemSettings.getSettingLocked(minRefreshRateSettingName); - - float peakRefreshRate = DEFAULT_REFRESH_RATE; - float minRefreshRate = 0; - try { - if (!peakRefreshRateSetting.isNull()) { - peakRefreshRate = Float.parseFloat(peakRefreshRateSetting.getValue()); - } - } catch (NumberFormatException e) { - // Do nothing. Overwrite with default value. - } - try { - if (!minRefreshRateSetting.isNull()) { - minRefreshRate = Float.parseFloat(minRefreshRateSetting.getValue()); - } - } catch (NumberFormatException e) { - // Do nothing. Overwrite with default value. - } - - systemSettings.deleteSettingLocked(peakRefreshRateSettingName); - systemSettings.deleteSettingLocked(minRefreshRateSettingName); - - systemSettings.insertSettingLocked(Settings.System.SMOOTH_DISPLAY, - peakRefreshRate > DEFAULT_REFRESH_RATE ? "1" : "0", /* tag= */ null, - /* makeDefault= */ false, SettingsState.SYSTEM_PACKAGE_NAME); - systemSettings.insertSettingLocked(Settings.System.FORCE_PEAK_REFRESH_RATE, - minRefreshRate > 0 ? "1" : "0", /* tag= */ null, - /* makeDefault= */ false, SettingsState.SYSTEM_PACKAGE_NAME); - + // Version 219: Removed + // TODO(b/211737588): Back up the Smooth Display setting + // Future upgrades to the `peak_refresh_rate` and `min_refresh_rate` settings + // should account for the database in a non-upgraded and upgraded (change id: + // Ib2cb2dd100f06f5452083b7606109a486e795a0e) state. currentVersion = 219; } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 36aa2ac74406..706666cbebab 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -97,7 +97,8 @@ public class SettingsBackupTest { Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug? Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only Settings.System.DESKTOP_MODE, // developer setting for internal prototyping - Settings.System.FORCE_PEAK_REFRESH_RATE, // depends on hardware capabilities + Settings.System.MIN_REFRESH_RATE, // depends on hardware capabilities + Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities Settings.System.SCREEN_BRIGHTNESS_FLOAT, Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 32d6b70f7f47..c8eb4b41f003 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -695,7 +695,9 @@ android:relinquishTaskIdentity="true" android:configChanges= "screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden" - android:visibleToInstantApps="true"/> + android:visibleToInstantApps="true" + android:exported="false" + android:permission="android.permission.MANAGE_MEDIA_PROJECTION"/> <!-- started from TvNotificationPanel --> <activity @@ -990,6 +992,18 @@ android:excludeFromRecents="true" android:resizeableActivity="false" android:theme="@android:style/Theme.NoDisplay" /> + + <activity + android:name=".notetask.LaunchNotesRoleSettingsTrampolineActivity" + android:exported="true" + android:excludeFromRecents="true" + android:resizeableActivity="false" + android:theme="@android:style/Theme.NoDisplay" > + <intent-filter> + <action android:name="com.android.systemui.action.MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> <!-- endregion --> <!-- started from ControlsRequestReceiver --> diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS index 77ddc6e573b7..1ce347215954 100644 --- a/packages/SystemUI/OWNERS +++ b/packages/SystemUI/OWNERS @@ -55,6 +55,7 @@ lynhan@google.com madym@google.com mankoff@google.com mateuszc@google.com +mgalhardo@google.com michaelmikhil@google.com michschn@google.com mkephart@google.com diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/DynamicColor.java b/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/DynamicColor.java index e6b2c2f9c7ed..e839d9bc3c52 100644 --- a/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/DynamicColor.java +++ b/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/DynamicColor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * 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. @@ -71,12 +71,11 @@ public final class DynamicColor { * The base constructor for DynamicColor. * * <p>Functional arguments allow overriding without risks that come with subclasses. _Strongly_ - * prefer using one of the static convenience constructors. This class is arguably too flexible - * to + * prefer using one of the static convenience constructors. This class is arguably too + * flexible to * ensure it can support any scenario. * - * <p>For example, the default behavior of adjust tone at max contrast to be at a 7.0 ratio - * with + * <p>For example, the default behavior of adjust tone at max contrast to be at a 7.0 ratio with * its background is principled and matches a11y guidance. That does not mean it's the desired * approach for _every_ design system, and every color pairing, always, in every case. * @@ -89,23 +88,23 @@ public final class DynamicColor { * lower and raise contrast are * made. * @param toneMinContrast given DynamicScheme, return tone in HCT/L* in L*a*b* this color - * should - * be at minimum contrast. See toneMinContrastDefault for the default - * behavior, and strongly + * should + * be at minimum contrast. See toneMinContrastDefault for the + * default behavior, and strongly * consider using it unless you have strong opinions on a11y. The * static constructors use it. * @param toneMaxContrast given DynamicScheme, return tone in HCT/L* in L*a*b* this color - * should - * be at maximum contrast. See toneMaxContrastDefault for the default - * behavior, and strongly + * should + * be at maximum contrast. See toneMaxContrastDefault for the + * default behavior, and strongly * consider using it unless you have strong opinions on a11y. The * static constructors use it. * @param toneDeltaConstraint given DynamicScheme, return a ToneDeltaConstraint instance that * describes a requirement that this DynamicColor must always have * some difference in tone/L* * from another DynamicColor.<br> - * Unlikely to be useful unless a design system has some distortions - * where colors that don't + * Unlikely to be useful unless a design system has some + * distortions where colors that don't * have a background/foreground relationship must have _some_ * difference in tone, yet, not * enough difference to create meaningful contrast. @@ -164,8 +163,8 @@ public final class DynamicColor { * <p>If the design system uses the same hex code on multiple backgrounds, define that in * multiple * DynamicColors so that the background is accurate for each one. If you define a DynamicColor - * with one background, and actually use it on another, DynamicColor can't guarantee contrast. - * For + * with one background, and actually use it on another, DynamicColor can't guarantee contrast + * . For * example, if you use a color on both black and white, increasing the contrast on one * necessarily * decreases contrast of the other. @@ -195,8 +194,8 @@ public final class DynamicColor { * for contrast, given a background, colors can adjust to * increase/decrease contrast. * @param toneDeltaConstraint Function that provides a ToneDeltaConstraint given DynamicScheme. - * Useful for ensuring lightness difference between colors that don't - * _require_ contrast or + * Useful for ensuring lightness difference between colors that + * don't _require_ contrast or * have a formal background/foreground relationship. */ public static DynamicColor fromArgb( @@ -212,17 +211,17 @@ public final class DynamicColor { * Create a DynamicColor. * * @param palette Function that provides a TonalPalette given DynamicScheme. A TonalPalette is - * defined by a hue and chroma, so this replaces the need to specify hue/chroma. - * By providing + * defined by a hue and chroma, so this replaces the need to specify + * hue/chroma. By providing * a tonal palette, when contrast adjustments are made, intended chroma can be * preserved. For * example, at T/L* 90, there is a significant limit to the amount of chroma. * There is no - * colorful red, a red that light is pink. By preserving the _intended_ chroma if - * lightness + * colorful red, a red that light is pink. By preserving the _intended_ chroma + * if lightness * lowers for contrast adjustments, the intended chroma is restored. - * @param tone Function that provides a tone given DynamicScheme. (useful for dark vs. light - * mode) + * @param tone Function that provides a tone given DynamicScheme. (useful for dark vs. + * light mode) */ public static DynamicColor fromPalette( Function<DynamicScheme, TonalPalette> palette, Function<DynamicScheme, Double> tone) { @@ -232,16 +231,16 @@ public final class DynamicColor { /** * Create a DynamicColor. * - * @param palette Function that provides a TonalPalette given DynamicScheme. A TonalPalette - * is + * @param palette Function that provides a TonalPalette given DynamicScheme. A + * TonalPalette is * defined by a hue and chroma, so this replaces the need to specify * hue/chroma. By providing - * a tonal palette, when contrast adjustments are made, intended chroma can be - * preserved. For - * example, at T/L* 90, there is a significant limit to the amount of chroma. - * There is no - * colorful red, a red that light is pink. By preserving the _intended_ chroma - * if lightness + * a tonal palette, when contrast adjustments are made, intended chroma can + * be preserved. For + * example, at T/L* 90, there is a significant limit to the amount of + * chroma. There is no + * colorful red, a red that light is pink. By preserving the _intended_ + * chroma if lightness * lowers for contrast adjustments, the intended chroma is restored. * @param tone Function that provides a tone given DynamicScheme. (useful for dark vs. * light mode) @@ -261,12 +260,12 @@ public final class DynamicColor { * * @param palette Function that provides a TonalPalette given DynamicScheme. A * TonalPalette is - * defined by a hue and chroma, so this replaces the need to specify - * hue/chroma. By providing + * defined by a hue and chroma, so this replaces the need to + * specify hue/chroma. By providing * a tonal palette, when contrast adjustments are made, intended * chroma can be preserved. For - * example, at T/L* 90, there is a significant limit to the amount of - * chroma. There is no + * example, at T/L* 90, there is a significant limit to the amount + * of chroma. There is no * colorful red, a red that light is pink. By preserving the * _intended_ chroma if lightness * lowers for contrast adjustments, the intended chroma is restored. @@ -277,8 +276,8 @@ public final class DynamicColor { * for contrast, given a background, colors can adjust to * increase/decrease contrast. * @param toneDeltaConstraint Function that provides a ToneDeltaConstraint given DynamicScheme. - * Useful for ensuring lightness difference between colors that don't - * _require_ contrast or + * Useful for ensuring lightness difference between colors that + * don't _require_ contrast or * have a formal background/foreground relationship. */ public static DynamicColor fromPalette( @@ -297,96 +296,6 @@ public final class DynamicColor { toneDeltaConstraint); } - /** Returns the ARGB (i.e. hex code) representation of the resolved color given scheme. */ - public int getArgb(DynamicScheme scheme) { - final int argb = getHct(scheme).toInt(); - if (opacity == null) { - return argb; - } - final double percentage = opacity.apply(scheme); - final int alpha = MathUtils.clampInt(0, 255, (int) Math.round(percentage * 255)); - return (argb & 0x00ffffff) | (alpha << 24); - } - - /** Returns the HCT representation of the resolved color given scheme. */ - public Hct getHct(DynamicScheme scheme) { - final Hct cachedAnswer = hctCache.get(scheme); - if (cachedAnswer != null) { - return cachedAnswer; - } - // This is crucial for aesthetics: we aren't simply the taking the standard color - // and changing its tone for contrast. Rather, we find the tone for contrast, then - // use the specified chroma from the palette to construct a new color. - // - // For example, this enables colors with standard tone of T90, which has limited chroma, to - // "recover" intended chroma as contrast increases. - final Hct answer = Hct.from(hue.apply(scheme), chroma.apply(scheme), getTone(scheme)); - // NOMUTANTS--trivial test with onerous dependency injection requirement. - if (hctCache.size() > 4) { - hctCache.clear(); - } - // NOMUTANTS--trivial test with onerous dependency injection requirement. - hctCache.put(scheme, answer); - return answer; - } - - /** Returns the tone in HCT, ranging from 0 to 100, of the resolved color given scheme. */ - public double getTone(DynamicScheme scheme) { - double answer = tone.apply(scheme); - - final boolean decreasingContrast = scheme.contrastLevel < 0.0; - if (scheme.contrastLevel != 0.0) { - final double startTone = tone.apply(scheme); - final double endTone = - decreasingContrast ? toneMinContrast.apply(scheme) : toneMaxContrast.apply( - scheme); - final double delta = (endTone - startTone) * Math.abs(scheme.contrastLevel); - answer = delta + startTone; - } - - final DynamicColor bgDynamicColor = background == null ? null : background.apply(scheme); - double minRatio = Contrast.RATIO_MIN; - double maxRatio = Contrast.RATIO_MAX; - if (bgDynamicColor != null) { - final boolean bgHasBg = - bgDynamicColor.background != null && bgDynamicColor.background.apply(scheme) - != null; - final double standardRatio = - Contrast.ratioOfTones(tone.apply(scheme), bgDynamicColor.tone.apply(scheme)); - if (decreasingContrast) { - final double minContrastRatio = - Contrast.ratioOfTones( - toneMinContrast.apply(scheme), - bgDynamicColor.toneMinContrast.apply(scheme)); - minRatio = bgHasBg ? minContrastRatio : 1.0; - maxRatio = standardRatio; - } else { - final double maxContrastRatio = - Contrast.ratioOfTones( - toneMaxContrast.apply(scheme), - bgDynamicColor.toneMaxContrast.apply(scheme)); - minRatio = bgHasBg ? min(maxContrastRatio, standardRatio) : 1.0; - maxRatio = bgHasBg ? max(maxContrastRatio, standardRatio) : 21.0; - } - } - - final double finalMinRatio = minRatio; - final double finalMaxRatio = maxRatio; - final double finalAnswer = answer; - answer = - calculateDynamicTone( - scheme, - this.tone, - (dynamicColor) -> dynamicColor.getTone(scheme), - (a, b) -> finalAnswer, - (s) -> bgDynamicColor, - toneDeltaConstraint, - (s) -> finalMinRatio, - (s) -> finalMaxRatio); - - return answer; - } - /** * The default algorithm for calculating the tone of a color at minimum contrast.<br> * If the original contrast ratio was >= 7.0, reach contrast 4.5.<br> @@ -475,8 +384,8 @@ public final class DynamicColor { * <p>It enforces important properties:<br> * #1. Desired contrast ratio is reached.<br> * As contrast increases from standard to max, the tones involved should always be at least the - * standard ratio. For example, if a button is T90, and button text is T0, and the button is T0 - * at + * standard ratio. For example, if a button is T90, and button text is T0, and the button is + * T0 at * max contrast, the button text cannot simply linearly interpolate from T0 to T100, or at some * point they'll both be at the same tone. * @@ -489,8 +398,7 @@ public final class DynamicColor { * * <p>#3. Ensure tone delta with another color.<br> * In design systems, there may be colors that don't have a pure background/foreground - * relationship, but, do require different tones for visual differentiation. - * ToneDeltaConstraint + * relationship, but, do require different tones for visual differentiation. ToneDeltaConstraint * models this requirement, and DynamicColor enforces it. */ public static double calculateDynamicTone( @@ -596,13 +504,16 @@ public final class DynamicColor { final boolean preferLighter = tonePrefersLightForeground(bgTone); if (preferLighter) { - // "Negligible difference" handles an edge case where the initial contrast ratio is high + // "Neglible difference" handles an edge case where the initial contrast ratio is high // (ex. 13.0), and the ratio passed to the function is that high ratio, and both the - // lighter and darker ratio fails to pass that ratio. + // lighter + // and darker ratio fails to pass that ratio. // // This was observed with Tonal Spot's On Primary Container turning black momentarily - // between high and max contrast in light mode. PC's standard tone was T90, OPC's was - // T10, it was light mode, and the contrast level was 0.6568521221032331. + // between + // high and max contrast in light mode. PC's standard tone was T90, OPC's was T10, it + // was + // light mode, and the contrast level was 0.6568521221032331. final boolean negligibleDifference = Math.abs(lighterRatio - darkerRatio) < 0.1 && lighterRatio < ratio && darkerRatio < ratio; @@ -634,13 +545,109 @@ public final class DynamicColor { * <p>T60 used as to create the smallest discontinuity possible when skipping down to T49 in * order * to ensure light foregrounds. + * + * <p>Since `tertiaryContainer` in dark monochrome scheme requires a tone of 60, it should + * not be + * adjusted. Therefore, 60 is excluded here. */ public static boolean tonePrefersLightForeground(double tone) { - return Math.round(tone) <= 60; + return Math.round(tone) < 60; } - /** Tones less than ~T50 always permit white at 4.5 contrast. */ + /** + * Tones less than ~T50 always permit white at 4.5 contrast. + */ public static boolean toneAllowsLightForeground(double tone) { return Math.round(tone) <= 49; } + + public int getArgb(DynamicScheme scheme) { + final int argb = getHct(scheme).toInt(); + if (opacity == null) { + return argb; + } + final double percentage = opacity.apply(scheme); + final int alpha = MathUtils.clampInt(0, 255, (int) Math.round(percentage * 255)); + return (argb & 0x00ffffff) | (alpha << 24); + } + + public Hct getHct(DynamicScheme scheme) { + final Hct cachedAnswer = hctCache.get(scheme); + if (cachedAnswer != null) { + return cachedAnswer; + } + // This is crucial for aesthetics: we aren't simply the taking the standard color + // and changing its tone for contrast. Rather, we find the tone for contrast, then + // use the specified chroma from the palette to construct a new color. + // + // For example, this enables colors with standard tone of T90, which has limited chroma, to + // "recover" intended chroma as contrast increases. + final Hct answer = Hct.from(hue.apply(scheme), chroma.apply(scheme), getTone(scheme)); + // NOMUTANTS--trivial test with onerous dependency injection requirement. + if (hctCache.size() > 4) { + hctCache.clear(); + } + // NOMUTANTS--trivial test with onerous dependency injection requirement. + hctCache.put(scheme, answer); + return answer; + } + + /** + * Returns the tone in HCT, ranging from 0 to 100, of the resolved color given scheme. + */ + public double getTone(DynamicScheme scheme) { + double answer = tone.apply(scheme); + + final boolean decreasingContrast = scheme.contrastLevel < 0.0; + if (scheme.contrastLevel != 0.0) { + final double startTone = tone.apply(scheme); + final double endTone = + decreasingContrast ? toneMinContrast.apply(scheme) : toneMaxContrast.apply( + scheme); + final double delta = (endTone - startTone) * Math.abs(scheme.contrastLevel); + answer = delta + startTone; + } + + final DynamicColor bgDynamicColor = background == null ? null : background.apply(scheme); + double minRatio = Contrast.RATIO_MIN; + double maxRatio = Contrast.RATIO_MAX; + if (bgDynamicColor != null) { + final boolean bgHasBg = + bgDynamicColor.background != null && bgDynamicColor.background.apply(scheme) + != null; + final double standardRatio = + Contrast.ratioOfTones(tone.apply(scheme), bgDynamicColor.tone.apply(scheme)); + if (decreasingContrast) { + final double minContrastRatio = + Contrast.ratioOfTones( + toneMinContrast.apply(scheme), + bgDynamicColor.toneMinContrast.apply(scheme)); + minRatio = bgHasBg ? minContrastRatio : 1.0; + maxRatio = standardRatio; + } else { + final double maxContrastRatio = + Contrast.ratioOfTones( + toneMaxContrast.apply(scheme), + bgDynamicColor.toneMaxContrast.apply(scheme)); + minRatio = bgHasBg ? min(maxContrastRatio, standardRatio) : 1.0; + maxRatio = bgHasBg ? max(maxContrastRatio, standardRatio) : 21.0; + } + } + + final double finalMinRatio = minRatio; + final double finalMaxRatio = maxRatio; + final double finalAnswer = answer; + answer = + calculateDynamicTone( + scheme, + this.tone, + (dynamicColor) -> dynamicColor.getTone(scheme), + (a, b) -> finalAnswer, + (s) -> bgDynamicColor, + toneDeltaConstraint, + (s) -> finalMinRatio, + (s) -> finalMaxRatio); + + return answer; + } } diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java b/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java index 5212e8ef1187..21218a2dc8e2 100644 --- a/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java +++ b/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java @@ -22,7 +22,7 @@ import com.android.systemui.monet.hct.ViewingConditions; import com.android.systemui.monet.scheme.DynamicScheme; import com.android.systemui.monet.scheme.Variant; -/** Named colors, otherwise known as tokens, or roles, in the Material Design system. */ +/** Named colors, otherwise known as tokens, or roles, in the Material Design system.*/ // Prevent lint for Function.apply not being available on Android before API level 14 (4.0.1). // "AndroidJdkLibsChecker" for Function, "NewApi" for Function.apply(). // A java_library Bazel rule with an Android constraint cannot skip these warnings without this @@ -33,341 +33,54 @@ public final class MaterialDynamicColors { private static final double CONTAINER_ACCENT_TONE_DELTA = 15.0; - private MaterialDynamicColors() { + public MaterialDynamicColors() { } - public static DynamicColor highestSurface(DynamicScheme s) { - return s.isDark ? surfaceBright : surfaceDim; - } - - public static final DynamicColor background = - DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 98.0); - - public static final DynamicColor onBackground = - DynamicColor.fromPalette( - (s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 10.0, (s) -> background); - - public static final DynamicColor surface = - DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 98.0); - - public static final DynamicColor surfaceInverse = - DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 30.0); - - public static final DynamicColor surfaceBright = - DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 24.0 : 98.0); - - public static final DynamicColor surfaceDim = - DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 87.0); - - public static final DynamicColor surfaceSub2 = - DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 4.0 : 100.0); - - public static final DynamicColor surfaceSub1 = - DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 96.0); - - public static final DynamicColor surfaceContainer = - DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 12.0 : 94.0); - - public static final DynamicColor surfaceAdd1 = - DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 17.0 : 92.0); - - public static final DynamicColor surfaceAdd2 = - DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 22.0 : 90.0); - - public static final DynamicColor onSurface = - DynamicColor.fromPalette( - (s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 10.0, - (s) -> highestSurface(s)); - - public static final DynamicColor onSurfaceInverse = - DynamicColor.fromPalette( - (s) -> s.neutralPalette, (s) -> s.isDark ? 20.0 : 95.0, (s) -> surfaceInverse); - - public static final DynamicColor surfaceVariant = - DynamicColor.fromPalette((s) -> s.neutralVariantPalette, (s) -> s.isDark ? 30.0 : 90.0); - - public static final DynamicColor onSurfaceVariant = - DynamicColor.fromPalette( - (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 80.0 : 30.0, - (s) -> surfaceVariant); - - public static final DynamicColor outline = - DynamicColor.fromPalette( - (s) -> s.neutralVariantPalette, (s) -> 50.0, (s) -> highestSurface(s)); - - public static final DynamicColor outlineVariant = - DynamicColor.fromPalette( - (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 30.0 : 80.0, - (s) -> highestSurface(s)); - - public static final DynamicColor primaryContainer = - DynamicColor.fromPalette( - (s) -> s.primaryPalette, - (s) -> { - if (!isFidelity(s)) { - return s.isDark ? 30.0 : 90.0; - } - return performAlbers(s.sourceColorHct, s); - }, - (s) -> highestSurface(s)); - - public static final DynamicColor onPrimaryContainer = - DynamicColor.fromPalette( - (s) -> s.primaryPalette, - (s) -> { - if (!isFidelity(s)) { - return s.isDark ? 90.0 : 10.0; - } - return DynamicColor.contrastingTone(primaryContainer.tone.apply(s), 4.5); - }, - (s) -> primaryContainer, - null); - - public static final DynamicColor primary = - DynamicColor.fromPalette( - (s) -> s.primaryPalette, - (s) -> s.isDark ? 80.0 : 40.0, - (s) -> highestSurface(s), - (s) -> - new ToneDeltaConstraint( - CONTAINER_ACCENT_TONE_DELTA, - primaryContainer, - s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER)); - - public static final DynamicColor primaryInverse = - DynamicColor.fromPalette( - (s) -> s.primaryPalette, (s) -> s.isDark ? 40.0 : 80.0, (s) -> surfaceInverse); - - public static final DynamicColor onPrimary = - DynamicColor.fromPalette( - (s) -> s.primaryPalette, (s) -> s.isDark ? 20.0 : 100.0, (s) -> primary); - - public static final DynamicColor secondaryContainer = - DynamicColor.fromPalette( - (s) -> s.secondaryPalette, - (s) -> { - final double initialTone = s.isDark ? 30.0 : 90.0; - if (!isFidelity(s)) { - return initialTone; - } - double answer = - findDesiredChromaByTone( - s.secondaryPalette.getHue(), - s.secondaryPalette.getChroma(), - initialTone, - !s.isDark); - answer = performAlbers(s.secondaryPalette.getHct(answer), s); - return answer; - }, - (s) -> highestSurface(s)); - - public static final DynamicColor onSecondaryContainer = - DynamicColor.fromPalette( - (s) -> s.secondaryPalette, - (s) -> { - if (!isFidelity(s)) { - return s.isDark ? 90.0 : 10.0; - } - return DynamicColor.contrastingTone(secondaryContainer.tone.apply(s), 4.5); - }, - (s) -> secondaryContainer); - - public static final DynamicColor secondary = - DynamicColor.fromPalette( - (s) -> s.secondaryPalette, - (s) -> s.isDark ? 80.0 : 40.0, - (s) -> highestSurface(s), - (s) -> - new ToneDeltaConstraint( - CONTAINER_ACCENT_TONE_DELTA, - secondaryContainer, - s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER)); - - public static final DynamicColor onSecondary = - DynamicColor.fromPalette( - (s) -> s.secondaryPalette, (s) -> s.isDark ? 20.0 : 100.0, (s) -> secondary); - - public static final DynamicColor tertiaryContainer = - DynamicColor.fromPalette( - (s) -> s.tertiaryPalette, - (s) -> { - if (!isFidelity(s)) { - return s.isDark ? 30.0 : 90.0; - } - final double albersTone = - performAlbers(s.tertiaryPalette.getHct(s.sourceColorHct.getTone()), - s); - final Hct proposedHct = s.tertiaryPalette.getHct(albersTone); - return DislikeAnalyzer.fixIfDisliked(proposedHct).getTone(); - }, - (s) -> highestSurface(s)); - - public static final DynamicColor onTertiaryContainer = - DynamicColor.fromPalette( - (s) -> s.tertiaryPalette, - (s) -> { - if (!isFidelity(s)) { - return s.isDark ? 90.0 : 10.0; - } - return DynamicColor.contrastingTone(tertiaryContainer.tone.apply(s), 4.5); - }, - (s) -> tertiaryContainer); - - public static final DynamicColor tertiary = - DynamicColor.fromPalette( - (s) -> s.tertiaryPalette, - (s) -> s.isDark ? 80.0 : 40.0, - (s) -> highestSurface(s), - (s) -> - new ToneDeltaConstraint( - CONTAINER_ACCENT_TONE_DELTA, - tertiaryContainer, - s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER)); - - public static final DynamicColor onTertiary = - DynamicColor.fromPalette( - (s) -> s.tertiaryPalette, (s) -> s.isDark ? 20.0 : 100.0, (s) -> tertiary); - - public static final DynamicColor errorContainer = - DynamicColor.fromPalette( - (s) -> s.errorPalette, (s) -> s.isDark ? 30.0 : 90.0, (s) -> highestSurface(s)); - - public static final DynamicColor onErrorContainer = - DynamicColor.fromPalette( - (s) -> s.errorPalette, (s) -> s.isDark ? 90.0 : 10.0, (s) -> errorContainer); - - public static final DynamicColor error = - DynamicColor.fromPalette( - (s) -> s.errorPalette, - (s) -> s.isDark ? 80.0 : 40.0, - (s) -> highestSurface(s), - (s) -> - new ToneDeltaConstraint( - CONTAINER_ACCENT_TONE_DELTA, - errorContainer, - s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER)); - - public static final DynamicColor onError = - DynamicColor.fromPalette((s) -> s.errorPalette, (s) -> s.isDark ? 20.0 : 100.0, - (s) -> error); - - public static final DynamicColor primaryFixed = - DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> 90.0, - (s) -> highestSurface(s)); - - public static final DynamicColor primaryFixedDarker = - DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> 80.0, - (s) -> highestSurface(s)); - - public static final DynamicColor onPrimaryFixed = - DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> 10.0, - (s) -> primaryFixedDarker); - - public static final DynamicColor onPrimaryFixedVariant = - DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> 30.0, - (s) -> primaryFixedDarker); - - public static final DynamicColor secondaryFixed = - DynamicColor.fromPalette((s) -> s.secondaryPalette, (s) -> 90.0, - (s) -> highestSurface(s)); - - public static final DynamicColor secondaryFixedDarker = - DynamicColor.fromPalette((s) -> s.secondaryPalette, (s) -> 80.0, - (s) -> highestSurface(s)); - - public static final DynamicColor onSecondaryFixed = - DynamicColor.fromPalette((s) -> s.secondaryPalette, (s) -> 10.0, - (s) -> secondaryFixedDarker); - - public static final DynamicColor onSecondaryFixedVariant = - DynamicColor.fromPalette((s) -> s.secondaryPalette, (s) -> 30.0, - (s) -> secondaryFixedDarker); - - public static final DynamicColor tertiaryFixed = - DynamicColor.fromPalette((s) -> s.tertiaryPalette, (s) -> 90.0, - (s) -> highestSurface(s)); - - public static final DynamicColor tertiaryFixedDarker = - DynamicColor.fromPalette((s) -> s.tertiaryPalette, (s) -> 80.0, - (s) -> highestSurface(s)); - - public static final DynamicColor onTertiaryFixed = - DynamicColor.fromPalette((s) -> s.tertiaryPalette, (s) -> 10.0, - (s) -> tertiaryFixedDarker); - - public static final DynamicColor onTertiaryFixedVariant = - DynamicColor.fromPalette((s) -> s.tertiaryPalette, (s) -> 30.0, - (s) -> tertiaryFixedDarker); - /** * These colors were present in Android framework before Android U, and used by MDC controls. * They * should be avoided, if possible. It's unclear if they're used on multiple backgrounds, and if - * they are, they can't be adjusted for contrast.* For now, they will be set with no - * background, + * they are, they can't be adjusted for contrast.* For now, they will be set with no background, * and those won't adjust for contrast, avoiding issues. * - * <p>* For example, if the same color is on a white background _and_ black background, there's - * no + * <p>* For example, if the same color is on a white background _and_ black background, + * there's no * way to increase contrast with either without losing contrast with the other. */ // colorControlActivated documented as colorAccent in M3 & GM3. // colorAccent documented as colorSecondary in M3 and colorPrimary in GM3. // Android used Material's Container as Primary/Secondary/Tertiary at launch. // Therefore, this is a duplicated version of Primary Container. - public static final DynamicColor controlActivated = - DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> s.isDark ? 30.0 : 90.0, null); - - // colorControlNormal documented as textColorSecondary in M3 & GM3. - // In Material, textColorSecondary points to onSurfaceVariant in the non-disabled state, - // which is Neutral Variant T30/80 in light/dark. - public static final DynamicColor controlNormal = - DynamicColor.fromPalette((s) -> s.neutralVariantPalette, (s) -> s.isDark ? 80.0 : 30.0); - - // colorControlHighlight documented, in both M3 & GM3: - // Light mode: #1f000000 dark mode: #33ffffff. - // These are black and white with some alpha. - // 1F hex = 31 decimal; 31 / 255 = 12% alpha. - // 33 hex = 51 decimal; 51 / 255 = 20% alpha. - // DynamicColors do not support alpha currently, and _may_ not need it for this use case, - // depending on how MDC resolved alpha for the other cases. - // Returning black in dark mode, white in light mode. - public static final DynamicColor controlHighlight = - new DynamicColor( - s -> 0.0, - s -> 0.0, - s -> s.isDark ? 100.0 : 0.0, - s -> s.isDark ? 0.20 : 0.12, - null, - scheme -> - DynamicColor.toneMinContrastDefault( - (s) -> s.isDark ? 100.0 : 0.0, null, scheme, null), - scheme -> - DynamicColor.toneMaxContrastDefault( - (s) -> s.isDark ? 100.0 : 0.0, null, scheme, null), - null); + public static DynamicColor controlActivated() { + return DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> s.isDark ? 30.0 : 90.0, null); + } - // textColorPrimaryInverse documented, in both M3 & GM3, documented as N10/N90. - public static final DynamicColor textPrimaryInverse = - DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0); + // Compatibility Keys Colors for Android + public static DynamicColor primaryPaletteKeyColor() { + return DynamicColor.fromPalette( + (s) -> s.primaryPalette, (s) -> s.primaryPalette.getKeyColor().getTone()); + } - // textColorSecondaryInverse and textColorTertiaryInverse both documented, in both M3 & GM3, as - // NV30/NV80 - public static final DynamicColor textSecondaryAndTertiaryInverse = - DynamicColor.fromPalette((s) -> s.neutralVariantPalette, (s) -> s.isDark ? 30.0 : 80.0); + public static DynamicColor secondaryPaletteKeyColor() { + return DynamicColor.fromPalette( + (s) -> s.secondaryPalette, (s) -> s.secondaryPalette.getKeyColor().getTone()); + } - // textColorPrimaryInverseDisableOnly documented, in both M3 & GM3, as N10/N90 - public static final DynamicColor textPrimaryInverseDisableOnly = - DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0); + public static DynamicColor tertiaryPaletteKeyColor() { + return DynamicColor.fromPalette( + (s) -> s.tertiaryPalette, (s) -> s.tertiaryPalette.getKeyColor().getTone()); + } - // textColorSecondaryInverse and textColorTertiaryInverse in disabled state both documented, - // in both M3 & GM3, as N10/N90 - public static final DynamicColor textSecondaryAndTertiaryInverseDisabled = - DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0); + public static DynamicColor neutralPaletteKeyColor() { + return DynamicColor.fromPalette( + (s) -> s.neutralPalette, (s) -> s.neutralPalette.getKeyColor().getTone()); + } - // textColorHintInverse documented, in both M3 & GM3, as N10/N90 - public static final DynamicColor textHintInverse = - DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0); + public static DynamicColor neutralVariantPaletteKeyColor() { + return DynamicColor.fromPalette( + (s) -> s.neutralVariantPalette, + (s) -> s.neutralVariantPalette.getKeyColor().getTone()); + } private static ViewingConditions viewingConditionsForAlbers(DynamicScheme scheme) { return ViewingConditions.defaultWithBackgroundLstar(scheme.isDark ? 30.0 : 80.0); @@ -377,6 +90,10 @@ public final class MaterialDynamicColors { return scheme.variant == Variant.FIDELITY || scheme.variant == Variant.CONTENT; } + private static boolean isMonochrome(DynamicScheme scheme) { + return scheme.variant == Variant.MONOCHROME; + } + static double findDesiredChromaByTone( double hue, double chroma, double tone, boolean byDecreasingTone) { double answer = tone; @@ -416,34 +133,456 @@ public final class MaterialDynamicColors { } } - // Compatibility mappings for Android - public static final DynamicColor surfaceContainerLow = surfaceSub1; - public static final DynamicColor surfaceContainerLowest = surfaceSub2; - public static final DynamicColor surfaceContainerHigh = surfaceAdd1; - public static final DynamicColor surfaceContainerHighest = surfaceAdd2; - public static final DynamicColor primaryFixedDim = primaryFixedDarker; - public static final DynamicColor secondaryFixedDim = secondaryFixedDarker; - public static final DynamicColor tertiaryFixedDim = tertiaryFixedDarker; + public static DynamicColor highestSurface(DynamicScheme s) { + return s.isDark ? surfaceBright() : surfaceDim(); + } - // Compatibility Keys Colors for Android - public static final DynamicColor primaryPaletteKeyColor = - DynamicColor.fromPalette( - (s) -> s.primaryPalette, (s) -> s.primaryPalette.getKeyColor().getTone()); - - public static final DynamicColor secondaryPaletteKeyColor = - DynamicColor.fromPalette( - (s) -> s.secondaryPalette, (s) -> s.secondaryPalette.getKeyColor().getTone()); - - public static final DynamicColor tertiaryPaletteKeyColor = - DynamicColor.fromPalette( - (s) -> s.tertiaryPalette, (s) -> s.tertiaryPalette.getKeyColor().getTone()); - - public static final DynamicColor neutralPaletteKeyColor = - DynamicColor.fromPalette( - (s) -> s.neutralPalette, (s) -> s.neutralPalette.getKeyColor().getTone()); - - public static final DynamicColor neutralVariantPaletteKeyColor = - DynamicColor.fromPalette( - (s) -> s.neutralVariantPalette, - (s) -> s.neutralVariantPalette.getKeyColor().getTone()); + public static DynamicColor background() { + return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 98.0); + } + + public static DynamicColor onBackground() { + return DynamicColor.fromPalette( + (s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 10.0, (s) -> background()); + } + + public static DynamicColor surface() { + return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 98.0); + } + + public static DynamicColor inverseSurface() { + return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 20.0); + } + + public static DynamicColor surfaceBright() { + return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 24.0 : 98.0); + } + + public static DynamicColor surfaceDim() { + return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 87.0); + } + + public static DynamicColor surfaceContainerLowest() { + return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 4.0 : 100.0); + } + + public static DynamicColor surfaceContainerLow() { + return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 96.0); + } + + public static DynamicColor surfaceContainer() { + return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 12.0 : 94.0); + } + + public static DynamicColor surfaceContainerHigh() { + return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 17.0 : 92.0); + } + + public static DynamicColor surfaceContainerHighest() { + return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 22.0 : 90.0); + } + + public static DynamicColor onSurface() { + return DynamicColor.fromPalette( + (s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 10.0, + MaterialDynamicColors::highestSurface); + } + + public static DynamicColor inverseOnSurface() { + return DynamicColor.fromPalette( + (s) -> s.neutralPalette, (s) -> s.isDark ? 20.0 : 95.0, (s) -> inverseSurface()); + } + + public static DynamicColor surfaceVariant() { + return DynamicColor.fromPalette((s) -> s.neutralVariantPalette, + (s) -> s.isDark ? 30.0 : 90.0); + } + + public static DynamicColor onSurfaceVariant() { + return DynamicColor.fromPalette( + (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 80.0 : 30.0, + (s) -> surfaceVariant()); + } + + public static DynamicColor outline() { + return DynamicColor.fromPalette( + (s) -> s.neutralVariantPalette, (s) -> 50.0, MaterialDynamicColors::highestSurface); + } + + public static DynamicColor outlineVariant() { + return DynamicColor.fromPalette( + (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 30.0 : 80.0, + MaterialDynamicColors::highestSurface); + } + + public static DynamicColor primaryContainer() { + return DynamicColor.fromPalette( + (s) -> s.primaryPalette, + (s) -> { + if (isFidelity(s)) { + return performAlbers(s.sourceColorHct, s); + } + if (isMonochrome(s)) { + return s.isDark ? 85.0 : 25.0; + } + return s.isDark ? 30.0 : 90.0; + }, + MaterialDynamicColors::highestSurface); + } + + public static DynamicColor onPrimaryContainer() { + return DynamicColor.fromPalette( + (s) -> s.primaryPalette, + (s) -> { + if (isFidelity(s)) { + return DynamicColor.contrastingTone(primaryContainer().tone.apply(s), 4.5); + } + if (isMonochrome(s)) { + return s.isDark ? 0.0 : 100.0; + } + return s.isDark ? 90.0 : 10.0; + }, + (s) -> primaryContainer(), + null); + } + + public static DynamicColor primary() { + return DynamicColor.fromPalette( + (s) -> s.primaryPalette, + (s) -> { + if (isMonochrome(s)) { + return s.isDark ? 100.0 : 0.0; + } + return s.isDark ? 80.0 : 40.0; + }, + MaterialDynamicColors::highestSurface, + (s) -> + new ToneDeltaConstraint( + CONTAINER_ACCENT_TONE_DELTA, + primaryContainer(), + s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER)); + } + + public static DynamicColor inversePrimary() { + return DynamicColor.fromPalette( + (s) -> s.primaryPalette, (s) -> s.isDark ? 40.0 : 80.0, (s) -> inverseSurface()); + } + + public static DynamicColor onPrimary() { + return DynamicColor.fromPalette( + (s) -> s.primaryPalette, + (s) -> { + if (isMonochrome(s)) { + return s.isDark ? 10.0 : 90.0; + } + return s.isDark ? 20.0 : 100.0; + }, + (s) -> primary()); + } + + public static DynamicColor secondaryContainer() { + return DynamicColor.fromPalette( + (s) -> s.secondaryPalette, + (s) -> { + if (isMonochrome(s)) { + return s.isDark ? 30.0 : 85.0; + } + final double initialTone = s.isDark ? 30.0 : 90.0; + if (!isFidelity(s)) { + return initialTone; + } + double answer = + findDesiredChromaByTone( + s.secondaryPalette.getHue(), + s.secondaryPalette.getChroma(), + initialTone, + !s.isDark); + answer = performAlbers(s.secondaryPalette.getHct(answer), s); + return answer; + }, + MaterialDynamicColors::highestSurface); + } + + public static DynamicColor onSecondaryContainer() { + return DynamicColor.fromPalette( + (s) -> s.secondaryPalette, + (s) -> { + if (!isFidelity(s)) { + return s.isDark ? 90.0 : 10.0; + } + return DynamicColor.contrastingTone(secondaryContainer().tone.apply(s), 4.5); + }, + (s) -> secondaryContainer()); + } + + public static DynamicColor secondary() { + return DynamicColor.fromPalette( + (s) -> s.secondaryPalette, + (s) -> s.isDark ? 80.0 : 40.0, + MaterialDynamicColors::highestSurface, + (s) -> + new ToneDeltaConstraint( + CONTAINER_ACCENT_TONE_DELTA, + secondaryContainer(), + s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER)); + } + + public static DynamicColor onSecondary() { + return DynamicColor.fromPalette( + (s) -> s.secondaryPalette, + (s) -> { + if (isMonochrome(s)) { + return s.isDark ? 10.0 : 100.0; + } + return s.isDark ? 20.0 : 100.0; + }, + (s) -> secondary()); + } + + public static DynamicColor tertiaryContainer() { + return DynamicColor.fromPalette( + (s) -> s.tertiaryPalette, + (s) -> { + if (isMonochrome(s)) { + return s.isDark ? 60.0 : 49.0; + } + if (!isFidelity(s)) { + return s.isDark ? 30.0 : 90.0; + } + final double albersTone = + performAlbers(s.tertiaryPalette.getHct(s.sourceColorHct.getTone()), s); + final Hct proposedHct = s.tertiaryPalette.getHct(albersTone); + return DislikeAnalyzer.fixIfDisliked(proposedHct).getTone(); + }, + MaterialDynamicColors::highestSurface); + } + + public static DynamicColor onTertiaryContainer() { + return DynamicColor.fromPalette( + (s) -> s.tertiaryPalette, + (s) -> { + if (isMonochrome(s)) { + return s.isDark ? 0.0 : 100.0; + } + if (!isFidelity(s)) { + return s.isDark ? 90.0 : 10.0; + } + return DynamicColor.contrastingTone(tertiaryContainer().tone.apply(s), 4.5); + }, + (s) -> tertiaryContainer()); + } + + public static DynamicColor tertiary() { + return DynamicColor.fromPalette( + (s) -> s.tertiaryPalette, + (s) -> { + if (isMonochrome(s)) { + return s.isDark ? 90.0 : 25.0; + } + return s.isDark ? 80.0 : 40.0; + }, + MaterialDynamicColors::highestSurface, + (s) -> + new ToneDeltaConstraint( + CONTAINER_ACCENT_TONE_DELTA, + tertiaryContainer(), + s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER)); + } + + public static DynamicColor onTertiary() { + return DynamicColor.fromPalette( + (s) -> s.tertiaryPalette, + (s) -> { + if (isMonochrome(s)) { + return s.isDark ? 10.0 : 90.0; + } + return s.isDark ? 20.0 : 100.0; + }, + (s) -> tertiary()); + } + + public static DynamicColor errorContainer() { + return DynamicColor.fromPalette( + (s) -> s.errorPalette, (s) -> s.isDark ? 30.0 : 90.0, + MaterialDynamicColors::highestSurface); + } + + public static DynamicColor onErrorContainer() { + return DynamicColor.fromPalette( + (s) -> s.errorPalette, (s) -> s.isDark ? 90.0 : 10.0, (s) -> errorContainer()); + } + + public static DynamicColor error() { + return DynamicColor.fromPalette( + (s) -> s.errorPalette, + (s) -> s.isDark ? 80.0 : 40.0, + MaterialDynamicColors::highestSurface, + (s) -> + new ToneDeltaConstraint( + CONTAINER_ACCENT_TONE_DELTA, + errorContainer(), + s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER)); + } + + public static DynamicColor onError() { + return DynamicColor.fromPalette( + (s) -> s.errorPalette, (s) -> s.isDark ? 20.0 : 100.0, (s) -> error()); + } + + public static DynamicColor primaryFixed() { + return DynamicColor.fromPalette( + (s) -> s.primaryPalette, + (s) -> { + if (isMonochrome(s)) { + return s.isDark ? 100.0 : 10.0; + } + return 90.0; + }, + MaterialDynamicColors::highestSurface); + } + + public static DynamicColor primaryFixedDim() { + return DynamicColor.fromPalette( + (s) -> s.primaryPalette, + (s) -> { + if (isMonochrome(s)) { + return s.isDark ? 90.0 : 20.0; + } + return 80.0; + }, + MaterialDynamicColors::highestSurface); + } + + public static DynamicColor onPrimaryFixed() { + return DynamicColor.fromPalette( + (s) -> s.primaryPalette, + (s) -> { + if (isMonochrome(s)) { + return s.isDark ? 10.0 : 90.0; + } + return 10.0; + }, + (s) -> primaryFixedDim()); + } + + public static DynamicColor onPrimaryFixedVariant() { + return DynamicColor.fromPalette( + (s) -> s.primaryPalette, + (s) -> { + if (isMonochrome(s)) { + return s.isDark ? 30.0 : 70.0; + } + return 30.0; + }, + (s) -> primaryFixedDim()); + } + + public static DynamicColor secondaryFixed() { + return DynamicColor.fromPalette( + (s) -> s.secondaryPalette, (s) -> isMonochrome(s) ? 80.0 : 90.0, + MaterialDynamicColors::highestSurface); + } + + public static DynamicColor secondaryFixedDim() { + return DynamicColor.fromPalette( + (s) -> s.secondaryPalette, (s) -> isMonochrome(s) ? 70.0 : 80.0, + MaterialDynamicColors::highestSurface); + } + + public static DynamicColor onSecondaryFixed() { + return DynamicColor.fromPalette( + (s) -> s.secondaryPalette, (s) -> 10.0, (s) -> secondaryFixedDim()); + } + + public static DynamicColor onSecondaryFixedVariant() { + return DynamicColor.fromPalette( + (s) -> s.secondaryPalette, + (s) -> isMonochrome(s) ? 25.0 : 30.0, + (s) -> secondaryFixedDim()); + } + + public static DynamicColor tertiaryFixed() { + return DynamicColor.fromPalette( + (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 40.0 : 90.0, + MaterialDynamicColors::highestSurface); + } + + public static DynamicColor tertiaryFixedDim() { + return DynamicColor.fromPalette( + (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 30.0 : 80.0, + MaterialDynamicColors::highestSurface); + } + + public static DynamicColor onTertiaryFixed() { + return DynamicColor.fromPalette( + (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 90.0 : 10.0, + (s) -> tertiaryFixedDim()); + } + + public static DynamicColor onTertiaryFixedVariant() { + return DynamicColor.fromPalette( + (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 70.0 : 30.0, + (s) -> tertiaryFixedDim()); + } + + // colorControlNormal documented as textColorSecondary in M3 & GM3. + // In Material, textColorSecondary points to onSurfaceVariant in the non-disabled state, + // which is Neutral Variant T30/80 in light/dark. + public static DynamicColor controlNormal() { + return DynamicColor.fromPalette((s) -> s.neutralVariantPalette, + (s) -> s.isDark ? 80.0 : 30.0); + } + + // colorControlHighlight documented, in both M3 & GM3: + // Light mode: #1f000000 dark mode: #33ffffff. + // These are black and white with some alpha. + // 1F hex = 31 decimal; 31 / 255 = 12% alpha. + // 33 hex = 51 decimal; 51 / 255 = 20% alpha. + // DynamicColors do not support alpha currently, and _may_ not need it for this use case, + // depending on how MDC resolved alpha for the other cases. + // Returning black in dark mode, white in light mode. + public static DynamicColor controlHighlight() { + return new DynamicColor( + s -> 0.0, + s -> 0.0, + s -> s.isDark ? 100.0 : 0.0, + s -> s.isDark ? 0.20 : 0.12, + null, + scheme -> + + DynamicColor.toneMinContrastDefault((s) -> s.isDark ? 100.0 : 0.0, null, + scheme, null), + scheme -> + DynamicColor.toneMaxContrastDefault((s) -> s.isDark ? 100.0 : 0.0, null, + scheme, null), + null); + } + + // textColorPrimaryInverse documented, in both M3 & GM3, documented as N10/N90. + public static DynamicColor textPrimaryInverse() { + return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0); + } + + // textColorSecondaryInverse and textColorTertiaryInverse both documented, in both M3 & GM3, as + // NV30/NV80 + public static DynamicColor textSecondaryAndTertiaryInverse() { + return DynamicColor.fromPalette((s) -> s.neutralVariantPalette, + (s) -> s.isDark ? 30.0 : 80.0); + } + + // textColorPrimaryInverseDisableOnly documented, in both M3 & GM3, as N10/N90 + public static DynamicColor textPrimaryInverseDisableOnly() { + return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0); + } + + // textColorSecondaryInverse and textColorTertiaryInverse in disabled state both documented, + // in both M3 & GM3, as N10/N90 + public static DynamicColor textSecondaryAndTertiaryInverseDisabled() { + return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0); + } + + // textColorHintInverse documented, in both M3 & GM3, as N10/N90 + public static DynamicColor textHintInverse() { + return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0); + } } diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/scheme/SchemeTonalSpot.java b/packages/SystemUI/monet/src/com/android/systemui/monet/scheme/SchemeTonalSpot.java index 8480684043dc..cc6b492dadc9 100644 --- a/packages/SystemUI/monet/src/com/android/systemui/monet/scheme/SchemeTonalSpot.java +++ b/packages/SystemUI/monet/src/com/android/systemui/monet/scheme/SchemeTonalSpot.java @@ -32,7 +32,7 @@ public class SchemeTonalSpot extends DynamicScheme { TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 16.0), TonalPalette.fromHueAndChroma( MathUtils.sanitizeDegreesDouble(sourceColorHct.getHue() + 60.0), 24.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 4.0), + TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 6.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 8.0)); } } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java index 436145ec0692..3244eb43c8c4 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java @@ -131,7 +131,8 @@ public interface QS extends FragmentBase { /** * A rounded corner clipping that makes QS feel as if it were behind everything. */ - void setFancyClipping(int top, int bottom, int cornerRadius, boolean visible); + void setFancyClipping(int leftInset, int top, int rightInset, int bottom, int cornerRadius, + boolean visible, boolean fullWidth); /** * @return if quick settings is fully collapsed currently diff --git a/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml b/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml index 29832a081612..934fa6f54286 100644 --- a/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml +++ b/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml @@ -30,7 +30,7 @@ <FrameLayout android:id="@+id/inout_container" - android:layout_height="17dp" + android:layout_height="@*android:dimen/status_bar_system_icon_intrinsic_size" android:layout_width="wrap_content" android:layout_gravity="center_vertical"> <ImageView @@ -39,24 +39,25 @@ android:layout_width="wrap_content" android:src="@drawable/ic_activity_down" android:visibility="gone" - android:paddingEnd="2dp" + android:paddingEnd="2sp" /> <ImageView android:id="@+id/mobile_out" android:layout_height="wrap_content" android:layout_width="wrap_content" android:src="@drawable/ic_activity_up" - android:paddingEnd="2dp" + android:paddingEnd="2sp" android:visibility="gone" /> </FrameLayout> <ImageView android:id="@+id/mobile_type" - android:layout_height="wrap_content" + android:layout_height="@dimen/status_bar_mobile_signal_size" android:layout_width="wrap_content" android:layout_gravity="center_vertical" - android:paddingStart="2.5dp" - android:paddingEnd="1dp" + android:adjustViewBounds="true" + android:paddingStart="2.5sp" + android:paddingEnd="1sp" android:visibility="gone" /> <Space android:id="@+id/mobile_roaming_space" @@ -70,14 +71,14 @@ android:layout_gravity="center_vertical"> <com.android.systemui.statusbar.AnimatedImageView android:id="@+id/mobile_signal" - android:layout_height="wrap_content" - android:layout_width="wrap_content" + android:layout_height="@dimen/status_bar_mobile_signal_size" + android:layout_width="@dimen/status_bar_mobile_signal_size" systemui:hasOverlappingRendering="false" /> <ImageView android:id="@+id/mobile_roaming" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_width="@dimen/status_bar_mobile_signal_size" + android:layout_height="@dimen/status_bar_mobile_signal_size" android:src="@drawable/stat_sys_roaming" android:contentDescription="@string/data_connection_roaming" android:visibility="gone" /> diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml index 4fc411eaea9d..4d289ebeb30b 100644 --- a/packages/SystemUI/res-keyguard/values/styles.xml +++ b/packages/SystemUI/res-keyguard/values/styles.xml @@ -22,6 +22,7 @@ <!-- Keyguard PIN pad styles --> <style name="Keyguard.TextView" parent="@android:style/Widget.DeviceDefault.TextView"> <item name="android:textSize">@dimen/kg_status_line_font_size</item> + <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> </style> <style name="Keyguard.TextView.EmergencyButton" parent="Theme.SystemUI"> <item name="android:textColor">?androidprv:attr/materialColorOnTertiaryFixed</item> diff --git a/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml index ee8d488343c4..a35504f9c202 100644 --- a/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml +++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml @@ -16,16 +16,12 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" - android:viewportHeight="24" - android:viewportWidth="24"> + android:viewportWidth="24" + android:viewportHeight="24"> <path - android:fillAlpha="1" - android:fillColor="@android:color/white" - android:fillType="nonZero" - android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z" /> + android:pathData="M23.41,6L22,4.59C21.63,4.21 21.12,4 20.59,4C20.06,4 19.55,4.21 19.18,4.59L11.39,12.38L9.09,14.68L8.04,18.9C8.01,18.96 8,19.04 8,19.11C8,19.6 8.4,20 8.89,20C8.96,20 9.04,19.99 9.11,19.97L13.33,18.92L15.63,16.62L23.42,8.83C23.79,8.45 24,7.94 24,7.41C24,6.88 23.79,6.37 23.41,6ZM14.21,15.21L13.21,16.21L11.8,14.8L12.8,13.8L20.59,6L22,7.41L14.21,15.21Z" + android:fillColor="@android:color/white"/> <path - android:fillAlpha="1" - android:fillColor="@android:color/white" - android:fillType="nonZero" - android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z" /> + android:pathData="M6.688,20C2.047,20 0.333,18.65 0.333,16C0.333,13.61 2.439,12.474 5.713,12C6.792,11.844 7.344,11.397 7.344,10.927C7.344,9.625 4.679,9.705 3.833,9.667V7.667C3.833,7.667 6.792,7.667 8.208,8.729C8.932,9.272 9.333,9.979 9.333,11.05C9.333,12.52 8.281,13.677 5.713,13.885C4.017,14.023 2.333,14.52 2.333,16C2.333,17.33 4.013,18 7.333,18L6.688,20Z" + android:fillColor="@android:color/white"/> </vector> diff --git a/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml index 759018289562..860fc7d6b316 100644 --- a/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml +++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml @@ -13,19 +13,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportHeight="24" - android:viewportWidth="24"> - <path - android:fillAlpha="1" - android:fillColor="#636C6F" - android:fillType="nonZero" - android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z" /> - <path - android:fillAlpha="1" - android:fillColor="#636C6F" - android:fillType="nonZero" - android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z" /> -</vector> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/ic_note_task_shortcut_widget_background" /> + <foreground android:drawable="@drawable/ic_note_task_shortcut_widget_foreground" /> +</adaptive-icon> diff --git a/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_background.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_background.xml new file mode 100644 index 000000000000..9f98f07c8a58 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_background.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <path + android:pathData="M0,0h108v108h-108z" + android:fillColor="#0B57D0"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_foreground.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_foreground.xml new file mode 100644 index 000000000000..fcb3ef4bfdc5 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_foreground.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <path + android:pathData="M74.92,43L72.33,40.42C71.65,39.72 70.72,39.33 69.75,39.33C68.78,39.33 67.84,39.72 67.16,40.42L52.88,54.7L48.67,58.91L46.74,66.65C46.69,66.76 46.67,66.91 46.67,67.04C46.67,67.93 47.4,68.67 48.3,68.67C48.43,68.67 48.57,68.65 48.7,68.61L56.44,66.69L60.65,62.47L74.94,48.19C75.61,47.49 76,46.56 76,45.58C76,44.61 75.61,43.68 74.92,43ZM58.05,59.88L56.22,61.72L53.63,59.13L55.47,57.3L69.75,43L72.33,45.58L58.05,59.88Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M44.26,68.67C35.75,68.67 32.61,66.19 32.61,61.33C32.61,56.95 36.47,54.87 42.47,54C44.45,53.71 45.46,52.89 45.46,52.03C45.46,49.65 40.58,49.79 39.03,49.72V46.06C39.03,46.06 44.45,46.06 47.05,48C48.37,49 49.11,50.3 49.11,52.26C49.11,54.95 47.18,57.07 42.47,57.46C39.36,57.71 36.28,58.62 36.28,61.33C36.28,63.77 39.36,65 45.44,65L44.26,68.67Z" + android:fillColor="#ffffff"/> +</vector> diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml index 441f963a855a..e989372adde3 100644 --- a/packages/SystemUI/res/layout/combined_qs_header.xml +++ b/packages/SystemUI/res/layout/combined_qs_header.xml @@ -126,8 +126,7 @@ <com.android.systemui.battery.BatteryMeterView android:id="@+id/batteryRemainingIcon" android:layout_width="wrap_content" - android:layout_height="@dimen/large_screen_shade_header_min_height" - app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height" + android:layout_height="0dp" app:layout_constrainedWidth="true" app:textAppearance="@style/TextAppearance.QS.Status" app:layout_constraintStart_toEndOf="@id/statusIcons" diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml index 7dbe0598f3c8..0d86e0a40b6d 100644 --- a/packages/SystemUI/res/layout/screen_share_dialog.xml +++ b/packages/SystemUI/res/layout/screen_share_dialog.xml @@ -76,7 +76,7 @@ android:orientation="horizontal" android:layout_marginTop="@dimen/screenrecord_buttons_margin_top"> <TextView - android:id="@+id/button_cancel" + android:id="@android:id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="0" @@ -87,7 +87,7 @@ android:layout_height="match_parent" android:layout_weight="1"/> <TextView - android:id="@+id/button_start" + android:id="@android:id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="0" diff --git a/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml b/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml index 0ea0653ab89f..473ab08a1935 100644 --- a/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml +++ b/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml @@ -24,11 +24,11 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical" - android:layout_marginStart="2.5dp" + android:layout_marginStart="2.5sp" > <FrameLayout android:id="@+id/inout_container" - android:layout_height="17dp" + android:layout_height="@*android:dimen/status_bar_system_icon_intrinsic_size" android:layout_width="wrap_content" android:gravity="center_vertical" > <ImageView @@ -37,14 +37,14 @@ android:layout_width="wrap_content" android:src="@drawable/ic_activity_down" android:visibility="gone" - android:paddingEnd="2dp" + android:paddingEnd="2sp" /> <ImageView android:id="@+id/wifi_out" android:layout_height="wrap_content" android:layout_width="wrap_content" android:src="@drawable/ic_activity_up" - android:paddingEnd="2dp" + android:paddingEnd="2sp" android:visibility="gone" /> </FrameLayout> @@ -62,7 +62,7 @@ <View android:id="@+id/wifi_signal_spacer" android:layout_width="@dimen/status_bar_wifi_signal_spacer_width" - android:layout_height="4dp" + android:layout_height="4sp" android:visibility="gone" /> <!-- Looks like CarStatusBar uses this... --> @@ -75,7 +75,7 @@ <View android:id="@+id/wifi_airplane_spacer" android:layout_width="@dimen/status_bar_airplane_spacer_width" - android:layout_height="4dp" + android:layout_height="4sp" android:visibility="gone" /> </com.android.keyguard.AlphaOptimizedLinearLayout> diff --git a/packages/SystemUI/res/raw/biometricprompt_rear_landscape_base.json b/packages/SystemUI/res/raw/biometricprompt_rear_landscape_base.json new file mode 100644 index 000000000000..49c1c40f7f4f --- /dev/null +++ b/packages/SystemUI/res/raw/biometricprompt_rear_landscape_base.json @@ -0,0 +1 @@ +{"v":"5.8.1","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Rear_Landscape_Base_Foldable","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 18","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[169.478,169.749,0],"ix":2,"l":2},"a":{"a":0,"k":[-48.123,-30.19,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".grey400","cl":"grey400","parent":13,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.741176486015,0.75686275959,0.776470601559,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"black circle matte","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".grey904","cl":"grey904","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,35.536,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.552,0.087],[0,0]],"o":[[0,0],[0,-3.287],[0,0],[0,0]],"v":[[-2.301,8.869],[-2.301,-3.772],[2.301,-9.806],[2.301,9.806]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"black circle matte 2","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue401","cl":"blue401","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,-27.655,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,3.286],[0,0],[-2.552,0.086],[0,0]],"o":[[0,0],[0,-3.286],[0,0],[-2.552,-0.086]],"v":[[-2.301,16.282],[-2.301,-16.281],[2.301,-22.313],[2.301,22.313]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"black circle matte 3","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Finger 2","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-129,"s":[-67]},{"t":-29,"s":[0]}],"ix":10},"p":{"a":0,"k":[-75.352,41.307,0],"ix":2,"l":2},"a":{"a":0,"k":[94.648,211.307,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[6.72,-5.642],[0,0],[-9.394,-0.562],[-0.298,-0.038]],"o":[[-5.153,4.329],[3.882,-16.05],[0.31,0.019],[-0.044,0.75]],"v":[[0.863,12.222],[-8.931,14.755],[8.005,-15.108],[8.931,-15.021]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.792156875134,0.454901963472,0.376470595598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[81.486,130.081],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 9","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.459,6.045],[-5.153,4.329],[-0.044,0.75],[3.116,-24.664],[5.23,-22.052],[8.666,11.92],[-2.9,9.135]],"o":[[0,0],[6.72,-5.642],[12.723,1.335],[-2.369,18.762],[-13.993,-5.333],[2.255,-5.502],[1.843,-5.815]],"v":[[-9.99,-18.348],[-0.196,-20.881],[7.872,-48.124],[21.578,-9.331],[12.104,48.124],[-22.574,21.555],[-14.791,-0.206]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.713725507259,0.384313732386,0.282352954149,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82.545,163.184],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 8","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"black circle matte 4","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".grey903","cl":"grey903","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-18.345,-92.442,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[24.07,0],[0,0],[-8.27,0],[0,0]],"o":[[0,0],[0,8.269],[0,0],[-14.024,-17.379]],"v":[[-29.778,-14.252],[-29.778,-0.721],[-14.805,14.252],[29.778,14.252]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"black circle matte 5","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".grey902","cl":"grey902","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-15.947,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[154.053,139.81,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.3,0.367],[0,0],[-2.364,0.157],[0,0]],"o":[[0,0],[2.3,-0.367],[0,0],[-2.364,-0.157]],"v":[[-3.5,75.533],[-3.5,-75.533],[3.5,-76.312],[3.5,76.312]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[113.225,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 7","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,8.269],[0,0],[2.181,-0.187],[0,0],[-2.23,0],[0,42.252],[10.593,13.127],[0,0]],"o":[[0,0],[-2.23,0],[0,0],[2.181,0.187],[42.252,0],[0,-18.182],[0,0],[-8.27,0]],"v":[[-34.946,-62.973],[-34.946,-76.504],[-41.558,-76.201],[-41.558,76.201],[-34.946,76.504],[41.558,0],[24.61,-48],[-19.973,-48]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[156.824,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 5","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".black 2","cl":"black","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-48.123,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-129,"s":[0,0,100]},{"t":-79,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".grey700","cl":"grey700","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-56.481,-59.936,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[3.767,0],[0,0],[0,-3.767],[0,0],[-3.767,0],[0,0],[0,3.767],[0,0]],"o":[[0,0],[-3.767,0],[0,0],[0,3.767],[0,0],[3.767,0],[0,0],[0,-3.767]],"v":[[46.055,-14.479],[-46.056,-14.479],[-52.876,-7.659],[-52.876,7.658],[-46.056,14.479],[46.055,14.479],[52.876,7.658],[52.876,-7.659]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".grey901","cl":"grey901","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[16.485,2.727,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[4.184,0],[0,0],[0,0],[0,0],[0,-4.375]],"o":[[0,4.184],[0,0],[0,0],[0,0],[4.375,0],[0,0]],"v":[[114.116,92.129],[106.54,99.705],[7.788,99.705],[7.788,-99.704],[106.161,-99.704],[114.116,-91.749]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[5.707,0],[0,0],[1.894,-1.05],[0.886,0.346],[0,0],[2.166,0],[0,0],[0,-5.707],[0,0],[0,-1.46],[0,0],[-1.133,-0.038],[0,0],[0,-1.459],[0,0],[-1.133,-0.038],[0,0],[-5.708,0],[0,0],[-1.894,1.05],[-0.846,-0.289],[0,0],[-2.166,0],[0,0],[0,5.706],[0,0]],"o":[[0,0],[-2.166,0],[-0.883,0.354],[0,0],[-1.895,-1.05],[0,0],[-5.708,0],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[0,5.707],[0,0],[2.165,0],[0.833,-0.334],[0,0],[1.894,1.05],[0,0],[5.707,0],[0,0],[0,-5.707]],"v":[[106.16,-102.082],[8.455,-102.082],[2.265,-100.48],[-0.488,-100.468],[-0.519,-100.48],[-6.71,-102.082],[-104.116,-102.082],[-114.45,-91.748],[-114.45,-36.119],[-116.494,-33.44],[-116.494,-18.979],[-114.45,-16.3],[-114.45,-0.877],[-116.494,1.802],[-116.494,28.704],[-114.45,31.383],[-114.45,91.749],[-104.116,102.083],[-6.495,102.083],[-0.305,100.481],[2.294,100.425],[2.395,100.481],[9.872,102.083],[106.161,102.083],[116.494,91.75],[116.494,-91.748]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.529411792755,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file diff --git a/packages/SystemUI/res/raw/biometricprompt_rear_portrait_base.json b/packages/SystemUI/res/raw/biometricprompt_rear_portrait_base.json new file mode 100644 index 000000000000..9ea0d35e1de2 --- /dev/null +++ b/packages/SystemUI/res/raw/biometricprompt_rear_portrait_base.json @@ -0,0 +1 @@ +{"v":"5.8.1","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Rear_Portrait_Base_Foldable","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 18","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[169.478,169.749,0],"ix":2,"l":2},"a":{"a":0,"k":[-48.123,-30.19,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".grey400","cl":"grey400","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.741176486015,0.75686275959,0.776470601559,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"black circle matte","parent":14,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".grey904","cl":"grey904","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,35.536,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.552,0.087],[0,0]],"o":[[0,0],[0,-3.287],[0,0],[0,0]],"v":[[-2.301,8.869],[-2.301,-3.772],[2.301,-9.806],[2.301,9.806]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"black circle matte 2","parent":14,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue401","cl":"blue401","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,-27.655,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,3.286],[0,0],[-2.552,0.086],[0,0]],"o":[[0,0],[0,-3.286],[0,0],[-2.552,-0.086]],"v":[[-2.301,16.282],[-2.301,-16.281],[2.301,-22.313],[2.301,22.313]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"black circle matte 3","parent":14,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Finger 3","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-2,"ix":10},"p":{"a":0,"k":[260.134,83.782,0],"ix":2,"l":2},"a":{"a":0,"k":[302.634,38.782,0],"ix":1,"l":2},"s":{"a":0,"k":[178,178,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.262,5.076],[0,0],[-0.424,-7.095],[-0.028,-0.225]],"o":[[3.269,-3.892],[-12.123,2.932],[0.015,0.234],[0.567,-0.034]],"v":[[9.232,0.652],[11.145,-6.746],[-11.412,6.046],[-11.346,6.746]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.792156875134,0.454901963472,0.376470595598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[241.281,55.033],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.565,-1.102],[3.269,-3.892],[0.566,-0.033],[-18.63,2.353],[-16.656,3.951],[9.004,6.546],[6.9,-2.19]],"o":[[0,0],[-4.262,5.076],[1.008,9.61],[14.171,-1.79],[-4.028,-10.569],[-4.156,1.703],[-4.392,1.392]],"v":[[-13.858,-7.546],[-15.771,-0.148],[-36.349,5.946],[-7.047,16.299],[36.349,9.142],[16.281,-17.051],[-0.156,-11.172]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.713725507259,0.384313732386,0.282352954149,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[266.285,55.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 4","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"black circle matte 4","parent":14,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".grey903","cl":"grey903","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-18.345,-92.442,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[24.07,0],[0,0],[-8.27,0],[0,0]],"o":[[0,0],[0,8.269],[0,0],[-14.024,-17.379]],"v":[[-29.778,-14.252],[-29.778,-0.721],[-14.805,14.252],[29.778,14.252]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"black circle matte 5","parent":14,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".grey902","cl":"grey902","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-15.947,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[154.053,139.81,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.3,0.367],[0,0],[-2.364,0.157],[0,0]],"o":[[0,0],[2.3,-0.367],[0,0],[-2.364,-0.157]],"v":[[-3.5,75.533],[-3.5,-75.533],[3.5,-76.312],[3.5,76.312]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[113.225,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 7","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,8.269],[0,0],[2.181,-0.187],[0,0],[-2.23,0],[0,42.252],[10.593,13.127],[0,0]],"o":[[0,0],[-2.23,0],[0,0],[2.181,0.187],[42.252,0],[0,-18.182],[0,0],[-8.27,0]],"v":[[-34.946,-62.973],[-34.946,-76.504],[-41.558,-76.201],[-41.558,76.201],[-34.946,76.504],[41.558,0],[24.61,-48],[-19.973,-48]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[156.824,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 5","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".black 2","cl":"black","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-48.123,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-129,"s":[0,0,100]},{"t":-79,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".grey700","cl":"grey700","parent":16,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-56.481,-59.936,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[3.767,0],[0,0],[0,-3.767],[0,0],[-3.767,0],[0,0],[0,3.767],[0,0]],"o":[[0,0],[-3.767,0],[0,0],[0,3.767],[0,0],[3.767,0],[0,0],[0,-3.767]],"v":[[46.055,-14.479],[-46.056,-14.479],[-52.876,-7.659],[-52.876,7.658],[-46.056,14.479],[46.055,14.479],[52.876,7.658],[52.876,-7.659]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey901","cl":"grey901","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[16.485,2.727,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[4.184,0],[0,0],[0,0],[0,0],[0,-4.375]],"o":[[0,4.184],[0,0],[0,0],[0,0],[4.375,0],[0,0]],"v":[[114.116,92.129],[106.54,99.705],[7.788,99.705],[7.788,-99.704],[106.161,-99.704],[114.116,-91.749]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[5.707,0],[0,0],[1.894,-1.05],[0.886,0.346],[0,0],[2.166,0],[0,0],[0,-5.707],[0,0],[0,-1.46],[0,0],[-1.133,-0.038],[0,0],[0,-1.459],[0,0],[-1.133,-0.038],[0,0],[-5.708,0],[0,0],[-1.894,1.05],[-0.846,-0.289],[0,0],[-2.166,0],[0,0],[0,5.706],[0,0]],"o":[[0,0],[-2.166,0],[-0.883,0.354],[0,0],[-1.895,-1.05],[0,0],[-5.708,0],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[0,5.707],[0,0],[2.165,0],[0.833,-0.334],[0,0],[1.894,1.05],[0,0],[5.707,0],[0,0],[0,-5.707]],"v":[[106.16,-102.082],[8.455,-102.082],[2.265,-100.48],[-0.488,-100.468],[-0.519,-100.48],[-6.71,-102.082],[-104.116,-102.082],[-114.45,-91.748],[-114.45,-36.119],[-116.494,-33.44],[-116.494,-18.979],[-114.45,-16.3],[-114.45,-0.877],[-116.494,1.802],[-116.494,28.704],[-114.45,31.383],[-114.45,91.749],[-104.116,102.083],[-6.495,102.083],[-0.305,100.481],[2.294,100.425],[2.395,100.481],[9.872,102.083],[106.161,102.083],[116.494,91.75],[116.494,-91.748]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.529411792755,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file diff --git a/packages/SystemUI/res/raw/biometricprompt_rear_portrait_reverse_base.json b/packages/SystemUI/res/raw/biometricprompt_rear_portrait_reverse_base.json new file mode 100644 index 000000000000..f2b259335034 --- /dev/null +++ b/packages/SystemUI/res/raw/biometricprompt_rear_portrait_reverse_base.json @@ -0,0 +1 @@ +{"v":"5.8.1","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Rear_Portrait_Reverse_Base_Foldable","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 18","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":270,"ix":10},"p":{"a":0,"k":[169.478,169.749,0],"ix":2,"l":2},"a":{"a":0,"k":[-48.123,-30.19,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".grey400","cl":"grey400","parent":13,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.741176486015,0.75686275959,0.776470601559,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"black circle matte","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".grey904","cl":"grey904","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,35.536,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.552,0.087],[0,0]],"o":[[0,0],[0,-3.287],[0,0],[0,0]],"v":[[-2.301,8.869],[-2.301,-3.772],[2.301,-9.806],[2.301,9.806]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"black circle matte 2","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue401","cl":"blue401","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,-27.655,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,3.286],[0,0],[-2.552,0.086],[0,0]],"o":[[0,0],[0,-3.286],[0,0],[-2.552,-0.086]],"v":[[-2.301,16.282],[-2.301,-16.281],[2.301,-22.313],[2.301,22.313]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"black circle matte 3","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Finger 2","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-75.352,41.307,0],"ix":2,"l":2},"a":{"a":0,"k":[94.648,211.307,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[6.72,-5.642],[0,0],[-9.394,-0.562],[-0.298,-0.038]],"o":[[-5.153,4.329],[3.882,-16.05],[0.31,0.019],[-0.044,0.75]],"v":[[0.863,12.222],[-8.931,14.755],[8.005,-15.108],[8.931,-15.021]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.792156875134,0.454901963472,0.376470595598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[81.486,130.081],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 9","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.459,6.045],[-5.153,4.329],[-0.044,0.75],[3.116,-24.664],[5.23,-22.052],[8.666,11.92],[-2.9,9.135]],"o":[[0,0],[6.72,-5.642],[12.723,1.335],[-2.369,18.762],[-13.993,-5.333],[2.255,-5.502],[1.843,-5.815]],"v":[[-9.99,-18.348],[-0.196,-20.881],[7.872,-48.124],[21.578,-9.331],[12.104,48.124],[-22.574,21.555],[-14.791,-0.206]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.713725507259,0.384313732386,0.282352954149,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82.545,163.184],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 8","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"black circle matte 4","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".grey903","cl":"grey903","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-18.345,-92.442,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[24.07,0],[0,0],[-8.27,0],[0,0]],"o":[[0,0],[0,8.269],[0,0],[-14.024,-17.379]],"v":[[-29.778,-14.252],[-29.778,-0.721],[-14.805,14.252],[29.778,14.252]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"black circle matte 5","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".grey902","cl":"grey902","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-15.947,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[154.053,139.81,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.3,0.367],[0,0],[-2.364,0.157],[0,0]],"o":[[0,0],[2.3,-0.367],[0,0],[-2.364,-0.157]],"v":[[-3.5,75.533],[-3.5,-75.533],[3.5,-76.312],[3.5,76.312]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[113.225,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 7","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,8.269],[0,0],[2.181,-0.187],[0,0],[-2.23,0],[0,42.252],[10.593,13.127],[0,0]],"o":[[0,0],[-2.23,0],[0,0],[2.181,0.187],[42.252,0],[0,-18.182],[0,0],[-8.27,0]],"v":[[-34.946,-62.973],[-34.946,-76.504],[-41.558,-76.201],[-41.558,76.201],[-34.946,76.504],[41.558,0],[24.61,-48],[-19.973,-48]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[156.824,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 5","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".black 2","cl":"black","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-48.123,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-129,"s":[0,0,100]},{"t":-79,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".grey700","cl":"grey700","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-56.481,-59.936,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[3.767,0],[0,0],[0,-3.767],[0,0],[-3.767,0],[0,0],[0,3.767],[0,0]],"o":[[0,0],[-3.767,0],[0,0],[0,3.767],[0,0],[3.767,0],[0,0],[0,-3.767]],"v":[[46.055,-14.479],[-46.056,-14.479],[-52.876,-7.659],[-52.876,7.658],[-46.056,14.479],[46.055,14.479],[52.876,7.658],[52.876,-7.659]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".grey901","cl":"grey901","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[16.485,2.727,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[4.184,0],[0,0],[0,0],[0,0],[0,-4.375]],"o":[[0,4.184],[0,0],[0,0],[0,0],[4.375,0],[0,0]],"v":[[114.116,92.129],[106.54,99.705],[7.788,99.705],[7.788,-99.704],[106.161,-99.704],[114.116,-91.749]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[5.707,0],[0,0],[1.894,-1.05],[0.886,0.346],[0,0],[2.166,0],[0,0],[0,-5.707],[0,0],[0,-1.46],[0,0],[-1.133,-0.038],[0,0],[0,-1.459],[0,0],[-1.133,-0.038],[0,0],[-5.708,0],[0,0],[-1.894,1.05],[-0.846,-0.289],[0,0],[-2.166,0],[0,0],[0,5.706],[0,0]],"o":[[0,0],[-2.166,0],[-0.883,0.354],[0,0],[-1.895,-1.05],[0,0],[-5.708,0],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[0,5.707],[0,0],[2.165,0],[0.833,-0.334],[0,0],[1.894,1.05],[0,0],[5.707,0],[0,0],[0,-5.707]],"v":[[106.16,-102.082],[8.455,-102.082],[2.265,-100.48],[-0.488,-100.468],[-0.519,-100.48],[-6.71,-102.082],[-104.116,-102.082],[-114.45,-91.748],[-114.45,-36.119],[-116.494,-33.44],[-116.494,-18.979],[-114.45,-16.3],[-114.45,-0.877],[-116.494,1.802],[-116.494,28.704],[-114.45,31.383],[-114.45,91.749],[-104.116,102.083],[-6.495,102.083],[-0.305,100.481],[2.294,100.425],[2.395,100.481],[9.872,102.083],[106.161,102.083],[116.494,91.75],[116.494,-91.748]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.529411792755,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml index 20864591ae5a..f40615eb46d0 100644 --- a/packages/SystemUI/res/values-sw720dp/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp/dimens.xml @@ -17,7 +17,7 @@ --> <resources> <!-- gap on either side of status bar notification icons --> - <dimen name="status_bar_icon_padding">1dp</dimen> + <dimen name="status_bar_icon_padding">1sp</dimen> <dimen name="controls_header_horizontal_padding">28dp</dimen> <dimen name="controls_content_margin_horizontal">40dp</dimen> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 0aa880fe6d88..f5c4a4e4bb52 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -122,26 +122,26 @@ <dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen> <!-- Default horizontal drawable padding for status bar icons. --> - <dimen name="status_bar_horizontal_padding">2.5dp</dimen> + <dimen name="status_bar_horizontal_padding">2.5sp</dimen> <!-- Height of the battery icon in the status bar. --> - <dimen name="status_bar_battery_icon_height">13.0dp</dimen> + <dimen name="status_bar_battery_icon_height">13.0sp</dimen> <!-- Width of the battery icon in the status bar. The battery drawable assumes a 12x20 canvas, - so the width of the icon should be 13.0dp * (12.0 / 20.0) --> - <dimen name="status_bar_battery_icon_width">7.8dp</dimen> + so the width of the icon should be 13.0sp * (12.0 / 20.0) --> + <dimen name="status_bar_battery_icon_width">7.8sp</dimen> - <!-- The battery icon is 13dp tall, but the other system icons are 15dp tall (see + <!-- The battery icon is 13sp tall, but the other system icons are 15sp tall (see @*android:dimen/status_bar_system_icon_size) with some top and bottom padding embedded in - the drawables themselves. So, the battery icon may need an extra 1dp of spacing so that its + the drawables themselves. So, the battery icon may need an extra 1sp of spacing so that its bottom still aligns with the bottom of all the other system icons. See b/258672854. --> - <dimen name="status_bar_battery_extra_vertical_spacing">1dp</dimen> + <dimen name="status_bar_battery_extra_vertical_spacing">1sp</dimen> <!-- The font size for the clock in the status bar. --> <dimen name="status_bar_clock_size">14sp</dimen> <!-- The starting padding for the clock in the status bar. --> - <dimen name="status_bar_clock_starting_padding">7dp</dimen> + <dimen name="status_bar_clock_starting_padding">7sp</dimen> <!-- The end padding for the clock in the status bar. --> <dimen name="status_bar_clock_end_padding">0dp</dimen> @@ -153,16 +153,19 @@ <dimen name="status_bar_left_clock_end_padding">2dp</dimen> <!-- Spacing after the wifi signals that is present if there are any icons following it. --> - <dimen name="status_bar_wifi_signal_spacer_width">2.5dp</dimen> + <dimen name="status_bar_wifi_signal_spacer_width">2.5sp</dimen> <!-- Size of the view displaying the wifi signal icon in the status bar. --> - <dimen name="status_bar_wifi_signal_size">@*android:dimen/status_bar_system_icon_size</dimen> + <dimen name="status_bar_wifi_signal_size">13sp</dimen> + + <!-- Size of the view displaying the mobile signal icon in the status bar. --> + <dimen name="status_bar_mobile_signal_size">13sp</dimen> <!-- Spacing before the airplane mode icon if there are any icons preceding it. --> - <dimen name="status_bar_airplane_spacer_width">4dp</dimen> + <dimen name="status_bar_airplane_spacer_width">4sp</dimen> <!-- Spacing between system icons. --> - <dimen name="status_bar_system_icon_spacing">0dp</dimen> + <dimen name="status_bar_system_icon_spacing">2sp</dimen> <!-- The amount to scale each of the status bar icons by. A value of 1 means no scaling. --> <item name="status_bar_icon_scale_factor" format="float" type="dimen">1.0</item> @@ -310,7 +313,7 @@ <dimen name="snooze_snackbar_min_height">56dp</dimen> <!-- size at which Notification icons will be drawn in the status bar --> - <dimen name="status_bar_icon_drawing_size">15dp</dimen> + <dimen name="status_bar_icon_drawing_size">15sp</dimen> <!-- size at which Notification icons will be drawn on Ambient Display --> <dimen name="status_bar_icon_drawing_size_dark"> @@ -321,22 +324,22 @@ <item type="dimen" name="status_bar_icon_drawing_alpha">90%</item> <!-- gap on either side of status bar notification icons --> - <dimen name="status_bar_icon_padding">0dp</dimen> + <dimen name="status_bar_icon_padding">0sp</dimen> <!-- the padding on the start of the statusbar --> - <dimen name="status_bar_padding_start">8dp</dimen> + <dimen name="status_bar_padding_start">8sp</dimen> <!-- the padding on the end of the statusbar --> - <dimen name="status_bar_padding_end">8dp</dimen> + <dimen name="status_bar_padding_end">8sp</dimen> <!-- the padding on the top of the statusbar (usually 0) --> - <dimen name="status_bar_padding_top">0dp</dimen> + <dimen name="status_bar_padding_top">0sp</dimen> <!-- the radius of the overflow dot in the status bar --> - <dimen name="overflow_dot_radius">2dp</dimen> + <dimen name="overflow_dot_radius">2sp</dimen> <!-- the padding between dots in the icon overflow --> - <dimen name="overflow_icon_dot_padding">3dp</dimen> + <dimen name="overflow_icon_dot_padding">3sp</dimen> <!-- Dimensions related to screenshots --> @@ -617,8 +620,8 @@ <dimen name="qs_footer_icon_size">20dp</dimen> <dimen name="qs_header_row_min_height">48dp</dimen> - <dimen name="qs_header_non_clickable_element_height">24dp</dimen> - <dimen name="new_qs_header_non_clickable_element_height">24dp</dimen> + <dimen name="qs_header_non_clickable_element_height">24sp</dimen> + <dimen name="new_qs_header_non_clickable_element_height">24sp</dimen> <dimen name="qs_footer_padding">20dp</dimen> <dimen name="qs_security_footer_height">88dp</dimen> @@ -822,7 +825,7 @@ <!-- Padding between the mobile signal indicator and the start icon when the roaming icon is displayed in the upper left corner. --> - <dimen name="roaming_icon_start_padding">2dp</dimen> + <dimen name="roaming_icon_start_padding">2sp</dimen> <!-- Extra padding between the mobile data type icon and the strength indicator when the data type icon is wide for the tile in quick settings. --> @@ -1042,13 +1045,13 @@ <dimen name="display_cutout_margin_consumption">0px</dimen> <!-- Height of the Ongoing App Ops chip --> - <dimen name="ongoing_appops_chip_height">24dp</dimen> + <dimen name="ongoing_appops_chip_height">24sp</dimen> <!-- Side padding between background of Ongoing App Ops chip and content --> <dimen name="ongoing_appops_chip_side_padding">8dp</dimen> <!-- Margin between icons of Ongoing App Ops chip --> <dimen name="ongoing_appops_chip_icon_margin">4dp</dimen> <!-- Icon size of Ongoing App Ops chip --> - <dimen name="ongoing_appops_chip_icon_size">16dp</dimen> + <dimen name="ongoing_appops_chip_icon_size">16sp</dimen> <!-- Radius of Ongoing App Ops chip corners --> <dimen name="ongoing_appops_chip_bg_corner_radius">28dp</dimen> <!-- One or two privacy items --> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 853930e174a7..c57fef1c52f7 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1257,7 +1257,10 @@ <string name="monitoring_description_managed_profile_network_logging">Your admin has turned on network logging, which monitors traffic in your work profile but not in your personal profile.</string> <!-- Monitoring dialog: Description of an active VPN. [CHAR LIMIT=NONE]--> - <string name="monitoring_description_named_vpn">This device is connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>. Your network activity, including emails and browsing data, is visible to your IT admin.</string> + <string name="monitoring_description_named_vpn">This device is connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>. Your network activity, including emails and browsing data, is visible to the VPN provider.</string> + + <!-- Monitoring dialog: Description of an active VPN on a managed device. [CHAR LIMIT=NONE]--> + <string name="monitoring_description_managed_device_named_vpn">This device is connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>. Your network activity, including emails and browsing data, is visible to your IT admin.</string> <!-- Monitoring dialog: Description of two active VPNs. [CHAR LIMIT=NONE]--> <string name="monitoring_description_two_named_vpns">This device is connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g> and <xliff:g id="vpn_app" example="Bar VPN App">%2$s</xliff:g>. Your network activity, including emails and browsing data, is visible to your IT admin.</string> @@ -3035,6 +3038,19 @@ --> <string name="keyguard_affordance_enablement_dialog_home_instruction_2">• At least one device is available</string> + <!--- + Requirement for the notes app to be available for the user to use. This is shown as part of a + bulleted list of requirements. When all requirements are met, the app can be accessed through a + shortcut button on the lock screen. [CHAR LIMIT=NONE] --> + <string name="keyguard_affordance_enablement_dialog_notes_app_instruction">Select a default notes app to use the notetaking shortcut</string> + + <!--- + The action to make the lock screen shortcut for the notes app to be available for the user to + use. This is shown as the action button in the dialog listing the requirements. When all + requirements are met, the app can be accessed through a shortcut button on the lock screen. + [CHAR LIMIT=NONE] --> + <string name="keyguard_affordance_enablement_dialog_notes_app_action">Select app</string> + <!-- Error message shown when a shortcut must be pressed and held to activate it, usually shown when the user tried to tap the shortcut or held it for too short a time. [CHAR LIMIT=32]. diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java index 016d573930e8..4a665621b3fe 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java @@ -230,7 +230,7 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager private ClassLoader getParentClassLoader(ClassLoader baseClassLoader) { return new PluginManagerImpl.ClassLoaderFilter( - baseClassLoader, "com.android.systemui.plugin"); + baseClassLoader, "com.android.systemui.log", "com.android.systemui.plugin"); } /** Returns class loader specific for the given plugin. */ diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java index 2f9f5b2ac938..1e668b84cdc6 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java @@ -248,19 +248,23 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage // This allows plugins to include any libraries or copied code they want by only including // classes from the plugin library. static class ClassLoaderFilter extends ClassLoader { - private final String mPackage; + private final String[] mPackages; private final ClassLoader mBase; - public ClassLoaderFilter(ClassLoader base, String pkg) { + ClassLoaderFilter(ClassLoader base, String... pkgs) { super(ClassLoader.getSystemClassLoader()); mBase = base; - mPackage = pkg; + mPackages = pkgs; } @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { - if (!name.startsWith(mPackage)) super.loadClass(name, resolve); - return mBase.loadClass(name); + for (String pkg : mPackages) { + if (name.startsWith(pkg)) { + return mBase.loadClass(name); + } + } + return super.loadClass(name, resolve); } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java index 362d7a9390d4..7cf3121957ca 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java @@ -68,7 +68,7 @@ public interface TaskStackChangeListener { onActivityLaunchOnSecondaryDisplayRerouted(); } - default void onTaskProfileLocked(RunningTaskInfo taskInfo) { } + default void onTaskProfileLocked(RunningTaskInfo taskInfo, int userId) { } default void onTaskCreated(int taskId, ComponentName componentName) { } default void onTaskRemoved(int taskId) { } default void onTaskMovedToFront(int taskId) { } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java index dd52cfbdc80f..c613afbda5b8 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java @@ -262,8 +262,8 @@ public class TaskStackChangeListeners { } @Override - public void onTaskProfileLocked(RunningTaskInfo taskInfo) { - mHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, taskInfo).sendToTarget(); + public void onTaskProfileLocked(RunningTaskInfo taskInfo, int userId) { + mHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, userId, 0, taskInfo).sendToTarget(); } @Override @@ -418,8 +418,9 @@ public class TaskStackChangeListeners { } case ON_TASK_PROFILE_LOCKED: { final RunningTaskInfo info = (RunningTaskInfo) msg.obj; + final int userId = msg.arg1; for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onTaskProfileLocked(info); + mTaskStackListeners.get(i).onTaskProfileLocked(info, userId); } break; } diff --git a/packages/SystemUI/src-debug/com/android/systemui/log/DebugLogger.kt b/packages/SystemUI/src-debug/com/android/systemui/log/DebugLogger.kt new file mode 100644 index 000000000000..af29b05a3fb1 --- /dev/null +++ b/packages/SystemUI/src-debug/com/android/systemui/log/DebugLogger.kt @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.log + +import android.os.Build +import android.util.Log +import android.util.Log.LOG_ID_MAIN + +/** + * A simplified debug logger built as a wrapper around Android's [Log]. Internal for development. + * + * The main advantages are: + * - Sensible defaults, automatically retrieving the class name from the call-site (i.e., tag); + * - The messages are purged from source on release builds (keep in mind they are visible on AOSP); + * - Lazily evaluate Strings for zero impact in production builds or when disabled; + * + * Usage example: + * ```kotlin + * // Logging a message: + * debugLog { "message" } + * + * // Logging an error: + * debugLog(error = exception) { "message" } + * + * // Logging the current stack trace, for debugging: + * debugLog(error = Throwable()) { "message" } + * ``` + */ +object DebugLogger { + + /** + * Log a debug message, with sensible defaults. + * + * For example: + * ```kotlin + * val one = 1 + * debugLog { "message#$one" } + * ``` + * + * The output will be: `D/NoteTaskController: message#1` + * + * Beware, the [debugLog] content is **REMOVED FROM SOURCE AND BINARY** in Release builds. + * + * @param enabled: whether or not the message should be logged. By default, it is + * [Build.IS_DEBUGGABLE]. + * @param priority: type of this log. By default, it is [Log.DEBUG]. + * @param tag: identifies the source of a log. By default, it is the receiver's simple name. + * @param error: a [Throwable] to log. + * @param message: a lazily evaluated message you wish to log. + */ + inline fun Any.debugLog( + enabled: Boolean = Build.IS_DEBUGGABLE, + priority: Int = Log.DEBUG, + tag: String = this::class.simpleName.orEmpty(), + error: Throwable? = null, + message: () -> String, + ) { + if (enabled) { + if (error == null) { + Log.println(priority, tag, message()) + } else { + Log.printlns(LOG_ID_MAIN, priority, tag, message(), error) + } + } + } +} diff --git a/packages/SystemUI/src-release/com/android/systemui/log/DebugLogger.kt b/packages/SystemUI/src-release/com/android/systemui/log/DebugLogger.kt new file mode 100644 index 000000000000..2764a1fdfe3d --- /dev/null +++ b/packages/SystemUI/src-release/com/android/systemui/log/DebugLogger.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.log + +import android.os.Build +import android.util.Log + +/** An empty logger for release builds. */ +object DebugLogger { + + @JvmName("logcatMessage") + inline fun Any.debugLog( + enabled: Boolean = Build.IS_DEBUGGABLE, + priority: Int = Log.DEBUG, + tag: String = this::class.simpleName.orEmpty(), + error: Throwable? = null, + message: () -> String, + ) { + // no-op. + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 9573913e5e2f..1f1d3b618157 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -522,6 +522,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab FACE_AUTH_TRIGGERED_TRUST_DISABLED); } + mLogger.logTrustChanged(wasTrusted, enabled, userId); + for (int i = 0; i < mCallbacks.size(); i++) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); + if (cb != null) { + cb.onTrustChanged(userId); + } + } + if (enabled) { String message = null; if (KeyguardUpdateMonitor.getCurrentUser() == userId @@ -560,14 +568,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } } - - mLogger.logTrustChanged(wasTrusted, enabled, userId); - for (int i = 0; i < mCallbacks.size(); i++) { - KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); - if (cb != null) { - cb.onTrustChanged(userId); - } - } } /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt index 4b5c50ff7fa5..5499d2c07181 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt @@ -19,7 +19,6 @@ package com.android.systemui.biometrics import android.annotation.RawRes import android.content.Context import android.content.Context.FINGERPRINT_SERVICE -import android.content.res.Configuration import android.hardware.fingerprint.FingerprintManager import android.view.DisplayInfo import android.view.Surface @@ -35,21 +34,18 @@ import com.android.systemui.biometrics.AuthBiometricView.STATE_ERROR import com.android.systemui.biometrics.AuthBiometricView.STATE_HELP import com.android.systemui.biometrics.AuthBiometricView.STATE_IDLE import com.android.systemui.biometrics.AuthBiometricView.STATE_PENDING_CONFIRMATION -import com.android.systemui.unfold.compat.ScreenSizeFoldProvider -import com.android.systemui.unfold.updates.FoldProvider /** Fingerprint only icon animator for BiometricPrompt. */ open class AuthBiometricFingerprintIconController( context: Context, iconView: LottieAnimationView, protected val iconViewOverlay: LottieAnimationView -) : AuthIconController(context, iconView), FoldProvider.FoldCallback { +) : AuthIconController(context, iconView) { - private var isDeviceFolded: Boolean = false private val isSideFps: Boolean private val isReverseDefaultRotation = context.resources.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation) - private val screenSizeFoldProvider: ScreenSizeFoldProvider = ScreenSizeFoldProvider(context) + var iconLayoutParamSize: Pair<Int, Int> = Pair(1, 1) set(value) { if (field == value) { @@ -77,20 +73,16 @@ open class AuthBiometricFingerprintIconController( if (isSideFps && getRotationFromDefault(displayInfo.rotation) == Surface.ROTATION_180) { iconView.rotation = 180f } - screenSizeFoldProvider.registerCallback(this, context.mainExecutor) - screenSizeFoldProvider.onConfigurationChange(context.resources.configuration) } private fun updateIconSideFps(@BiometricState lastState: Int, @BiometricState newState: Int) { val displayInfo = DisplayInfo() context.display?.getDisplayInfo(displayInfo) val rotation = getRotationFromDefault(displayInfo.rotation) - val iconAnimation = getSideFpsAnimationForTransition(rotation) val iconViewOverlayAnimation = getSideFpsOverlayAnimationForTransition(lastState, newState, rotation) ?: return if (!(lastState == STATE_AUTHENTICATING_ANIMATING_IN && newState == STATE_AUTHENTICATING)) { - iconView.setAnimation(iconAnimation) iconViewOverlay.setAnimation(iconViewOverlayAnimation) } @@ -132,10 +124,6 @@ open class AuthBiometricFingerprintIconController( LottieColorUtils.applyDynamicColors(context, iconView) } - override fun onConfigurationChanged(newConfig: Configuration) { - screenSizeFoldProvider.onConfigurationChange(newConfig) - } - override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) { if (isSideFps) { updateIconSideFps(lastState, newState) @@ -230,25 +218,6 @@ open class AuthBiometricFingerprintIconController( if (isReverseDefaultRotation) (rotation + 1) % 4 else rotation @RawRes - private fun getSideFpsAnimationForTransition(rotation: Int): Int = when (rotation) { - Surface.ROTATION_90 -> if (isDeviceFolded) { - R.raw.biometricprompt_folded_base_topleft - } else { - R.raw.biometricprompt_portrait_base_topleft - } - Surface.ROTATION_270 -> if (isDeviceFolded) { - R.raw.biometricprompt_folded_base_bottomright - } else { - R.raw.biometricprompt_portrait_base_bottomright - } - else -> if (isDeviceFolded) { - R.raw.biometricprompt_folded_base_default - } else { - R.raw.biometricprompt_landscape_base - } - } - - @RawRes private fun getSideFpsOverlayAnimationForTransition( @BiometricState oldState: Int, @BiometricState newState: Int, @@ -357,8 +326,4 @@ open class AuthBiometricFingerprintIconController( ) } } - - override fun onFoldUpdated(isFolded: Boolean) { - isDeviceFolded = isFolded - } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index e04dd0680060..f330c34bfaa0 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -124,6 +124,7 @@ public abstract class AuthBiometricView extends LinearLayout { protected final int mTextColorHint; private AuthPanelController mPanelController; + private PromptInfo mPromptInfo; private boolean mRequireConfirmation; private int mUserId; @@ -266,11 +267,9 @@ public abstract class AuthBiometricView extends LinearLayout { /** Create the controller for managing the icons transitions during the prompt.*/ @NonNull protected abstract AuthIconController createIconController(); - void setPanelController(AuthPanelController panelController) { mPanelController = panelController; } - void setPromptInfo(PromptInfo promptInfo) { mPromptInfo = promptInfo; } @@ -463,9 +462,16 @@ public abstract class AuthBiometricView extends LinearLayout { return false; } + /** + * Updates mIconView animation on updates to fold state, device rotation, or rear display mode + * @param animation new asset to use for iconw + */ + public void updateIconViewAnimation(int animation) { + mIconView.setAnimation(animation); + } + public void updateState(@BiometricState int newState) { Log.v(TAG, "newState: " + newState); - mIconController.updateState(mState, newState); switch (newState) { @@ -625,7 +631,6 @@ public abstract class AuthBiometricView extends LinearLayout { public void restoreState(@Nullable Bundle savedState) { mSavedState = savedState; } - private void setTextOrHide(TextView view, CharSequence charSequence) { if (TextUtils.isEmpty(charSequence)) { view.setVisibility(View.GONE); @@ -658,7 +663,6 @@ public abstract class AuthBiometricView extends LinearLayout { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - mIconController.onConfigurationChanged(newConfig); if (mSavedState != null) { updateState(mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_STATE)); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index aeebb010eb1e..b386bc9d050b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -67,6 +67,8 @@ import com.android.systemui.animation.Interpolators; import com.android.systemui.biometrics.AuthController.ScaleFactorProvider; import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor; import com.android.systemui.biometrics.ui.CredentialView; +import com.android.systemui.biometrics.ui.binder.AuthBiometricFingerprintViewBinder; +import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel; import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.keyguard.WakefulnessLifecycle; @@ -126,6 +128,8 @@ public class AuthContainerView extends LinearLayout // TODO: these should be migrated out once ready private final Provider<BiometricPromptCredentialInteractor> mBiometricPromptInteractor; + private final Provider<AuthBiometricFingerprintViewModel> + mAuthBiometricFingerprintViewModelProvider; private final Provider<CredentialViewModel> mCredentialViewModelProvider; @VisibleForTesting final BiometricCallback mBiometricCallback; @@ -241,12 +245,14 @@ public class AuthContainerView extends LinearLayout @NonNull LockPatternUtils lockPatternUtils, @NonNull InteractionJankMonitor jankMonitor, @NonNull Provider<BiometricPromptCredentialInteractor> biometricPromptInteractor, + @NonNull Provider<AuthBiometricFingerprintViewModel> + authBiometricFingerprintViewModelProvider, @NonNull Provider<CredentialViewModel> credentialViewModelProvider) { mConfig.mSensorIds = sensorIds; return new AuthContainerView(mConfig, fpProps, faceProps, wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils, jankMonitor, - biometricPromptInteractor, credentialViewModelProvider, - new Handler(Looper.getMainLooper()), bgExecutor); + biometricPromptInteractor, authBiometricFingerprintViewModelProvider, + credentialViewModelProvider, new Handler(Looper.getMainLooper()), bgExecutor); } } @@ -339,6 +345,8 @@ public class AuthContainerView extends LinearLayout @NonNull LockPatternUtils lockPatternUtils, @NonNull InteractionJankMonitor jankMonitor, @NonNull Provider<BiometricPromptCredentialInteractor> biometricPromptInteractor, + @NonNull Provider<AuthBiometricFingerprintViewModel> + authBiometricFingerprintViewModelProvider, @NonNull Provider<CredentialViewModel> credentialViewModelProvider, @NonNull Handler mainHandler, @NonNull @Background DelayableExecutor bgExecutor) { @@ -368,6 +376,7 @@ public class AuthContainerView extends LinearLayout mBackgroundExecutor = bgExecutor; mInteractionJankMonitor = jankMonitor; mBiometricPromptInteractor = biometricPromptInteractor; + mAuthBiometricFingerprintViewModelProvider = authBiometricFingerprintViewModelProvider; mCredentialViewModelProvider = credentialViewModelProvider; // Inflate biometric view only if necessary. @@ -386,6 +395,9 @@ public class AuthContainerView extends LinearLayout fingerprintAndFaceView.updateOverrideIconLayoutParamsSize(); fingerprintAndFaceView.setFaceClass3( faceProperties.sensorStrength == STRENGTH_STRONG); + final AuthBiometricFingerprintViewModel fpAndFaceViewModel = + mAuthBiometricFingerprintViewModelProvider.get(); + AuthBiometricFingerprintViewBinder.bind(fingerprintAndFaceView, fpAndFaceViewModel); mBiometricView = fingerprintAndFaceView; } else if (fpProperties != null) { final AuthBiometricFingerprintView fpView = @@ -394,6 +406,9 @@ public class AuthContainerView extends LinearLayout fpView.setSensorProperties(fpProperties); fpView.setScaleFactorProvider(config.mScaleProvider); fpView.updateOverrideIconLayoutParamsSize(); + final AuthBiometricFingerprintViewModel fpViewModel = + mAuthBiometricFingerprintViewModelProvider.get(); + AuthBiometricFingerprintViewBinder.bind(fpView, fpViewModel); mBiometricView = fpView; } else if (faceProperties != null) { mBiometricView = (AuthBiometricFaceView) layoutInflater.inflate( diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 6eb3c708e9b5..3579e8cc0218 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -73,6 +73,7 @@ import com.android.settingslib.udfps.UdfpsUtils; import com.android.systemui.CoreStartable; import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor; import com.android.systemui.biometrics.domain.interactor.LogContextInteractor; +import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel; import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; @@ -127,6 +128,9 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, // TODO: these should be migrated out once ready @NonNull private final Provider<BiometricPromptCredentialInteractor> mBiometricPromptInteractor; + + @NonNull private final Provider<AuthBiometricFingerprintViewModel> + mAuthBiometricFingerprintViewModelProvider; @NonNull private final Provider<CredentialViewModel> mCredentialViewModelProvider; @NonNull private final LogContextInteractor mLogContextInteractor; @@ -722,7 +726,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, } onDialogDismissed(reason); } - @Inject public AuthController(Context context, Execution execution, @@ -741,6 +744,8 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, @NonNull UdfpsLogger udfpsLogger, @NonNull LogContextInteractor logContextInteractor, @NonNull Provider<BiometricPromptCredentialInteractor> biometricPromptInteractor, + @NonNull Provider<AuthBiometricFingerprintViewModel> + authBiometricFingerprintViewModelProvider, @NonNull Provider<CredentialViewModel> credentialViewModelProvider, @NonNull InteractionJankMonitor jankMonitor, @Main Handler handler, @@ -771,6 +776,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, mLogContextInteractor = logContextInteractor; mBiometricPromptInteractor = biometricPromptInteractor; + mAuthBiometricFingerprintViewModelProvider = authBiometricFingerprintViewModelProvider; mCredentialViewModelProvider = credentialViewModelProvider; mOrientationListener = new BiometricDisplayListener( @@ -1299,7 +1305,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, .build(bgExecutor, sensorIds, mFpProps, mFaceProps, wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils, mInteractionJankMonitor, mBiometricPromptInteractor, - mCredentialViewModelProvider); + mAuthBiometricFingerprintViewModelProvider, mCredentialViewModelProvider); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt index d6ad4da04dbe..f5f46405660f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt @@ -18,7 +18,6 @@ package com.android.systemui.biometrics import android.annotation.DrawableRes import android.content.Context -import android.content.res.Configuration import android.graphics.drawable.Animatable2 import android.graphics.drawable.AnimatedVectorDrawable import android.graphics.drawable.Drawable @@ -94,8 +93,6 @@ abstract class AuthIconController( /** Called during [onAnimationEnd] if the controller is not [deactivated]. */ open fun handleAnimationEnd(drawable: Drawable) {} - open fun onConfigurationChanged(newConfig: Configuration) {} - // TODO(b/251476085): Migrate this to an extension at the appropriate level? /** Load the given [rawResources] immediately so they are cached for use in the [context]. */ protected fun cacheLottieAssetsInContext(context: Context, vararg rawResources: Int) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt index 4319f01bed14..962140fa2cff 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt @@ -55,6 +55,7 @@ import com.airbnb.lottie.model.KeyPath import com.android.internal.annotations.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.R +import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main @@ -84,6 +85,7 @@ constructor( private val activityTaskManager: ActivityTaskManager, overviewProxyService: OverviewProxyService, displayManager: DisplayManager, + private val displayStateInteractor: DisplayStateInteractor, @Main private val mainExecutor: DelayableExecutor, @Main private val handler: Handler, private val alternateBouncerInteractor: AlternateBouncerInteractor, @@ -203,14 +205,16 @@ constructor( request: SideFpsUiRequestSource, @BiometricOverlayConstants.ShowReason reason: Int = BiometricOverlayConstants.REASON_UNKNOWN ) { - requests.add(request) - mainExecutor.execute { - if (overlayView == null) { - traceSection("SideFpsController#show(request=${request.name}, reason=$reason") { - createOverlayForDisplay(reason) + if (!displayStateInteractor.isInRearDisplayMode.value) { + requests.add(request) + mainExecutor.execute { + if (overlayView == null) { + traceSection("SideFpsController#show(request=${request.name}, reason=$reason") { + createOverlayForDisplay(reason) + } + } else { + Log.v(TAG, "overlay already shown") } - } else { - Log.v(TAG, "overlay already shown") } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt index f0b9f670f1e0..c83166385ac6 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt @@ -21,8 +21,12 @@ import com.android.systemui.biometrics.data.repository.FingerprintPropertyReposi import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepositoryImpl import com.android.systemui.biometrics.data.repository.PromptRepository import com.android.systemui.biometrics.data.repository.PromptRepositoryImpl +import com.android.systemui.biometrics.data.repository.RearDisplayStateRepository +import com.android.systemui.biometrics.data.repository.RearDisplayStateRepositoryImpl import com.android.systemui.biometrics.domain.interactor.CredentialInteractor import com.android.systemui.biometrics.domain.interactor.CredentialInteractorImpl +import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor +import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl import com.android.systemui.biometrics.domain.interactor.LogContextInteractor import com.android.systemui.biometrics.domain.interactor.LogContextInteractorImpl import com.android.systemui.dagger.SysUISingleton @@ -45,6 +49,9 @@ interface BiometricsModule { @SysUISingleton fun fingerprintRepository(impl: FingerprintPropertyRepositoryImpl): FingerprintPropertyRepository + @Binds + @SysUISingleton + fun rearDisplayStateRepository(impl: RearDisplayStateRepositoryImpl): RearDisplayStateRepository @Binds @SysUISingleton @@ -52,6 +59,10 @@ interface BiometricsModule { @Binds @SysUISingleton + fun providesDisplayStateInteractor(impl: DisplayStateInteractorImpl): DisplayStateInteractor + + @Binds + @SysUISingleton fun bindsLogContextInteractor(impl: LogContextInteractorImpl): LogContextInteractor companion object { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepository.kt new file mode 100644 index 000000000000..d17d961b5279 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepository.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics.data.repository + +import android.content.Context +import android.hardware.devicestate.DeviceStateManager +import com.android.internal.util.ArrayUtils +import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Main +import java.util.concurrent.Executor +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.stateIn + +/** Provide current rear display state. */ +interface RearDisplayStateRepository { + /** Provides the current rear display state. */ + val isInRearDisplayMode: StateFlow<Boolean> +} + +@SysUISingleton +class RearDisplayStateRepositoryImpl +@Inject +constructor( + @Application applicationScope: CoroutineScope, + @Application context: Context, + deviceStateManager: DeviceStateManager, + @Main mainExecutor: Executor +) : RearDisplayStateRepository { + override val isInRearDisplayMode: StateFlow<Boolean> = + conflatedCallbackFlow { + val sendRearDisplayStateUpdate = { state: Boolean -> + trySendWithFailureLogging( + state, + TAG, + "Error sending rear display state update to $state" + ) + } + + val callback = + DeviceStateManager.DeviceStateCallback { state -> + val isInRearDisplayMode = + ArrayUtils.contains( + context.resources.getIntArray( + com.android.internal.R.array.config_rearDisplayDeviceStates + ), + state + ) + sendRearDisplayStateUpdate(isInRearDisplayMode) + } + + sendRearDisplayStateUpdate(false) + deviceStateManager.registerCallback(mainExecutor, callback) + awaitClose { deviceStateManager.unregisterCallback(callback) } + } + .stateIn( + applicationScope, + started = SharingStarted.Eagerly, + initialValue = false, + ) + + companion object { + const val TAG = "RearDisplayStateRepositoryImpl" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt new file mode 100644 index 000000000000..c935aa290e21 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics.domain.interactor + +import android.content.Context +import android.content.res.Configuration +import com.android.systemui.biometrics.data.repository.RearDisplayStateRepository +import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.unfold.compat.ScreenSizeFoldProvider +import com.android.systemui.unfold.updates.FoldProvider +import java.util.concurrent.Executor +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.stateIn + +/** Aggregates display state information. */ +interface DisplayStateInteractor { + + /** Whether the device is currently in rear display mode. */ + val isInRearDisplayMode: StateFlow<Boolean> + + /** Whether the device is currently folded. */ + val isFolded: Flow<Boolean> + + /** Called on configuration changes, used to keep the display state in sync */ + fun onConfigurationChanged(newConfig: Configuration) +} + +/** Encapsulates logic for interacting with the display state. */ +class DisplayStateInteractorImpl +@Inject +constructor( + @Application applicationScope: CoroutineScope, + @Application context: Context, + @Main mainExecutor: Executor, + rearDisplayStateRepository: RearDisplayStateRepository, +) : DisplayStateInteractor { + private var screenSizeFoldProvider: ScreenSizeFoldProvider = ScreenSizeFoldProvider(context) + + fun setScreenSizeFoldProvider(foldProvider: ScreenSizeFoldProvider) { + screenSizeFoldProvider = foldProvider + } + + override val isFolded: Flow<Boolean> = + conflatedCallbackFlow { + val sendFoldStateUpdate = { state: Boolean -> + trySendWithFailureLogging( + state, + TAG, + "Error sending fold state update to $state" + ) + } + + val callback = + object : FoldProvider.FoldCallback { + override fun onFoldUpdated(isFolded: Boolean) { + sendFoldStateUpdate(isFolded) + } + } + sendFoldStateUpdate(false) + screenSizeFoldProvider.registerCallback(callback, mainExecutor) + awaitClose { screenSizeFoldProvider.unregisterCallback(callback) } + } + .stateIn( + applicationScope, + started = SharingStarted.Eagerly, + initialValue = false, + ) + + override val isInRearDisplayMode: StateFlow<Boolean> = + rearDisplayStateRepository.isInRearDisplayMode + + override fun onConfigurationChanged(newConfig: Configuration) { + screenSizeFoldProvider.onConfigurationChange(newConfig) + } + + companion object { + private const val TAG = "DisplayStateInteractor" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt new file mode 100644 index 000000000000..e776ab44ee42 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.biometrics.ui.binder + +import android.view.Surface +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.biometrics.AuthBiometricFingerprintView +import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel +import com.android.systemui.lifecycle.repeatWhenAttached +import kotlinx.coroutines.launch + +object AuthBiometricFingerprintViewBinder { + + /** Binds a [AuthBiometricFingerprintView] to a [AuthBiometricFingerprintViewModel]. */ + @JvmStatic + fun bind(view: AuthBiometricFingerprintView, viewModel: AuthBiometricFingerprintViewModel) { + view.repeatWhenAttached { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.onConfigurationChanged(view.context.resources.configuration) + viewModel.setRotation(view.context.display?.orientation ?: Surface.ROTATION_0) + launch { + viewModel.iconAsset.collect { iconAsset -> + view.updateIconViewAnimation(iconAsset) + } + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModel.kt new file mode 100644 index 000000000000..617d80cee09d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModel.kt @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.biometrics.ui.viewmodel + +import android.annotation.RawRes +import android.content.res.Configuration +import android.view.Surface +import com.android.systemui.R +import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine + +/** Models UI of AuthBiometricFingerprintView to support rear display state changes. */ +class AuthBiometricFingerprintViewModel +@Inject +constructor(private val interactor: DisplayStateInteractor) { + /** Current device rotation. */ + private var rotation: Int = Surface.ROTATION_0 + + /** Current AuthBiometricFingerprintView asset. */ + val iconAsset: Flow<Int> = + combine(interactor.isFolded, interactor.isInRearDisplayMode) { + isFolded: Boolean, + isInRearDisplayMode: Boolean -> + getSideFpsAnimationAsset(isFolded, isInRearDisplayMode) + } + + @RawRes + private fun getSideFpsAnimationAsset( + isDeviceFolded: Boolean, + isInRearDisplayMode: Boolean, + ): Int = + when (rotation) { + Surface.ROTATION_90 -> + if (isInRearDisplayMode) { + R.raw.biometricprompt_rear_portrait_reverse_base + } else if (isDeviceFolded) { + R.raw.biometricprompt_folded_base_topleft + } else { + R.raw.biometricprompt_portrait_base_topleft + } + Surface.ROTATION_270 -> + if (isInRearDisplayMode) { + R.raw.biometricprompt_rear_portrait_base + } else if (isDeviceFolded) { + R.raw.biometricprompt_folded_base_bottomright + } else { + R.raw.biometricprompt_portrait_base_bottomright + } + else -> + if (isInRearDisplayMode) { + R.raw.biometricprompt_rear_landscape_base + } else if (isDeviceFolded) { + R.raw.biometricprompt_folded_base_default + } else { + R.raw.biometricprompt_landscape_base + } + } + + /** Called on configuration changes */ + fun onConfigurationChanged(newConfig: Configuration) { + interactor.onConfigurationChanged(newConfig) + } + + fun setRotation(newRotation: Int) { + rotation = newRotation + } +} diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt new file mode 100644 index 000000000000..0542e13ba6da --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt @@ -0,0 +1,60 @@ +/* + * 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.clipboardoverlay + +import android.content.Context +import android.graphics.Bitmap +import android.net.Uri +import android.util.Log +import android.util.Size +import com.android.systemui.R +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import java.io.IOException +import java.util.function.Consumer +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import kotlinx.coroutines.withTimeoutOrNull + +class ClipboardImageLoader +@Inject +constructor( + private val context: Context, + @Background private val bgDispatcher: CoroutineDispatcher, + @Application private val mainScope: CoroutineScope +) { + private val TAG: String = "ClipboardImageLoader" + + suspend fun load(uri: Uri, timeoutMs: Long = 300) = + withTimeoutOrNull(timeoutMs) { + withContext(bgDispatcher) { + try { + val size = context.resources.getDimensionPixelSize(R.dimen.overlay_x_scale) + context.contentResolver.loadThumbnail(uri, Size(size, size * 4), null) + } catch (e: IOException) { + Log.e(TAG, "Thumbnail loading failed!", e) + null + } + } + } + + fun loadAsync(uri: Uri, callback: Consumer<Bitmap?>) { + mainScope.launch { callback.accept(load(uri)) } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java index 0aeab10101f6..757ebf45e9ad 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java @@ -32,6 +32,7 @@ import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBO import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TAP_OUTSIDE; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TIMED_OUT; +import static com.android.systemui.flags.Flags.CLIPBOARD_IMAGE_TIMEOUT; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -90,6 +91,7 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv private final ClipboardOverlayUtils mClipboardUtils; private final FeatureFlags mFeatureFlags; private final Executor mBgExecutor; + private final ClipboardImageLoader mClipboardImageLoader; private final ClipboardOverlayView mView; @@ -109,6 +111,7 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv private Runnable mOnUiUpdate; + private boolean mShowingUi; private boolean mIsMinimized; private ClipboardModel mClipboardModel; @@ -175,9 +178,11 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv FeatureFlags featureFlags, ClipboardOverlayUtils clipboardUtils, @Background Executor bgExecutor, + ClipboardImageLoader clipboardImageLoader, UiEventLogger uiEventLogger) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; + mClipboardImageLoader = clipboardImageLoader; mClipboardLogger = new ClipboardLogger(uiEventLogger); @@ -260,21 +265,42 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv boolean shouldAnimate = !model.dataMatches(mClipboardModel) || wasExiting; mClipboardModel = model; mClipboardLogger.setClipSource(mClipboardModel.getSource()); - if (shouldAnimate) { - reset(); - mClipboardLogger.setClipSource(mClipboardModel.getSource()); - if (shouldShowMinimized(mWindow.getWindowInsets())) { - mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_SHOWN_MINIMIZED); - mIsMinimized = true; - mView.setMinimized(true); - } else { - mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_SHOWN_EXPANDED); + if (mFeatureFlags.isEnabled(CLIPBOARD_IMAGE_TIMEOUT)) { + if (shouldAnimate) { + reset(); + mClipboardLogger.setClipSource(mClipboardModel.getSource()); + if (shouldShowMinimized(mWindow.getWindowInsets())) { + mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_SHOWN_MINIMIZED); + mIsMinimized = true; + mView.setMinimized(true); + } else { + mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_SHOWN_EXPANDED); + setExpandedView(this::animateIn); + } + mView.announceForAccessibility( + getAccessibilityAnnouncement(mClipboardModel.getType())); + } else if (!mIsMinimized) { + setExpandedView(() -> { + }); + } + } else { + if (shouldAnimate) { + reset(); + mClipboardLogger.setClipSource(mClipboardModel.getSource()); + if (shouldShowMinimized(mWindow.getWindowInsets())) { + mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_SHOWN_MINIMIZED); + mIsMinimized = true; + mView.setMinimized(true); + } else { + mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_SHOWN_EXPANDED); + setExpandedView(); + animateIn(); + } + mView.announceForAccessibility( + getAccessibilityAnnouncement(mClipboardModel.getType())); + } else if (!mIsMinimized) { setExpandedView(); } - animateIn(); - mView.announceForAccessibility(getAccessibilityAnnouncement(mClipboardModel.getType())); - } else if (!mIsMinimized) { - setExpandedView(); } if (mClipboardModel.isRemote()) { mTimeoutHandler.cancelTimeout(); @@ -285,6 +311,58 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv } } + private void setExpandedView(Runnable onViewReady) { + final ClipboardModel model = mClipboardModel; + mView.setMinimized(false); + switch (model.getType()) { + case TEXT: + if (model.isRemote() || DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_SHOW_ACTIONS, false)) { + if (model.getTextLinks() != null) { + classifyText(model); + } + } + if (model.isSensitive()) { + mView.showTextPreview(mContext.getString(R.string.clipboard_asterisks), true); + } else { + mView.showTextPreview(model.getText().toString(), false); + } + mView.setEditAccessibilityAction(true); + mOnPreviewTapped = this::editText; + onViewReady.run(); + break; + case IMAGE: + mView.setEditAccessibilityAction(true); + mOnPreviewTapped = () -> editImage(model.getUri()); + if (model.isSensitive()) { + mView.showImagePreview(null); + onViewReady.run(); + } else { + mClipboardImageLoader.loadAsync(model.getUri(), (bitmap) -> mView.post(() -> { + if (bitmap == null) { + mView.showDefaultTextPreview(); + } else { + mView.showImagePreview(bitmap); + } + onViewReady.run(); + })); + } + break; + case URI: + case OTHER: + mView.showDefaultTextPreview(); + onViewReady.run(); + break; + } + if (!model.isRemote()) { + maybeShowRemoteCopy(model.getClipData()); + } + if (model.getType() != ClipboardModel.Type.OTHER) { + mOnShareTapped = () -> shareContent(model.getClipData()); + mView.showShareChip(); + } + } + private void setExpandedView() { final ClipboardModel model = mClipboardModel; mView.setMinimized(false); @@ -350,8 +428,12 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_EXPANDED_FROM_MINIMIZED); mIsMinimized = false; } - setExpandedView(); - animateIn(); + if (mFeatureFlags.isEnabled(CLIPBOARD_IMAGE_TIMEOUT)) { + setExpandedView(() -> animateIn()); + } else { + setExpandedView(); + animateIn(); + } } }); mEnterAnimator.start(); @@ -412,7 +494,8 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mInputMonitor.getInputChannel(), Looper.getMainLooper()) { @Override public void onInputEvent(InputEvent event) { - if (event instanceof MotionEvent) { + if ((!mFeatureFlags.isEnabled(CLIPBOARD_IMAGE_TIMEOUT) || mShowingUi) + && event instanceof MotionEvent) { MotionEvent motionEvent = (MotionEvent) event; if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) { if (!mView.isInTouchRegion( @@ -452,6 +535,12 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mEnterAnimator = mView.getEnterAnimation(); mEnterAnimator.addListener(new AnimatorListenerAdapter() { @Override + public void onAnimationStart(Animator animation) { + super.onAnimationStart(animation); + mShowingUi = true; + } + + @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (mOnUiUpdate != null) { @@ -518,6 +607,7 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mOnRemoteCopyTapped = null; mOnShareTapped = null; mOnPreviewTapped = null; + mShowingUi = false; mView.reset(); mTimeoutHandler.cancelTimeout(); mClipboardLogger.reset(); diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt index 5d608c3e3f9e..7cbd1f53612b 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt @@ -16,9 +16,11 @@ package com.android.systemui.controls.ui +import android.app.Activity import android.app.ActivityOptions import android.app.ActivityTaskManager import android.app.ActivityTaskManager.INVALID_TASK_ID +import android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED import android.app.Dialog import android.app.PendingIntent import android.content.ComponentName @@ -96,7 +98,9 @@ class DetailDialog( activityContext, 0 /* enterResId */, 0 /* exitResId */ - ) + ).setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED) + options.isPendingIntentBackgroundActivityLaunchAllowedByPermission = true + taskView.startActivity( pendingIntent, fillInIntent, @@ -214,6 +218,12 @@ class DetailDialog( if (!isShowing()) return taskView.release() + val isActivityFinishing = + (activityContext as? Activity)?.let { it.isFinishing || it.isDestroyed } + if (isActivityFinishing == true) { + // Don't dismiss the dialog if the activity is finishing, it will get removed + return + } super.dismiss() } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 78994c45a194..6ca409f07f6f 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -61,6 +61,9 @@ object Flags { // TODO(b/254512538): Tracking Bug val INSTANT_VOICE_REPLY = unreleasedFlag(111, "instant_voice_reply") + // TODO(b/279735475): Tracking Bug + @JvmField val NEW_LIGHT_BAR_LOGIC = unreleasedFlag(279735475, "new_light_bar_logic") + /** * This flag is server-controlled and should stay as [unreleasedFlag] since we never want to * enable it on release builds. @@ -203,11 +206,6 @@ object Flags { ) /** Whether to inflate the bouncer view on a background thread. */ - // TODO(b/272091103): Tracking Bug - @JvmField - val ASYNC_INFLATE_BOUNCER = releasedFlag(229, "async_inflate_bouncer") - - /** Whether to inflate the bouncer view on a background thread. */ // TODO(b/273341787): Tracking Bug @JvmField val PREVENT_BYPASS_KEYGUARD = releasedFlag(230, "prevent_bypass_keyguard") @@ -222,12 +220,22 @@ object Flags { val LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP = unreleasedFlag(232, "lock_screen_long_press_directly_opens_wallpaper_picker") + /** Whether to run the new udfps keyguard refactor code. */ + // TODO(b/279440316): Tracking bug. + @JvmField + val REFACTOR_UDFPS_KEYGUARD_VIEWS = unreleasedFlag(233, "refactor_udfps_keyguard_views") + /** Provide new auth messages on the bouncer. */ // TODO(b/277961132): Tracking bug. @JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag(234, "revamped_bouncer_messages") + /** Whether to delay showing bouncer UI when face auth or active unlock are enrolled. */ + // TODO(b/279794160): Tracking bug. + @JvmField + val DELAY_BOUNCER = unreleasedFlag(235, "delay_bouncer") + // 300 - power menu // TODO(b/254512600): Tracking Bug @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite") @@ -266,7 +274,7 @@ object Flags { ) @JvmField - val QS_PIPELINE_NEW_HOST = unreleasedFlag(504, "qs_pipeline_new_host", teamfood = false) + val QS_PIPELINE_NEW_HOST = unreleasedFlag(504, "qs_pipeline_new_host", teamfood = true) // TODO(b/278068252): Tracking Bug @JvmField @@ -383,7 +391,7 @@ object Flags { val MEDIA_TAP_TO_TRANSFER_DISMISS_GESTURE = releasedFlag(912, "media_ttt_dismiss_gesture") // TODO(b/266157412): Tracking Bug - val MEDIA_RETAIN_SESSIONS = releasedFlag(913, "media_retain_sessions") + val MEDIA_RETAIN_SESSIONS = unreleasedFlag(913, "media_retain_sessions") // TODO(b/266739309): Tracking Bug @JvmField @@ -393,10 +401,10 @@ object Flags { val MEDIA_RESUME_PROGRESS = releasedFlag(915, "media_resume_progress") // TODO(b/267166152) : Tracking Bug - val MEDIA_RETAIN_RECOMMENDATIONS = releasedFlag(916, "media_retain_recommendations") + val MEDIA_RETAIN_RECOMMENDATIONS = unreleasedFlag(916, "media_retain_recommendations") // TODO(b/270437894): Tracking Bug - val MEDIA_REMOTE_RESUME = releasedFlag(917, "media_remote_resume") + val MEDIA_REMOTE_RESUME = unreleasedFlag(917, "media_remote_resume") // 1000 - dock val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging") @@ -592,6 +600,8 @@ object Flags { // 1700 - clipboard @JvmField val CLIPBOARD_REMOTE_BEHAVIOR = releasedFlag(1701, "clipboard_remote_behavior") + // TODO(b/278714186) Tracking Bug + @JvmField val CLIPBOARD_IMAGE_TIMEOUT = unreleasedFlag(1702, "clipboard_image_timeout") // 1800 - shade container // TODO(b/265944639): Tracking Bug @@ -615,12 +625,14 @@ object Flags { // TODO(b/269132640): Tracking Bug @JvmField val APP_PANELS_REMOVE_APPS_ALLOWED = - unreleasedFlag(2003, "app_panels_remove_apps_allowed", teamfood = true) + releasedFlag(2003, "app_panels_remove_apps_allowed") - // 2200 - udfps + // 2200 - biometrics (udfps, sfps, BiometricPrompt, etc.) // TODO(b/259264861): Tracking Bug @JvmField val UDFPS_NEW_TOUCH_DETECTION = releasedFlag(2200, "udfps_new_touch_detection") @JvmField val UDFPS_ELLIPSE_DETECTION = releasedFlag(2201, "udfps_ellipse_detection") + // TODO(b/278622168): Tracking Bug + @JvmField val BIOMETRIC_BP_STRONG = unreleasedFlag(2202, "biometric_bp_strong") // 2300 - stylus @JvmField val TRACK_STYLUS_EVER_USED = releasedFlag(2300, "track_stylus_ever_used") diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index d3b6fc237084..f64ed6078ddb 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -128,6 +128,7 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.phone.CentralSurfaces; +import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -240,6 +241,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene private final ScreenshotHelper mScreenshotHelper; private final SysuiColorExtractor mSysuiColorExtractor; private final IStatusBarService mStatusBarService; + protected final LightBarController mLightBarController; protected final NotificationShadeWindowController mNotificationShadeWindowController; private final IWindowManager mIWindowManager; private final Executor mBackgroundExecutor; @@ -349,6 +351,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene MetricsLogger metricsLogger, SysuiColorExtractor colorExtractor, IStatusBarService statusBarService, + LightBarController lightBarController, NotificationShadeWindowController notificationShadeWindowController, IWindowManager iWindowManager, @Background Executor backgroundExecutor, @@ -381,6 +384,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mUiEventLogger = uiEventLogger; mSysuiColorExtractor = colorExtractor; mStatusBarService = statusBarService; + mLightBarController = lightBarController; mNotificationShadeWindowController = notificationShadeWindowController; mIWindowManager = iWindowManager; mBackgroundExecutor = backgroundExecutor; @@ -694,6 +698,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene ActionsDialogLite dialog = new ActionsDialogLite(mContext, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite, mAdapter, mOverflowAdapter, mSysuiColorExtractor, mStatusBarService, + mLightBarController, mNotificationShadeWindowController, this::onRefresh, mKeyguardShowing, mPowerAdapter, mUiEventLogger, mCentralSurfacesOptional, mKeyguardUpdateMonitor, mLockPatternUtils); @@ -2192,6 +2197,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene protected final SysuiColorExtractor mColorExtractor; private boolean mKeyguardShowing; protected float mScrimAlpha; + protected final LightBarController mLightBarController; protected final NotificationShadeWindowController mNotificationShadeWindowController; private ListPopupWindow mOverflowPopup; private Dialog mPowerOptionsDialog; @@ -2267,6 +2273,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene ActionsDialogLite(Context context, int themeRes, MyAdapter adapter, MyOverflowAdapter overflowAdapter, SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService, + LightBarController lightBarController, NotificationShadeWindowController notificationShadeWindowController, Runnable onRefreshCallback, boolean keyguardShowing, MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger, @@ -2282,6 +2289,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mPowerOptionsAdapter = powerAdapter; mColorExtractor = sysuiColorExtractor; mStatusBarService = statusBarService; + mLightBarController = lightBarController; mNotificationShadeWindowController = notificationShadeWindowController; mOnRefreshCallback = onRefreshCallback; mKeyguardShowing = keyguardShowing; @@ -2474,6 +2482,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene @Override protected void start() { mGlobalActionsLayout.updateList(); + mLightBarController.setGlobalActionsVisible(true); if (mBackgroundDrawable instanceof ScrimDrawable) { mColorExtractor.addOnColorsChangedListener(this); @@ -2504,6 +2513,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene @Override protected void stop() { + mLightBarController.setGlobalActionsVisible(false); mColorExtractor.removeOnColorsChangedListener(this); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java index b92499eb883e..b7ba2019777e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java @@ -56,11 +56,11 @@ public class WorkLockActivityController { tscl.registerTaskStackListener(mLockListener); } - private void startWorkChallengeInTask(ActivityManager.RunningTaskInfo info) { + private void startWorkChallengeInTask(ActivityManager.RunningTaskInfo info, int userId) { String packageName = info.baseActivity != null ? info.baseActivity.getPackageName() : ""; Intent intent = new Intent(KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER) .setComponent(new ComponentName(mContext, WorkLockActivity.class)) - .putExtra(Intent.EXTRA_USER_ID, info.userId) + .putExtra(Intent.EXTRA_USER_ID, userId) .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) .addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -76,10 +76,11 @@ public class WorkLockActivityController { } else { // Starting the activity inside the task failed. We can't be sure why, so to be // safe just remove the whole task if it still exists. + Log.w(TAG, "Failed to start work lock activity, will remove task=" + info.taskId); try { mIatm.removeTask(info.taskId); } catch (RemoteException e) { - Log.w(TAG, "Failed to get description for task=" + info.taskId); + Log.e(TAG, "Failed to remove task=" + info.taskId); } } } @@ -112,8 +113,8 @@ public class WorkLockActivityController { private final TaskStackChangeListener mLockListener = new TaskStackChangeListener() { @Override - public void onTaskProfileLocked(ActivityManager.RunningTaskInfo info) { - startWorkChallengeInTask(info); + public void onTaskProfileLocked(ActivityManager.RunningTaskInfo info, int userId) { + startWorkChallengeInTask(info, userId); } }; } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt index 6bbc6f61cc6f..c1aefc7bcbd7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt @@ -112,8 +112,6 @@ object KeyguardBouncerViewBinder { viewModel.isShowing.collect { isShowing -> view.visibility = if (isShowing) View.VISIBLE else View.INVISIBLE if (isShowing) { - // Reset Security Container entirely. - view.visibility = View.VISIBLE securityContainerController.reinflateViewFlipper { // Reset Security Container entirely. securityContainerController.onBouncerVisibilityChanged( 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 a98a7d8f8639..951f0bd76a64 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 @@ -21,6 +21,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.graphics.Rect import android.hardware.display.DisplayManager import android.os.Bundle import android.os.IBinder @@ -33,6 +34,7 @@ import android.widget.FrameLayout import com.android.keyguard.ClockEventController import com.android.keyguard.KeyguardClockSwitch import com.android.systemui.R +import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main @@ -61,6 +63,7 @@ constructor( private val clockRegistry: ClockRegistry, private val broadcastDispatcher: BroadcastDispatcher, private val lockscreenSmartspaceController: LockscreenSmartspaceController, + private val udfpsOverlayInteractor: UdfpsOverlayInteractor, @Assisted bundle: Bundle, ) { @@ -112,7 +115,9 @@ constructor( setUpBottomArea(rootView) - setupSmartspace(rootView) + setUpSmartspace(rootView) + + setUpUdfps(rootView) setUpClock(rootView) @@ -169,49 +174,53 @@ constructor( /** * This sets up and shows a non-interactive smart space * - * The top padding is as follows: - * Status bar height + clock top margin + keyguard smart space top offset + * The top padding is as follows: Status bar height + clock top margin + keyguard smart space + * top offset * - * The start padding is as follows: - * Clock padding start + Below clock padding start + * The start padding is as follows: Clock padding start + Below clock padding start * - * The end padding is as follows: - * Below clock padding end + * The end padding is as follows: Below clock padding end */ - private fun setupSmartspace(parentView: ViewGroup) { - if (!lockscreenSmartspaceController.isEnabled() || - !lockscreenSmartspaceController.isDateWeatherDecoupled()) { + private fun setUpSmartspace(parentView: ViewGroup) { + if ( + !lockscreenSmartspaceController.isEnabled() || + !lockscreenSmartspaceController.isDateWeatherDecoupled() + ) { return } smartSpaceView = lockscreenSmartspaceController.buildAndConnectDateView(parentView) - val topPadding: Int = with(context.resources) { - getDimensionPixelSize(R.dimen.status_bar_header_height_keyguard) + + val topPadding: Int = + with(context.resources) { + getDimensionPixelSize(R.dimen.status_bar_header_height_keyguard) + getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) + getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) - } + } - val startPadding: Int = with(context.resources) { - getDimensionPixelSize(R.dimen.clock_padding_start) + + val startPadding: Int = + with(context.resources) { + getDimensionPixelSize(R.dimen.clock_padding_start) + getDimensionPixelSize(R.dimen.below_clock_padding_start) - } + } - val endPadding: Int = context.resources - .getDimensionPixelSize(R.dimen.below_clock_padding_end) + val endPadding: Int = + context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_end) smartSpaceView?.let { it.setPaddingRelative(startPadding, topPadding, endPadding, 0) it.isClickable = false parentView.addView( - it, - FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.WRAP_CONTENT, - ), + it, + FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.WRAP_CONTENT, + ), ) } + + smartSpaceView?.alpha = if (shouldHighlightSelectedAffordance) DIM_ALPHA else 1.0f } private fun setUpBottomArea(parentView: ViewGroup) { @@ -234,6 +243,33 @@ constructor( ) } + private fun setUpUdfps(parentView: ViewGroup) { + val sensorBounds = udfpsOverlayInteractor.udfpsOverlayParams.value.sensorBounds + + // If sensorBounds are default rect, then there is no UDFPS + if (sensorBounds == Rect()) { + return + } + + // Place the UDFPS view in the proper sensor location + val fingerprintLayoutParams = + FrameLayout.LayoutParams(sensorBounds.width(), sensorBounds.height()) + fingerprintLayoutParams.setMarginsRelative( + sensorBounds.left, + sensorBounds.top, + sensorBounds.right, + sensorBounds.bottom + ) + val finger = + LayoutInflater.from(context) + .inflate( + R.layout.udfps_keyguard_view_internal, + parentView, + false, + ) as View + parentView.addView(finger, fingerprintLayoutParams) + } + private fun setUpClock(parentView: ViewGroup) { val clockChangeListener = object : ClockRegistry.ClockChangeListener { @@ -266,8 +302,6 @@ constructor( disposables.add(DisposableHandle { broadcastDispatcher.unregisterReceiver(receiver) }) onClockChanged(parentView) - - updateSmartspaceWithSetupClock() } private fun onClockChanged(parentView: ViewGroup) { @@ -281,33 +315,22 @@ constructor( ?.onTargetRegionChanged(KeyguardClockSwitch.getLargeClockRegion(parentView)) clockView?.let { parentView.removeView(it) } - clockView = largeClock?.view?.apply { - if (shouldHighlightSelectedAffordance) { - alpha = DIM_ALPHA + clockView = + largeClock?.view?.apply { + if (shouldHighlightSelectedAffordance) { + alpha = DIM_ALPHA + } + parentView.addView(this) + visibility = View.VISIBLE } - parentView.addView(this) - visibility = View.VISIBLE - } } else { clockView?.visibility = View.GONE } - } - /** - * Updates smart space after clock is set up. Used to show or hide smartspace with the right - * opacity based on the clock after setup. - */ - private fun updateSmartspaceWithSetupClock() { + // Hide smart space if the clock has weather display; otherwise show it val hasCustomWeatherDataDisplay = - clockController - .clock - ?.largeClock - ?.config - ?.hasCustomWeatherDataDisplay == true - + clockController.clock?.largeClock?.config?.hasCustomWeatherDataDisplay == true hideSmartspace(hasCustomWeatherDataDisplay) - - smartSpaceView?.alpha = if (shouldHighlightSelectedAffordance) DIM_ALPHA else 1.0f } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt index 42fdd689df6c..592044e514be 100644 --- a/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt +++ b/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt @@ -27,19 +27,27 @@ import androidx.annotation.VisibleForTesting */ data class TableChange( var timestamp: Long = 0, - var columnPrefix: String = "", - var columnName: String = "", - var isInitial: Boolean = false, - var type: DataType = DataType.EMPTY, - var bool: Boolean = false, - var int: Int? = null, - var str: String? = null, + private var columnPrefix: String = "", + private var columnName: String = "", + private var isInitial: Boolean = false, + private var type: DataType = DataType.EMPTY, + private var bool: Boolean = false, + private var int: Int? = null, + private var str: String? = null, ) { + init { + // Truncate any strings that were passed into the constructor. [reset] and [set] will take + // care of the rest of the truncation. + this.columnPrefix = columnPrefix.take(MAX_STRING_LENGTH) + this.columnName = columnName.take(MAX_STRING_LENGTH) + this.str = str?.take(MAX_STRING_LENGTH) + } + /** Resets to default values so that the object can be recycled. */ fun reset(timestamp: Long, columnPrefix: String, columnName: String, isInitial: Boolean) { this.timestamp = timestamp - this.columnPrefix = columnPrefix - this.columnName = columnName + this.columnPrefix = columnPrefix.take(MAX_STRING_LENGTH) + this.columnName = columnName.take(MAX_STRING_LENGTH) this.isInitial = isInitial this.type = DataType.EMPTY this.bool = false @@ -50,7 +58,7 @@ data class TableChange( /** Sets this to store a string change. */ fun set(value: String?) { type = DataType.STRING - str = value + str = value?.take(MAX_STRING_LENGTH) } /** Sets this to store a boolean change. */ @@ -89,6 +97,8 @@ data class TableChange( } } + fun getColumnName() = columnName + fun getVal(): String { val value = when (type) { @@ -109,5 +119,8 @@ data class TableChange( companion object { @VisibleForTesting const val IS_INITIAL_PREFIX = "**" + // Don't allow any strings larger than this length so that we have a hard upper limit on the + // size of the data stored by the buffer. + @VisibleForTesting const val MAX_STRING_LENGTH = 500 } } diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt index 9d883cc10d6c..8babfc90d269 100644 --- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt +++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt @@ -291,7 +291,7 @@ class TableLogBuffer( private fun echoToDesiredEndpoints(change: TableChange) { if ( logcatEchoTracker.isBufferLoggable(bufferName = name, LogLevel.DEBUG) || - logcatEchoTracker.isTagLoggable(change.columnName, LogLevel.DEBUG) + logcatEchoTracker.isTagLoggable(change.getColumnName(), LogLevel.DEBUG) ) { if (change.hasData()) { localLogcat.d(name, change.logcatRepresentation()) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt index 9997730fa938..fa4211487d1d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt @@ -43,7 +43,6 @@ import android.media.session.PlaybackState import android.net.Uri import android.os.Parcelable import android.os.Process -import android.os.RemoteException import android.os.UserHandle import android.provider.Settings import android.service.notification.StatusBarNotification @@ -53,7 +52,6 @@ import android.util.Log import android.util.Pair as APair import androidx.media.utils.MediaConstants import com.android.internal.logging.InstanceId -import com.android.internal.statusbar.IStatusBarService import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.Dumpable import com.android.systemui.R @@ -139,8 +137,6 @@ internal val EMPTY_SMARTSPACE_MEDIA_DATA = expiryTimeMs = 0, ) -const val MEDIA_TITLE_ERROR_MESSAGE = "Invalid media data: title is null or blank." - fun isMediaNotification(sbn: StatusBarNotification): Boolean { return sbn.notification.isMediaNotification() } @@ -185,7 +181,6 @@ class MediaDataManager( private val logger: MediaUiEventLogger, private val smartspaceManager: SmartspaceManager, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, - private val statusBarService: IStatusBarService, ) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener { companion object { @@ -257,7 +252,6 @@ class MediaDataManager( mediaFlags: MediaFlags, logger: MediaUiEventLogger, smartspaceManager: SmartspaceManager, - statusBarService: IStatusBarService, keyguardUpdateMonitor: KeyguardUpdateMonitor, ) : this( context, @@ -283,7 +277,6 @@ class MediaDataManager( logger, smartspaceManager, keyguardUpdateMonitor, - statusBarService, ) private val appChangeReceiver = @@ -385,21 +378,21 @@ class MediaDataManager( fun onNotificationAdded(key: String, sbn: StatusBarNotification) { if (useQsMediaPlayer && isMediaNotification(sbn)) { - var isNewlyActiveEntry = false + var logEvent = false Assert.isMainThread() val oldKey = findExistingEntry(key, sbn.packageName) if (oldKey == null) { val instanceId = logger.getNewInstanceId() val temp = LOADING.copy(packageName = sbn.packageName, instanceId = instanceId) mediaEntries.put(key, temp) - isNewlyActiveEntry = true + logEvent = true } else if (oldKey != key) { // Resume -> active conversion; move to new key val oldData = mediaEntries.remove(oldKey)!! - isNewlyActiveEntry = true + logEvent = true mediaEntries.put(key, oldData) } - loadMediaData(key, sbn, oldKey, isNewlyActiveEntry) + loadMediaData(key, sbn, oldKey, logEvent) } else { onNotificationRemoved(key) } @@ -482,9 +475,9 @@ class MediaDataManager( key: String, sbn: StatusBarNotification, oldKey: String?, - isNewlyActiveEntry: Boolean = false, + logEvent: Boolean = false ) { - backgroundExecutor.execute { loadMediaDataInBg(key, sbn, oldKey, isNewlyActiveEntry) } + backgroundExecutor.execute { loadMediaDataInBg(key, sbn, oldKey, logEvent) } } /** Add a listener for changes in this class */ @@ -608,11 +601,9 @@ class MediaDataManager( } } - private fun removeEntry(key: String, logEvent: Boolean = true) { + private fun removeEntry(key: String) { mediaEntries.remove(key)?.let { - if (logEvent) { - logger.logMediaRemoved(it.appUid, it.packageName, it.instanceId) - } + logger.logMediaRemoved(it.appUid, it.packageName, it.instanceId) } notifyMediaDataRemoved(key) } @@ -760,7 +751,7 @@ class MediaDataManager( key: String, sbn: StatusBarNotification, oldKey: String?, - isNewlyActiveEntry: Boolean = false, + logEvent: Boolean = false ) { val token = sbn.notification.extras.getParcelable( @@ -774,34 +765,6 @@ class MediaDataManager( val metadata = mediaController.metadata val notif: Notification = sbn.notification - // Song name - var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE) - if (song == null) { - song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE) - } - if (song == null) { - song = HybridGroupManager.resolveTitle(notif) - } - // Media data must have a title. - if (song.isNullOrBlank()) { - try { - statusBarService.onNotificationError( - sbn.packageName, - sbn.tag, - sbn.id, - sbn.uid, - sbn.initialPid, - MEDIA_TITLE_ERROR_MESSAGE, - sbn.user.identifier - ) - } catch (e: RemoteException) { - Log.e(TAG, "cancelNotification failed: $e") - } - // Only add log for media removed if active media is updated with invalid title. - foregroundExecutor.execute { removeEntry(key, !isNewlyActiveEntry) } - return - } - val appInfo = notif.extras.getParcelable( Notification.EXTRA_BUILDER_APPLICATION_INFO, @@ -830,6 +793,15 @@ class MediaDataManager( // App Icon val smallIcon = sbn.notification.smallIcon + // Song name + var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE) + if (song == null) { + song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE) + } + if (song == null) { + song = HybridGroupManager.resolveTitle(notif) + } + // Explicit Indicator var isExplicit = false if (mediaFlags.isExplicitIndicatorEnabled()) { @@ -901,7 +873,7 @@ class MediaDataManager( val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId() val appUid = appInfo?.uid ?: Process.INVALID_UID - if (isNewlyActiveEntry) { + if (logEvent) { logSingleVsMultipleMediaAdded(appUid, sbn.packageName, instanceId) logger.logActiveMediaAdded(appUid, sbn.packageName, instanceId, playbackLocation) } else if (playbackLocation != currentEntry?.playbackLocation) { diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index 41e3e6d83eea..9a1efc3d6456 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -15,6 +15,8 @@ */ package com.android.systemui.navigationbar.gestural; +import static android.view.InputDevice.SOURCE_MOUSE; +import static android.view.InputDevice.SOURCE_TOUCHPAD; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION; import static com.android.systemui.classifier.Classifier.BACK_GESTURE; @@ -937,10 +939,12 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mMLResults = 0; mLogGesture = false; mInRejectedExclusion = false; - boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY()); // Trackpad back gestures don't have zones, so we don't need to check if the down event - // is within insets. + // is within insets. Also we don't allow back for button press from the trackpad, and + // yet we do with a mouse. + boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY()); mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed + && !isButtonPressFromTrackpad(ev) && (isTrackpadMultiFingerSwipe || isWithinInsets) && !mGestureBlockingActivityRunning && !QuickStepContract.isBackGestureDisabled(mSysUiFlags) @@ -1047,6 +1051,11 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mProtoTracer.scheduleFrameUpdate(); } + private boolean isButtonPressFromTrackpad(MotionEvent ev) { + int sources = InputManager.getInstance().getInputDevice(ev.getDeviceId()).getSources(); + return (sources & (SOURCE_MOUSE | SOURCE_TOUCHPAD)) == sources && ev.getButtonState() != 0; + } + private void dispatchToBackAnimation(MotionEvent event) { if (mBackAnimation != null) { mVelocityTracker.addMovement(event); diff --git a/packages/SystemUI/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivity.kt new file mode 100644 index 000000000000..c209a000deb4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivity.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.notetask + +import android.os.Bundle +import androidx.activity.ComponentActivity +import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig +import javax.inject.Inject + +/** + * An internal proxy activity that starts the notes role setting. + * + * This activity is introduced mainly for the error handling of the notes app lock screen shortcut + * picker, which only supports package + action but not extras. See + * [KeyguardQuickAffordanceConfig.PickerScreenState.Disabled.actionComponentName]. + */ +class LaunchNotesRoleSettingsTrampolineActivity +@Inject +constructor( + private val controller: NoteTaskController, +) : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val entryPoint = + if (intent?.action == ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE) { + NoteTaskEntryPoint.QUICK_AFFORDANCE + } else { + null + } + controller.startNotesRoleSetting(this, entryPoint) + finish() + } + + companion object { + const val ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE = + "com.android.systemui.action.MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt index d4052f54b3da..7e9b346c9577 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt @@ -31,15 +31,14 @@ import android.content.Intent import android.content.pm.PackageManager import android.content.pm.ShortcutManager import android.graphics.drawable.Icon -import android.os.Build import android.os.UserHandle import android.os.UserManager -import android.util.Log import android.widget.Toast import androidx.annotation.VisibleForTesting import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled +import com.android.systemui.log.DebugLogger.debugLog import com.android.systemui.notetask.NoteTaskRoleManagerExt.createNoteShortcutInfoAsUser import com.android.systemui.notetask.NoteTaskRoleManagerExt.getDefaultRoleHolderAsUser import com.android.systemui.notetask.shortcut.LaunchNoteTaskManagedProfileProxyActivity @@ -92,10 +91,10 @@ constructor( if (info.launchMode != NoteTaskLaunchMode.AppBubble) return if (isExpanding) { - logDebug { "onBubbleExpandChanged - expanding: $info" } + debugLog { "onBubbleExpandChanged - expanding: $info" } eventLogger.logNoteTaskOpened(info) } else { - logDebug { "onBubbleExpandChanged - collapsing: $info" } + debugLog { "onBubbleExpandChanged - collapsing: $info" } eventLogger.logNoteTaskClosed(info) } } @@ -112,6 +111,43 @@ constructor( ) } + /** Starts the notes role setting. */ + fun startNotesRoleSetting(activityContext: Context, entryPoint: NoteTaskEntryPoint?) { + val user = + if (entryPoint == null) { + userTracker.userHandle + } else { + getUserForHandlingNotesTaking(entryPoint) + } + activityContext.startActivityAsUser( + Intent(Intent.ACTION_MANAGE_DEFAULT_APP).apply { + putExtra(Intent.EXTRA_ROLE_NAME, ROLE_NOTES) + }, + user + ) + } + + /** + * Returns the [UserHandle] of an android user that should handle the notes taking [entryPoint]. + * + * On company owned personally enabled (COPE) devices, if the given [entryPoint] is in the + * [FORCE_WORK_NOTE_APPS_ENTRY_POINTS_ON_COPE_DEVICES] list, the default notes app in the work + * profile user will always be launched. + * + * On non managed devices or devices with other management modes, the current [UserHandle] is + * returned. + */ + fun getUserForHandlingNotesTaking(entryPoint: NoteTaskEntryPoint): UserHandle = + if ( + entryPoint in FORCE_WORK_NOTE_APPS_ENTRY_POINTS_ON_COPE_DEVICES && + devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile + ) { + userTracker.userProfiles.firstOrNull { userManager.isManagedProfile(it.id) }?.userHandle + ?: userTracker.userHandle + } else { + userTracker.userHandle + } + /** * Shows a note task. How the task is shown will depend on when the method is invoked. * @@ -122,30 +158,13 @@ constructor( * bubble is already opened. * * That will let users open other apps in full screen, and take contextual notes. - * - * On company owned personally enabled (COPE) devices, if the given [entryPoint] is in the - * [FORCE_WORK_NOTE_APPS_ENTRY_POINTS_ON_COPE_DEVICES] list, the default notes app in the work - * profile user will always be launched. */ fun showNoteTask( entryPoint: NoteTaskEntryPoint, ) { if (!isEnabled) return - val user: UserHandle = - if ( - entryPoint in FORCE_WORK_NOTE_APPS_ENTRY_POINTS_ON_COPE_DEVICES && - devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile - ) { - userTracker.userProfiles - .firstOrNull { userManager.isManagedProfile(it.id) } - ?.userHandle - ?: userTracker.userHandle - } else { - userTracker.userHandle - } - - showNoteTaskAsUser(entryPoint, user) + showNoteTaskAsUser(entryPoint, getUserForHandlingNotesTaking(entryPoint)) } /** A variant of [showNoteTask] which launches note task in the given [user]. */ @@ -168,14 +187,14 @@ constructor( isKeyguardLocked && devicePolicyManager.areKeyguardShortcutsDisabled(userId = user.identifier) ) { - logDebug { "Enterprise policy disallows launching note app when the screen is locked." } + debugLog { "Enterprise policy disallows launching note app when the screen is locked." } return } val info = resolver.resolveInfo(entryPoint, isKeyguardLocked, user) if (info == null) { - logDebug { "Default notes app isn't set" } + debugLog { "Default notes app isn't set" } showNoDefaultNotesAppToast() return } @@ -184,7 +203,7 @@ constructor( try { // TODO(b/266686199): We should handle when app not available. For now, we log. - logDebug { "onShowNoteTask - start: $info on user#${user.identifier}" } + debugLog { "onShowNoteTask - start: $info on user#${user.identifier}" } when (info.launchMode) { is NoteTaskLaunchMode.AppBubble -> { val intent = createNoteTaskIntent(info) @@ -192,7 +211,7 @@ constructor( Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget) bubbles.showOrHideAppBubble(intent, user, icon) // App bubble logging happens on `onBubbleExpandChanged`. - logDebug { "onShowNoteTask - opened as app bubble: $info" } + debugLog { "onShowNoteTask - opened as app bubble: $info" } } is NoteTaskLaunchMode.Activity -> { if (activityManager.isInForeground(info.packageName)) { @@ -200,20 +219,20 @@ constructor( val intent = createHomeIntent() context.startActivityAsUser(intent, user) eventLogger.logNoteTaskClosed(info) - logDebug { "onShowNoteTask - closed as activity: $info" } + debugLog { "onShowNoteTask - closed as activity: $info" } } else { val intent = createNoteTaskIntent(info) context.startActivityAsUser(intent, user) eventLogger.logNoteTaskOpened(info) - logDebug { "onShowNoteTask - opened as activity: $info" } + debugLog { "onShowNoteTask - opened as activity: $info" } } } } - logDebug { "onShowNoteTask - success: $info" } + debugLog { "onShowNoteTask - success: $info" } } catch (e: ActivityNotFoundException) { - logDebug { "onShowNoteTask - failed: $info" } + debugLog { "onShowNoteTask - failed: $info" } } - logDebug { "onShowNoteTask - completed: $info" } + debugLog { "onShowNoteTask - completed: $info" } } @VisibleForTesting @@ -253,7 +272,7 @@ constructor( PackageManager.DONT_KILL_APP, ) - logDebug { "setNoteTaskShortcutEnabled - completed: $isEnabled" } + debugLog { "setNoteTaskShortcutEnabled - completed: $isEnabled" } } /** @@ -352,11 +371,6 @@ private fun createNoteTaskIntent(info: NoteTaskInfo): Intent = } } -/** [Log.println] a [Log.DEBUG] message, only when [Build.IS_DEBUGGABLE]. */ -private inline fun Any.logDebug(message: () -> String) { - if (Build.IS_DEBUGGABLE) Log.d(this::class.java.simpleName.orEmpty(), message()) -} - /** Creates an [Intent] which forces the current app to background by calling home. */ private fun createHomeIntent(): Intent = Intent(Intent.ACTION_MAIN).apply { diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt index 2c62ffd4841f..4d5173a1ec0a 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt @@ -45,6 +45,10 @@ interface NoteTaskModule { @[Binds IntoMap ClassKey(LaunchNoteTaskManagedProfileProxyActivity::class)] fun LaunchNoteTaskManagedProfileProxyActivity.bindNoteTaskLauncherProxyActivity(): Activity + @[Binds IntoMap ClassKey(LaunchNotesRoleSettingsTrampolineActivity::class)] + fun LaunchNotesRoleSettingsTrampolineActivity.bindLaunchNotesRoleSettingsTrampolineActivity(): + Activity + companion object { @[Provides NoteTaskEnabledKey] diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt index 2da5b7621723..444407c2341a 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt @@ -16,9 +16,12 @@ package com.android.systemui.notetask.quickaffordance +import android.app.role.OnRoleHoldersChangedListener +import android.app.role.RoleManager import android.content.Context import android.hardware.input.InputSettings import android.os.Build +import android.os.UserHandle import android.os.UserManager import android.util.Log import com.android.keyguard.KeyguardUpdateMonitor @@ -27,17 +30,22 @@ import com.android.systemui.R import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.PickerScreenState import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository +import com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity.Companion.ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE import com.android.systemui.notetask.NoteTaskController import com.android.systemui.notetask.NoteTaskEnabledKey -import com.android.systemui.notetask.NoteTaskEntryPoint +import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE +import com.android.systemui.notetask.NoteTaskInfoResolver +import com.android.systemui.shared.customization.data.content.CustomizationProviderContract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR import com.android.systemui.stylus.StylusManager import dagger.Lazy +import java.util.concurrent.Executor import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.trySendBlocking @@ -49,13 +57,16 @@ import kotlinx.coroutines.flow.onEach class NoteTaskQuickAffordanceConfig @Inject constructor( - context: Context, + private val context: Context, private val controller: NoteTaskController, + private val noteTaskInfoResolver: NoteTaskInfoResolver, private val stylusManager: StylusManager, + private val roleManager: RoleManager, private val keyguardMonitor: KeyguardUpdateMonitor, private val userManager: UserManager, private val lazyRepository: Lazy<KeyguardQuickAffordanceRepository>, @NoteTaskEnabledKey private val isEnabled: Boolean, + @Background private val backgroundExecutor: Executor, ) : KeyguardQuickAffordanceConfig { override val key = BuiltInKeyguardQuickAffordanceKeys.CREATE_NOTE @@ -73,15 +84,24 @@ constructor( val configSelectedFlow = repository.createConfigSelectedFlow(key) val stylusEverUsedFlow = stylusManager.createStylusEverUsedFlow(context) val userUnlockedFlow = userManager.createUserUnlockedFlow(keyguardMonitor) - combine(userUnlockedFlow, stylusEverUsedFlow, configSelectedFlow) { + val defaultNotesAppFlow = + roleManager.createNotesRoleFlow(backgroundExecutor, controller, noteTaskInfoResolver) + combine(userUnlockedFlow, stylusEverUsedFlow, configSelectedFlow, defaultNotesAppFlow) { isUserUnlocked, isStylusEverUsed, - isConfigSelected -> + isConfigSelected, + isDefaultNotesAppSet -> logDebug { "lockScreenState:isUserUnlocked=$isUserUnlocked" } logDebug { "lockScreenState:isStylusEverUsed=$isStylusEverUsed" } logDebug { "lockScreenState:isConfigSelected=$isConfigSelected" } + logDebug { "lockScreenState:isDefaultNotesAppSet=$isDefaultNotesAppSet" } - if (isEnabled && isUserUnlocked && (isConfigSelected || isStylusEverUsed)) { + if ( + isEnabled && + isUserUnlocked && + isDefaultNotesAppSet && + (isConfigSelected || isStylusEverUsed) + ) { val contentDescription = ContentDescription.Resource(pickerNameResourceId) val icon = Icon.Resource(pickerIconResourceId, contentDescription) LockScreenState.Visible(icon) @@ -92,15 +112,34 @@ constructor( .onEach { state -> logDebug { "lockScreenState=$state" } } } - override suspend fun getPickerScreenState() = - if (isEnabled) { - PickerScreenState.Default() - } else { - PickerScreenState.UnavailableOnDevice + override suspend fun getPickerScreenState(): PickerScreenState { + val isDefaultNotesAppSet = + noteTaskInfoResolver.resolveInfo( + QUICK_AFFORDANCE, + user = controller.getUserForHandlingNotesTaking(QUICK_AFFORDANCE) + ) != null + return when { + isEnabled && isDefaultNotesAppSet -> PickerScreenState.Default() + isEnabled -> { + PickerScreenState.Disabled( + listOf( + context.getString( + R.string.keyguard_affordance_enablement_dialog_notes_app_instruction + ) + ), + context.getString( + R.string.keyguard_affordance_enablement_dialog_notes_app_action + ), + "${context.packageName}$COMPONENT_NAME_SEPARATOR" + + "$ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE", + ) + } + else -> PickerScreenState.UnavailableOnDevice } + } override fun onTriggered(expandable: Expandable?): OnTriggeredResult { - controller.showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE) + controller.showNoteTask(entryPoint = QUICK_AFFORDANCE) return OnTriggeredResult.Handled } } @@ -129,6 +168,27 @@ private fun StylusManager.createStylusEverUsedFlow(context: Context) = callbackF awaitClose { unregisterCallback(callback) } } +private fun RoleManager.createNotesRoleFlow( + executor: Executor, + noteTaskController: NoteTaskController, + noteTaskInfoResolver: NoteTaskInfoResolver, +) = callbackFlow { + fun isDefaultNotesAppSetForUser() = + noteTaskInfoResolver.resolveInfo( + QUICK_AFFORDANCE, + user = noteTaskController.getUserForHandlingNotesTaking(QUICK_AFFORDANCE) + ) != null + + trySendBlocking(isDefaultNotesAppSetForUser()) + val callback = OnRoleHoldersChangedListener { roleName, _ -> + if (roleName == RoleManager.ROLE_NOTES) { + trySendBlocking(isDefaultNotesAppSetForUser()) + } + } + addOnRoleHoldersChangedListenerAsUser(executor, callback, UserHandle.ALL) + awaitClose { removeOnRoleHoldersChangedListenerAsUser(callback, UserHandle.ALL) } +} + private fun KeyguardQuickAffordanceRepository.createConfigSelectedFlow(key: String) = selections.map { selected -> selected.values.flatten().any { selectedConfig -> selectedConfig.key == key } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt index 0f38d32e0b64..8ca13b9776bb 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt @@ -18,12 +18,11 @@ package com.android.systemui.notetask.shortcut import android.content.Context import android.content.Intent -import android.os.Build import android.os.Bundle import android.os.UserHandle import android.os.UserManager -import android.util.Log import androidx.activity.ComponentActivity +import com.android.systemui.log.DebugLogger.debugLog import com.android.systemui.notetask.NoteTaskController import com.android.systemui.notetask.NoteTaskEntryPoint import com.android.systemui.settings.UserTracker @@ -68,7 +67,7 @@ constructor( val mainUser: UserHandle? = userManager.mainUser if (userManager.isManagedProfile) { if (mainUser == null) { - logDebug { "Can't find the main user. Skipping the notes app launch." } + debugLog { "Can't find the main user. Skipping the notes app launch." } } else { controller.startNoteTaskProxyActivityForUser(mainUser) } @@ -89,8 +88,3 @@ constructor( } } } - -/** [Log.println] a [Log.DEBUG] message, only when [Build.IS_DEBUGGABLE]. */ -private inline fun Any.logDebug(message: () -> String) { - if (Build.IS_DEBUGGABLE) Log.d(this::class.java.simpleName.orEmpty(), message()) -} diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt index 79167f276576..166ba9fba166 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt @@ -15,6 +15,7 @@ package com.android.systemui.privacy import android.content.Context +import android.content.res.Configuration import android.util.AttributeSet import android.view.Gravity.CENTER_VERTICAL import android.view.Gravity.END @@ -102,6 +103,11 @@ class OngoingPrivacyChip @JvmOverloads constructor( R.string.ongoing_privacy_chip_content_multiple_apps, typesText) } + override fun onConfigurationChanged(newConfig: Configuration?) { + super.onConfigurationChanged(newConfig) + updateResources() + } + private fun updateResources() { iconMargin = context.resources .getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_margin) @@ -110,8 +116,11 @@ class OngoingPrivacyChip @JvmOverloads constructor( iconColor = Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary) + val height = context.resources + .getDimensionPixelSize(R.dimen.ongoing_appops_chip_height) val padding = context.resources .getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding) + iconsContainer.layoutParams.height = height iconsContainer.setPaddingRelative(padding, 0, padding, 0) iconsContainer.background = context.getDrawable(R.drawable.statusbar_privacy_chip_bg) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index b7f9f6bc0e4a..1afc885cb70d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -38,7 +38,9 @@ import java.io.PrintWriter; */ public class QSContainerImpl extends FrameLayout implements Dumpable { + private int mFancyClippingLeftInset; private int mFancyClippingTop; + private int mFancyClippingRightInset; private int mFancyClippingBottom; private final float[] mFancyClippingRadii = new float[] {0, 0, 0, 0, 0, 0, 0, 0}; private final Path mFancyClippingPath = new Path(); @@ -53,6 +55,7 @@ public class QSContainerImpl extends FrameLayout implements Dumpable { private boolean mQsDisabled; private int mContentHorizontalPadding = -1; private boolean mClippingEnabled; + private boolean mIsFullWidth; public QSContainerImpl(Context context, AttributeSet attrs) { super(context, attrs); @@ -237,7 +240,8 @@ public class QSContainerImpl extends FrameLayout implements Dumpable { /** * Clip QS bottom using a concave shape. */ - public void setFancyClipping(int top, int bottom, int radius, boolean enabled) { + public void setFancyClipping(int leftInset, int top, int rightInset, int bottom, int radius, + boolean enabled, boolean fullWidth) { boolean updatePath = false; if (mFancyClippingRadii[0] != radius) { mFancyClippingRadii[0] = radius; @@ -246,10 +250,18 @@ public class QSContainerImpl extends FrameLayout implements Dumpable { mFancyClippingRadii[3] = radius; updatePath = true; } + if (mFancyClippingLeftInset != leftInset) { + mFancyClippingLeftInset = leftInset; + updatePath = true; + } if (mFancyClippingTop != top) { mFancyClippingTop = top; updatePath = true; } + if (mFancyClippingRightInset != rightInset) { + mFancyClippingRightInset = rightInset; + updatePath = true; + } if (mFancyClippingBottom != bottom) { mFancyClippingBottom = bottom; updatePath = true; @@ -258,6 +270,10 @@ public class QSContainerImpl extends FrameLayout implements Dumpable { mClippingEnabled = enabled; updatePath = true; } + if (mIsFullWidth != fullWidth) { + mIsFullWidth = fullWidth; + updatePath = true; + } if (updatePath) { updateClippingPath(); @@ -281,15 +297,21 @@ public class QSContainerImpl extends FrameLayout implements Dumpable { return; } - mFancyClippingPath.addRoundRect(0, mFancyClippingTop, getWidth(), + int clippingLeft = mIsFullWidth ? -mFancyClippingLeftInset : 0; + int clippingRight = mIsFullWidth ? getWidth() + mFancyClippingRightInset : getWidth(); + mFancyClippingPath.addRoundRect(clippingLeft, mFancyClippingTop, clippingRight, mFancyClippingBottom, mFancyClippingRadii, Path.Direction.CW); invalidate(); } @Override public void dump(PrintWriter pw, String[] args) { - pw.println(getClass().getSimpleName() + " updateClippingPath: top(" - + mFancyClippingTop + ") bottom(" + mFancyClippingBottom + ") mClippingEnabled(" - + mClippingEnabled + ")"); + pw.println(getClass().getSimpleName() + " updateClippingPath: " + + "leftInset(" + mFancyClippingLeftInset + ") " + + "top(" + mFancyClippingTop + ") " + + "rightInset(" + mFancyClippingRightInset + ") " + + "bottom(" + mFancyClippingBottom + ") " + + "mClippingEnabled(" + mClippingEnabled + ") " + + "mIsFullWidth(" + mIsFullWidth + ")"); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index d806afa91dd1..fd3f70148a69 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -18,9 +18,10 @@ import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; import static com.android.systemui.media.dagger.MediaModule.QS_PANEL; import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL; -import static com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; +import static com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.res.Configuration; @@ -395,9 +396,11 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } @Override - public void setFancyClipping(int top, int bottom, int cornerRadius, boolean visible) { + public void setFancyClipping(int leftInset, int top, int rightInset, int bottom, + int cornerRadius, boolean visible, boolean fullWidth) { if (getView() instanceof QSContainerImpl) { - ((QSContainerImpl) getView()).setFancyClipping(top, bottom, cornerRadius, visible); + ((QSContainerImpl) getView()).setFancyClipping(leftInset, top, rightInset, bottom, + cornerRadius, visible, fullWidth); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java index 0bce1f728e18..b70b94b00923 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java @@ -719,7 +719,8 @@ public class QSSecurityFooterUtils implements DialogInterface.OnClickListener { String name = vpnName != null ? vpnName : vpnNameWorkProfile; String namedVp = mDpm.getResources().getString( QS_DIALOG_MANAGEMENT_NAMED_VPN, - () -> mContext.getString(R.string.monitoring_description_named_vpn, name), + () -> mContext.getString( + R.string.monitoring_description_managed_device_named_vpn, name), name); message.append(namedVp); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt index 4a3199850e0f..b80668379e49 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt @@ -385,6 +385,11 @@ open class QSTileViewImpl @JvmOverloads constructor( super.onInitializeAccessibilityNodeInfo(info) // Clear selected state so it is not announce by talkback. info.isSelected = false + info.text = if (TextUtils.isEmpty(secondaryLabel.text)) { + "${label.text}" + } else { + "${label.text}, ${secondaryLabel.text}" + } if (lastDisabledByPolicy) { info.addAction( AccessibilityNodeInfo.AccessibilityAction( @@ -402,12 +407,6 @@ open class QSTileViewImpl @JvmOverloads constructor( accessibilityClass } if (Switch::class.java.name == accessibilityClass) { - val label = resources.getString( - if (tileState) R.string.switch_bar_on else R.string.switch_bar_off) - // Set the text here for tests in - // android.platform.test.scenario.sysui.quicksettings. Can be removed when - // UiObject2 has a new getStateDescription() API and tests are updated. - info.text = label info.isChecked = tileState info.isCheckable = true if (isLongClickable) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java index 7f7f8ad6a4c1..2d9f7dd038bc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java @@ -68,6 +68,7 @@ public class RotationLockTile extends QSTileImpl<BooleanState> implements private final SensorPrivacyManager mPrivacyManager; private final BatteryController mBatteryController; private final SettingObserver mSetting; + private final boolean mAllowRotationResolver; @Inject public RotationLockTile( @@ -105,6 +106,8 @@ public class RotationLockTile extends QSTileImpl<BooleanState> implements } }; mBatteryController.observe(getLifecycle(), this); + mAllowRotationResolver = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_allowRotationResolver); } @Override @@ -145,7 +148,7 @@ public class RotationLockTile extends QSTileImpl<BooleanState> implements final boolean powerSave = mBatteryController.isPowerSave(); final boolean cameraLocked = mPrivacyManager.isSensorPrivacyEnabled(CAMERA); - final boolean cameraRotation = + final boolean cameraRotation = mAllowRotationResolver && !powerSave && !cameraLocked && hasSufficientPermission(mContext) && mController.isCameraRotationEnabled(); state.value = !rotationLocked; diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt index f63bf07a5a3f..b34004397520 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt @@ -57,8 +57,8 @@ open class BaseScreenSharePermissionDialog( setContentView(R.layout.screen_share_dialog) dialogTitle = findViewById(R.id.screen_share_dialog_title) warning = findViewById(R.id.text_warning) - startButton = findViewById(R.id.button_start) - cancelButton = findViewById(R.id.button_cancel) + startButton = findViewById(android.R.id.button1) + cancelButton = findViewById(android.R.id.button2) updateIcon() initScreenShareOptions() createOptionsView(getOptionsViewLayoutId()) diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index 69008cca6fe3..84f358c303ca 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -33,6 +33,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.util.Log; @@ -60,11 +61,10 @@ public class RecordingService extends Service implements ScreenMediaRecorderList public static final int REQUEST_CODE = 2; private static final int USER_ID_NOT_SPECIFIED = -1; - private static final int NOTIFICATION_RECORDING_ID = 4274; - private static final int NOTIFICATION_PROCESSING_ID = 4275; - private static final int NOTIFICATION_VIEW_ID = 4273; + private static final int NOTIF_BASE_ID = 4273; private static final String TAG = "RecordingService"; private static final String CHANNEL_ID = "screen_record"; + private static final String GROUP_KEY = "screen_record_saved"; private static final String EXTRA_RESULT_CODE = "extra_resultCode"; private static final String EXTRA_PATH = "extra_path"; private static final String EXTRA_AUDIO_SOURCE = "extra_useAudio"; @@ -89,6 +89,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList private final UiEventLogger mUiEventLogger; private final NotificationManager mNotificationManager; private final UserContextProvider mUserContextTracker; + private int mNotificationId = NOTIF_BASE_ID; @Inject public RecordingService(RecordingController controller, @LongRunning Executor executor, @@ -134,14 +135,23 @@ public class RecordingService extends Service implements ScreenMediaRecorderList } String action = intent.getAction(); Log.d(TAG, "onStartCommand " + action); + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, + getString(R.string.screenrecord_title), + NotificationManager.IMPORTANCE_DEFAULT); + channel.setDescription(getString(R.string.screenrecord_channel_description)); + channel.enableVibration(true); + mNotificationManager.createNotificationChannel(channel); int currentUserId = mUserContextTracker.getUserContext().getUserId(); UserHandle currentUser = new UserHandle(currentUserId); switch (action) { case ACTION_START: + // Get a unique ID for this recording's notifications + mNotificationId = NOTIF_BASE_ID + (int) SystemClock.uptimeMillis(); mAudioSource = ScreenRecordingAudioSource .values()[intent.getIntExtra(EXTRA_AUDIO_SOURCE, 0)]; - Log.d(TAG, "recording with audio source" + mAudioSource); + Log.d(TAG, "recording with audio source " + mAudioSource); mShowTaps = intent.getBooleanExtra(EXTRA_SHOW_TAPS, false); MediaProjectionCaptureTarget captureTarget = intent.getParcelableExtra(EXTRA_CAPTURE_TARGET, @@ -169,7 +179,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList } else { updateState(false); createErrorNotification(); - stopForeground(true); + stopForeground(STOP_FOREGROUND_DETACH); stopSelf(); return Service.START_NOT_STICKY; } @@ -200,7 +210,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList startActivity(Intent.createChooser(shareIntent, shareLabel) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); // Remove notification - mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser); + mNotificationManager.cancelAsUser(null, mNotificationId, currentUser); return false; }, false, false); @@ -260,14 +270,6 @@ public class RecordingService extends Service implements ScreenMediaRecorderList @VisibleForTesting protected void createErrorNotification() { Resources res = getResources(); - NotificationChannel channel = new NotificationChannel( - CHANNEL_ID, - getString(R.string.screenrecord_title), - NotificationManager.IMPORTANCE_DEFAULT); - channel.setDescription(getString(R.string.screenrecord_channel_description)); - channel.enableVibration(true); - mNotificationManager.createNotificationChannel(channel); - Bundle extras = new Bundle(); extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, res.getString(R.string.screenrecord_title)); @@ -277,7 +279,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList .setSmallIcon(R.drawable.ic_screenrecord) .setContentTitle(notificationTitle) .addExtras(extras); - startForeground(NOTIFICATION_RECORDING_ID, builder.build()); + startForeground(mNotificationId, builder.build()); } @VisibleForTesting @@ -288,14 +290,6 @@ public class RecordingService extends Service implements ScreenMediaRecorderList @VisibleForTesting protected void createRecordingNotification() { Resources res = getResources(); - NotificationChannel channel = new NotificationChannel( - CHANNEL_ID, - getString(R.string.screenrecord_title), - NotificationManager.IMPORTANCE_DEFAULT); - channel.setDescription(getString(R.string.screenrecord_channel_description)); - channel.enableVibration(true); - mNotificationManager.createNotificationChannel(channel); - Bundle extras = new Bundle(); extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, res.getString(R.string.screenrecord_title)); @@ -323,7 +317,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList .setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE) .addAction(stopAction) .addExtras(extras); - startForeground(NOTIFICATION_RECORDING_ID, builder.build()); + startForeground(mNotificationId, builder.build()); } @VisibleForTesting @@ -337,11 +331,12 @@ public class RecordingService extends Service implements ScreenMediaRecorderList extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, res.getString(R.string.screenrecord_title)); - Notification.Builder builder = new Notification.Builder(getApplicationContext(), CHANNEL_ID) + Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID) .setContentTitle(notificationTitle) .setContentText( getResources().getString(R.string.screenrecord_background_processing_label)) .setSmallIcon(R.drawable.ic_screenrecord) + .setGroup(GROUP_KEY) .addExtras(extras); return builder.build(); } @@ -378,6 +373,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList PendingIntent.FLAG_IMMUTABLE)) .addAction(shareAction) .setAutoCancel(true) + .setGroup(GROUP_KEY) .addExtras(extras); // Add thumbnail if available @@ -391,6 +387,24 @@ public class RecordingService extends Service implements ScreenMediaRecorderList return builder.build(); } + /** + * Adds a group notification so that save notifications from multiple recordings are + * grouped together, and the foreground service recording notification is not + */ + private void postGroupNotification(UserHandle currentUser) { + Bundle extras = new Bundle(); + extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, + getResources().getString(R.string.screenrecord_title)); + Notification groupNotif = new Notification.Builder(this, CHANNEL_ID) + .setSmallIcon(R.drawable.ic_screenrecord) + .setContentTitle(getResources().getString(R.string.screenrecord_save_title)) + .setGroup(GROUP_KEY) + .setGroupSummary(true) + .setExtras(extras) + .build(); + mNotificationManager.notifyAsUser(TAG, NOTIF_BASE_ID, groupNotif, currentUser); + } + private void stopService() { stopService(USER_ID_NOT_SPECIFIED); } @@ -423,27 +437,26 @@ public class RecordingService extends Service implements ScreenMediaRecorderList Log.e(TAG, "stopRecording called, but recorder was null"); } updateState(false); + stopForeground(STOP_FOREGROUND_DETACH); stopSelf(); } private void saveRecording(int userId) { UserHandle currentUser = new UserHandle(userId); - mNotificationManager.notifyAsUser(null, NOTIFICATION_PROCESSING_ID, + mNotificationManager.notifyAsUser(null, mNotificationId, createProcessingNotification(), currentUser); mLongExecutor.execute(() -> { try { Log.d(TAG, "saving recording"); Notification notification = createSaveNotification(getRecorder().save()); - if (!mController.isRecording()) { - mNotificationManager.notifyAsUser(null, NOTIFICATION_VIEW_ID, notification, - currentUser); - } + postGroupNotification(currentUser); + mNotificationManager.notifyAsUser(null, mNotificationId, notification, + currentUser); } catch (IOException e) { Log.e(TAG, "Error saving screen recording: " + e.getMessage()); showErrorToast(R.string.screenrecord_delete_error); - } finally { - mNotificationManager.cancelAsUser(null, NOTIFICATION_PROCESSING_ID, currentUser); + mNotificationManager.cancelAsUser(null, mNotificationId, currentUser); } }); } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 51179153fc73..137a99ef39b8 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -4474,7 +4474,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mDisplayTopInset = combinedInsets.top; mDisplayRightInset = combinedInsets.right; mDisplayLeftInset = combinedInsets.left; - mQsController.setDisplayInsets(mDisplayRightInset, mDisplayLeftInset); + mQsController.setDisplayInsets(mDisplayLeftInset, mDisplayRightInset); mNavigationBarBottomHeight = insets.getStableInsetBottom(); updateMaxHeadsUpTranslation(); diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index 4012736b8beb..a1fa8fbf9075 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -85,11 +85,13 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.KeyguardStatusBarView; +import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; +import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.LargeScreenUtils; @@ -116,6 +118,7 @@ public class QuickSettingsController { private final PulseExpansionHandler mPulseExpansionHandler; private final ShadeExpansionStateManager mShadeExpansionStateManager; private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + private final LightBarController mLightBarController; private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; private final NotificationShadeDepthController mDepthController; @@ -135,6 +138,7 @@ public class QuickSettingsController { private final LockscreenGestureLogger mLockscreenGestureLogger; private final ShadeLogger mShadeLog; private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; + private final CastController mCastController; private final FeatureFlags mFeatureFlags; private final InteractionJankMonitor mInteractionJankMonitor; private final ShadeRepository mShadeRepository; @@ -302,6 +306,7 @@ public class QuickSettingsController { NotificationRemoteInputManager remoteInputManager, ShadeExpansionStateManager shadeExpansionStateManager, StatusBarKeyguardViewManager statusBarKeyguardViewManager, + LightBarController lightBarController, NotificationStackScrollLayoutController notificationStackScrollLayoutController, LockscreenShadeTransitionController lockscreenShadeTransitionController, NotificationShadeDepthController notificationShadeDepthController, @@ -324,7 +329,8 @@ public class QuickSettingsController { InteractionJankMonitor interactionJankMonitor, ShadeLogger shadeLog, KeyguardFaceAuthInteractor keyguardFaceAuthInteractor, - ShadeRepository shadeRepository + ShadeRepository shadeRepository, + CastController castController ) { mPanelViewControllerLazy = panelViewControllerLazy; mPanelView = panelView; @@ -343,6 +349,7 @@ public class QuickSettingsController { mRemoteInputManager = remoteInputManager; mShadeExpansionStateManager = shadeExpansionStateManager; mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; + mLightBarController = lightBarController; mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; mLockscreenShadeTransitionController = lockscreenShadeTransitionController; mDepthController = notificationShadeDepthController; @@ -364,6 +371,7 @@ public class QuickSettingsController { mMetricsLogger = metricsLogger; mShadeLog = shadeLog; mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; + mCastController = castController; mFeatureFlags = featureFlags; mInteractionJankMonitor = interactionJankMonitor; mShadeRepository = shadeRepository; @@ -879,7 +887,9 @@ public class QuickSettingsController { } void setOverScrollAmount(int overExpansion) { - mQs.setOverScrollAmount(overExpansion); + if (mQs != null) { + mQs.setOverScrollAmount(overExpansion); + } } private void setOverScrolling(boolean overscrolling) { @@ -1014,6 +1024,9 @@ public class QuickSettingsController { mShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction); mShadeHeaderController.setQsExpandedFraction(qsExpansionFraction); mShadeHeaderController.setQsVisible(mVisible); + + // Update the light bar + mLightBarController.setQsExpanded(mFullyExpanded); } float getLockscreenShadeDragProgress() { @@ -1188,7 +1201,9 @@ public class QuickSettingsController { mLastClipBounds.set(left, top, right, bottom); if (mIsFullWidth) { clipStatusView = qsVisible; - float screenCornerRadius = mRecordingController.isRecording() ? 0 : mScreenCornerRadius; + float screenCornerRadius = + mRecordingController.isRecording() || mCastController.hasConnectedCastDevice() + ? 0 : mScreenCornerRadius; radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius, Math.min(top / (float) mScrimCornerRadius, 1f)); mScrimController.setNotificationBottomRadius(radius); @@ -1217,10 +1232,13 @@ public class QuickSettingsController { mVisible = qsVisible; mQs.setQsVisible(qsVisible); mQs.setFancyClipping( + mDisplayLeftInset, clipTop, + mDisplayRightInset, clipBottom, radius, - qsVisible && !mSplitShadeEnabled); + qsVisible && !mSplitShadeEnabled, + mIsFullWidth); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt index 07b686948d7f..b6970aef6cad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt @@ -20,6 +20,7 @@ import android.app.ActivityManager import android.content.res.Resources import android.os.SystemProperties import android.os.Trace +import android.os.Trace.TRACE_TAG_APP import android.util.IndentingPrintWriter import android.util.MathUtils import android.view.CrossWindowBlurListeners @@ -43,8 +44,8 @@ open class BlurUtils @Inject constructor( ) : Dumpable { val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius) val maxBlurRadius = resources.getDimensionPixelSize(R.dimen.max_window_blur_radius) - private val traceCookie = System.identityHashCode(this) private var lastAppliedBlur = 0 + private var earlyWakeupEnabled = false init { dumpManager.registerDumpable(javaClass.name, this) @@ -72,6 +73,26 @@ open class BlurUtils @Inject constructor( } /** + * This method should be called before [applyBlur] so that, if needed, we can set the + * early-wakeup flag in SurfaceFlinger. + */ + fun prepareBlur(viewRootImpl: ViewRootImpl?, radius: Int) { + if (viewRootImpl == null || !viewRootImpl.surfaceControl.isValid || + !supportsBlursOnWindows() || earlyWakeupEnabled + ) { + return + } + if (lastAppliedBlur == 0 && radius != 0) { + Trace.asyncTraceForTrackBegin(TRACE_TAG_APP, TRACK_NAME, EARLY_WAKEUP_SLICE_NAME, 0) + earlyWakeupEnabled = true + createTransaction().use { + it.setEarlyWakeupStart() + it.apply() + } + } + } + + /** * Applies background blurs to a {@link ViewRootImpl}. * * @param viewRootImpl The window root. @@ -85,14 +106,20 @@ open class BlurUtils @Inject constructor( createTransaction().use { if (supportsBlursOnWindows()) { it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius) - if (lastAppliedBlur == 0 && radius != 0) { - Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, TRACK_NAME, - EARLY_WAKEUP_SLICE_NAME, traceCookie) + if (!earlyWakeupEnabled && lastAppliedBlur == 0 && radius != 0) { + Trace.asyncTraceForTrackBegin( + TRACE_TAG_APP, + TRACK_NAME, + EARLY_WAKEUP_SLICE_NAME, + 0 + ) it.setEarlyWakeupStart() + earlyWakeupEnabled = true } - if (lastAppliedBlur != 0 && radius == 0) { + if (earlyWakeupEnabled && lastAppliedBlur != 0 && radius == 0) { it.setEarlyWakeupEnd() - Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TRACK_NAME, traceCookie) + Trace.asyncTraceForTrackEnd(TRACE_TAG_APP, TRACK_NAME, 0) + earlyWakeupEnabled = false } lastAppliedBlur = radius } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java index 505f45d021b8..dcd9dc3e7fa3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java @@ -52,7 +52,6 @@ public class LegacyNotificationShelfControllerImpl implements NotificationShelfC mActivatableNotificationViewController = activatableNotificationViewController; mKeyguardBypassController = keyguardBypassController; mStatusBarStateController = statusBarStateController; - mView.useRoundnessSourceTypes(true); mView.setSensitiveRevealAnimEndabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM)); mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index 8dc78426da7d..a3bd247668fe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -189,12 +189,7 @@ class NotificationShadeDepthController @Inject constructor( scheduleUpdate() } - /** - * Callback that updates the window blur value and is called only once per frame. - */ - @VisibleForTesting - val updateBlurCallback = Choreographer.FrameCallback { - updateScheduled = false + private fun computeBlurAndZoomOut(): Pair<Int, Float> { val animationRadius = MathUtils.constrain(shadeAnimation.radius, blurUtils.minBlurRadius.toFloat(), blurUtils.maxBlurRadius.toFloat()) val expansionRadius = blurUtils.blurRadiusOfRatio( @@ -232,6 +227,16 @@ class NotificationShadeDepthController @Inject constructor( // Brightness slider removes blur, but doesn't affect zooms blur = (blur * (1f - brightnessMirrorSpring.ratio)).toInt() + return Pair(blur, zoomOut) + } + + /** + * Callback that updates the window blur value and is called only once per frame. + */ + @VisibleForTesting + val updateBlurCallback = Choreographer.FrameCallback { + updateScheduled = false + val (blur, zoomOut) = computeBlurAndZoomOut() val opaque = scrimsVisible && !blursDisabledForAppLaunch Trace.traceCounter(Trace.TRACE_TAG_APP, "shade_blur_radius", blur) blurUtils.applyBlur(root.viewRootImpl, blur, opaque) @@ -441,6 +446,8 @@ class NotificationShadeDepthController @Inject constructor( return } updateScheduled = true + val (blur, _) = computeBlurAndZoomOut() + blurUtils.prepareBlur(root.viewRootImpl, blur) choreographer.postFrameCallback(updateBlurCallback) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 7eb63da38028..49c7950fd923 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -44,7 +44,6 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; -import com.android.systemui.statusbar.notification.LegacySourceType; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; @@ -128,13 +127,6 @@ public class NotificationShelf extends ActivatableNotificationView implements St setClipToPadding(false); mShelfIcons.setIsStaticLayout(false); requestRoundness(/* top = */ 1f, /* bottom = */ 1f, BASE_VALUE, /* animate = */ false); - - if (!mUseRoundnessSourceTypes) { - // Setting this to first in section to get the clipping to the top roundness correct. - // This value determines the way we are clipping to the top roundness of the overall - // shade - setFirstInSection(true); - } updateResources(); } @@ -569,17 +561,8 @@ public class NotificationShelf extends ActivatableNotificationView implements St * mAmbientState.getExpansionFraction(); final float cornerAnimationTop = shelfStart - cornerAnimationDistance; - final SourceType sourceType; - if (mUseRoundnessSourceTypes) { - sourceType = SHELF_SCROLL; - } else { - sourceType = LegacySourceType.OnScroll; - } - final float topValue; - if (!mUseRoundnessSourceTypes && anv.isFirstInSection()) { - topValue = 1f; - } else if (viewStart >= cornerAnimationTop) { + if (viewStart >= cornerAnimationTop) { // Round top corners within animation bounds topValue = MathUtils.saturate( (viewStart - cornerAnimationTop) / cornerAnimationDistance); @@ -588,12 +571,10 @@ public class NotificationShelf extends ActivatableNotificationView implements St // Reset top and bottom corners outside of animation bounds. topValue = 0f; } - anv.requestTopRoundness(topValue, sourceType, /* animate = */ false); + anv.requestTopRoundness(topValue, SHELF_SCROLL, /* animate = */ false); final float bottomValue; - if (!mUseRoundnessSourceTypes && anv.isLastInSection()) { - bottomValue = 1f; - } else if (viewEnd >= cornerAnimationTop) { + if (viewEnd >= cornerAnimationTop) { // Round bottom corners within animation bounds bottomValue = MathUtils.saturate( (viewEnd - cornerAnimationTop) / cornerAnimationDistance); @@ -602,7 +583,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St // Reset top and bottom corners outside of animation bounds. bottomValue = 0f; } - anv.requestBottomRoundness(bottomValue, sourceType, /* animate = */ false); + anv.requestBottomRoundness(bottomValue, SHELF_SCROLL, /* animate = */ false); } private boolean isViewAffectedBySwipe(ExpandableView expandableView) { @@ -1100,15 +1081,6 @@ public class NotificationShelf extends ActivatableNotificationView implements St child.requestRoundnessReset(SHELF_SCROLL); } - /** - * This method resets the OnScroll roundness of a view to 0f - * <p> - * Note: This should be the only class that handles roundness {@code SourceType.OnScroll} - */ - public static void resetLegacyOnScrollRoundness(ExpandableView expandableView) { - expandableView.requestRoundnessReset(LegacySourceType.OnScroll); - } - @Override public void dump(PrintWriter pwOriginal, String[] args) { IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index 976924a2159d..821a17279cb3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -87,13 +87,8 @@ constructor( bypassController.isPulseExpanding = value if (changed) { if (value) { - val topEntry = headsUpManager.topEntry - topEntry?.let { - roundnessManager.setTrackingHeadsUp(it.row) - } lockscreenShadeTransitionController.onPulseExpansionStarted() } else { - roundnessManager.setTrackingHeadsUp(null) if (!leavingLockscreen) { bypassController.maybePerformPendingUnlock() pulseExpandAbortListener?.run() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 77550038c94a..129c8594e48e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -26,6 +26,7 @@ import android.annotation.IntDef; import android.app.ActivityManager; import android.app.Notification; import android.content.Context; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.res.ColorStateList; import android.content.res.Configuration; @@ -75,9 +76,9 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi */ private static final float DARK_ALPHA_BOOST = 0.67f; /** - * Status icons are currently drawn with the intention of being 17dp tall, but we - * want to scale them (in a way that doesn't require an asset dump) down 2dp. So - * 17dp * (15 / 17) = 15dp, the new height. After the first call to {@link #reloadDimens} all + * Status icons are currently drawn with the intention of being 17sp tall, but we + * want to scale them (in a way that doesn't require an asset dump) down 2sp. So + * 17sp * (15 / 17) = 15sp, the new height. After the first call to {@link #reloadDimens} all * values will be in px. */ private float mSystemIconDesiredHeight = 15f; @@ -144,7 +145,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi private String mNumberText; private StatusBarNotification mNotification; private final boolean mBlocked; - private int mDensity; + private Configuration mConfiguration; private boolean mNightMode; private float mIconScale = 1.0f; private final Paint mDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -198,9 +199,8 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi mNumberPain.setAntiAlias(true); setNotification(sbn); setScaleType(ScaleType.CENTER); - mDensity = context.getResources().getDisplayMetrics().densityDpi; - Configuration configuration = context.getResources().getConfiguration(); - mNightMode = (configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK) + mConfiguration = new Configuration(context.getResources().getConfiguration()); + mNightMode = (mConfiguration.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; initializeDecorColor(); reloadDimens(); @@ -214,7 +214,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi mAlwaysScaleIcon = true; reloadDimens(); maybeUpdateIconScaleDimens(); - mDensity = context.getResources().getDisplayMetrics().densityDpi; + mConfiguration = new Configuration(context.getResources().getConfiguration()); } /** Should always be preceded by {@link #reloadDimens()} */ @@ -231,12 +231,17 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi private void updateIconScaleForNotifications() { final float imageBounds = mIncreasedSize ? mStatusBarIconDrawingSizeIncreased : mStatusBarIconDrawingSize; - final int outerBounds = mStatusBarIconSize; - mIconScale = imageBounds / (float)outerBounds; + float iconHeight = getIconHeight(); + if (iconHeight != 0) { + mIconScale = imageBounds / iconHeight; + } else { + final int outerBounds = mStatusBarIconSize; + mIconScale = imageBounds / (float) outerBounds; + } updatePivot(); } - // Makes sure that all icons are scaled to the same height (15dp). If we cannot get a height + // Makes sure that all icons are scaled to the same height (15sp). If we cannot get a height // for the icon, it uses the default SCALE (15f / 17f) which is the old behavior private void updateIconScaleForSystemIcons() { float iconHeight = getIconHeight(); @@ -267,12 +272,10 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - int density = newConfig.densityDpi; - if (density != mDensity) { - mDensity = density; - reloadDimens(); - updateDrawable(); - maybeUpdateIconScaleDimens(); + final int configDiff = newConfig.diff(mConfiguration); + mConfiguration.setTo(newConfig); + if ((configDiff & (ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_FONT_SCALE)) != 0) { + updateIconDimens(); } boolean nightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; @@ -282,6 +285,15 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi } } + /** + * Update the icon dimens and drawable with current resources + */ + public void updateIconDimens() { + reloadDimens(); + updateDrawable(); + maybeUpdateIconScaleDimens(); + } + private void reloadDimens() { boolean applyRadius = mDotRadius == mStaticDotRadius; Resources res = getResources(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt index 76ff97ddb61b..212f2c215989 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt @@ -448,10 +448,3 @@ interface SourceType { } } } - -@Deprecated("Use SourceType.from() instead", ReplaceWith("SourceType.from()")) -enum class LegacySourceType : SourceType { - DefaultValue, - OnDismissAnimation, - OnScroll, -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 766ad88f8a55..66d4c3a97773 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -110,7 +110,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView protected Point mTargetPoint; private boolean mDismissed; private boolean mRefocusOnDismiss; - protected boolean mUseRoundnessSourceTypes; public ActivatableNotificationView(Context context, AttributeSet attrs) { super(context, attrs); @@ -709,18 +708,10 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mTouchHandler = touchHandler; } - /** - * Enable the support for rounded corner based on the SourceType - * @param enabled true if is supported - */ - public void useRoundnessSourceTypes(boolean enabled) { - mUseRoundnessSourceTypes = enabled; - } - @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (mUseRoundnessSourceTypes && !mOnDetachResetRoundness.isEmpty()) { + if (!mOnDetachResetRoundness.isEmpty()) { for (SourceType sourceType : mOnDetachResetRoundness) { requestRoundnessReset(sourceType); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index e468a59d4eb1..597813344d6e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -87,7 +87,6 @@ import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.notification.AboveShelfChangedListener; import com.android.systemui.statusbar.notification.FeedbackIcon; import com.android.systemui.statusbar.notification.LaunchAnimationParameters; -import com.android.systemui.statusbar.notification.LegacySourceType; import com.android.systemui.statusbar.notification.NotificationFadeAware; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController; import com.android.systemui.statusbar.notification.NotificationUtils; @@ -866,9 +865,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } onAttachedChildrenCountChanged(); row.setIsChildInGroup(false, null); - if (!mUseRoundnessSourceTypes) { - row.requestBottomRoundness(0.0f, LegacySourceType.DefaultValue, /* animate = */ false); - } } /** @@ -884,10 +880,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (child.keepInParentForDismissAnimation()) { mChildrenContainer.removeNotification(child); child.setIsChildInGroup(false, null); - if (!mUseRoundnessSourceTypes) { - LegacySourceType sourceType = LegacySourceType.DefaultValue; - child.requestBottomRoundness(0f, sourceType, /* animate = */ false); - } child.setKeepInParentForDismissAnimation(false); logKeepInParentChildDetached(child); childCountChanged = true; @@ -942,9 +934,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mNotificationParent.updateBackgroundForGroupState(); } updateBackgroundClipping(); - if (mUseRoundnessSourceTypes) { - updateBaseRoundness(); - } + updateBaseRoundness(); } @Override @@ -1054,15 +1044,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (isAboveShelf() != wasAboveShelf) { mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf); } - if (mUseRoundnessSourceTypes) { - if (pinned) { - // Should be animated if someone explicitly set it to 0 and the row is shown. - boolean animated = mAnimatePinnedRoundness && isShown(); - requestRoundness(/* top = */ 1f, /* bottom = */ 1f, PINNED, animated); - } else { - requestRoundnessReset(PINNED); - mAnimatePinnedRoundness = true; - } + if (pinned) { + // Should be animated if someone explicitly set it to 0 and the row is shown. + boolean animated = mAnimatePinnedRoundness && isShown(); + requestRoundness(/* top = */ 1f, /* bottom = */ 1f, PINNED, animated); + } else { + requestRoundnessReset(PINNED); + mAnimatePinnedRoundness = true; } } @@ -1879,7 +1867,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mChildrenContainer.setIsLowPriority(mIsLowPriority); mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this); mChildrenContainer.onNotificationUpdated(); - mChildrenContainer.useRoundnessSourceTypes(mUseRoundnessSourceTypes); mTranslateableViews.add(mChildrenContainer); }); @@ -2308,24 +2295,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mBackgroundNormal.setExpandAnimationSize(params.getWidth(), actualHeight); } - @Override - public float getTopRoundness() { - if (!mUseRoundnessSourceTypes && mExpandAnimationRunning) { - return mTopRoundnessDuringLaunchAnimation; - } - - return super.getTopRoundness(); - } - - @Override - public float getBottomRoundness() { - if (!mUseRoundnessSourceTypes && mExpandAnimationRunning) { - return mBottomRoundnessDuringLaunchAnimation; - } - - return super.getBottomRoundness(); - } - public void setExpandAnimationRunning(boolean expandAnimationRunning) { if (expandAnimationRunning) { setAboveShelf(true); @@ -3481,18 +3450,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private void applyChildrenRoundness() { if (mIsSummaryWithChildren) { - if (mUseRoundnessSourceTypes) { - mChildrenContainer.requestRoundness( - /* top = */ getTopRoundness(), - /* bottom = */ getBottomRoundness(), - /* sourceType = */ FROM_PARENT, - /* animate = */ false); - } else { - mChildrenContainer.requestBottomRoundness( - getBottomRoundness(), - LegacySourceType.DefaultValue, - /* animate = */ false); - } + mChildrenContainer.requestRoundness( + /* top = */ getTopRoundness(), + /* bottom = */ getBottomRoundness(), + /* sourceType = */ FROM_PARENT, + /* animate = */ false); } } @@ -3709,24 +3671,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } - /** - * Enable the support for rounded corner based on the SourceType - * @param enabled true if is supported - */ - @Override - public void useRoundnessSourceTypes(boolean enabled) { - super.useRoundnessSourceTypes(enabled); - if (mChildrenContainer != null) { - mChildrenContainer.useRoundnessSourceTypes(mUseRoundnessSourceTypes); - } - } - @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - if (mUseRoundnessSourceTypes) { - updateBaseRoundness(); - } + updateBaseRoundness(); } /** Set whether this notification may show a snooze action. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index 19c612ed21ff..1acc9f90b58f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -261,7 +261,6 @@ public class ExpandableNotificationRowController implements NotifViewController mStatusBarStateController.removeCallback(mStatusBarStateListener); } }); - mView.useRoundnessSourceTypes(true); } private final StatusBarStateController.StateListener mStatusBarStateListener = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index 9a777ea6230b..2c59c2e8a06f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -72,7 +72,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple private View mFeedbackIcon; private boolean mIsLowPriority; private boolean mTransformLowPriorityTitle; - private boolean mUseRoundnessSourceTypes; private RoundnessChangedListener mRoundnessChangedListener; protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) { @@ -122,7 +121,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple @Override public void applyRoundnessAndInvalidate() { - if (mUseRoundnessSourceTypes && mRoundnessChangedListener != null) { + if (mRoundnessChangedListener != null) { // We cannot apply the rounded corner to this View, so our parents (in drawChild()) will // clip our canvas. So we should invalidate our parent. mRoundnessChangedListener.applyRoundnessAndInvalidate(); @@ -377,15 +376,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple } /** - * Enable the support for rounded corner based on the SourceType - * - * @param enabled true if is supported - */ - public void useRoundnessSourceTypes(boolean enabled) { - mUseRoundnessSourceTypes = enabled; - } - - /** * Interface that handle the Roundness changes */ public interface RoundnessChangedListener { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt index 81067157ca5b..12956ab9498a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt @@ -81,7 +81,6 @@ object NotificationShelfViewBinder { ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager) shelf.apply { setRefactorFlagEnabled(true) - useRoundnessSourceTypes(true) setSensitiveRevealAnimEndabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM)) // TODO(278765923): Replace with eventual NotificationIconContainerViewBinder#bind() notificationIconAreaController.setShelfIcons(shelfIcons) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index 160a2309bfcc..d18757d3453f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -45,9 +45,7 @@ import com.android.internal.widget.NotificationExpandButton; import com.android.systemui.R; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.NotificationGroupingUtil; -import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.FeedbackIcon; -import com.android.systemui.statusbar.notification.LegacySourceType; import com.android.systemui.statusbar.notification.NotificationFadeAware; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.Roundable; @@ -133,7 +131,6 @@ public class NotificationChildrenContainer extends ViewGroup private int mUntruncatedChildCount; private boolean mContainingNotificationIsFaded = false; private RoundableState mRoundableState; - private boolean mUseRoundnessSourceTypes; public NotificationChildrenContainer(Context context) { this(context, null); @@ -328,13 +325,6 @@ public class NotificationChildrenContainer extends ViewGroup row.setContentTransformationAmount(0, false /* isLastChild */); row.setNotificationFaded(mContainingNotificationIsFaded); - if (!mUseRoundnessSourceTypes) { - // This is a workaround, the NotificationShelf should be the owner of `OnScroll` - // roundness. - // Here we should reset the `OnScroll` roundness only on top-level rows. - NotificationShelf.resetLegacyOnScrollRoundness(row); - } - // It doesn't make sense to keep old animations around, lets cancel them! ExpandableViewState viewState = row.getViewState(); if (viewState != null) { @@ -342,9 +332,7 @@ public class NotificationChildrenContainer extends ViewGroup row.cancelAppearDrawing(); } - if (mUseRoundnessSourceTypes) { - applyRoundnessAndInvalidate(); - } + applyRoundnessAndInvalidate(); } private void ensureRemovedFromTransientContainer(View v) { @@ -379,10 +367,8 @@ public class NotificationChildrenContainer extends ViewGroup mGroupingUtil.restoreChildNotification(row); } - if (mUseRoundnessSourceTypes) { - row.requestRoundnessReset(FROM_PARENT, /* animate = */ false); - applyRoundnessAndInvalidate(); - } + row.requestRoundnessReset(FROM_PARENT, /* animate = */ false); + applyRoundnessAndInvalidate(); } /** @@ -409,10 +395,7 @@ public class NotificationChildrenContainer extends ViewGroup getContext(), mNotificationHeader, mContainingNotification); - mNotificationHeaderWrapper.useRoundnessSourceTypes(mUseRoundnessSourceTypes); - if (mUseRoundnessSourceTypes) { - mNotificationHeaderWrapper.setOnRoundnessChangedListener(this::invalidate); - } + mNotificationHeaderWrapper.setOnRoundnessChangedListener(this::invalidate); addView(mNotificationHeader, 0); invalidate(); } else { @@ -450,12 +433,7 @@ public class NotificationChildrenContainer extends ViewGroup getContext(), mNotificationHeaderLowPriority, mContainingNotification); - mNotificationHeaderWrapperLowPriority.useRoundnessSourceTypes( - mUseRoundnessSourceTypes - ); - if (mUseRoundnessSourceTypes) { - mNotificationHeaderWrapper.setOnRoundnessChangedListener(this::invalidate); - } + mNotificationHeaderWrapper.setOnRoundnessChangedListener(this::invalidate); addView(mNotificationHeaderLowPriority, 0); invalidate(); } else { @@ -891,7 +869,7 @@ public class NotificationChildrenContainer extends ViewGroup isCanvasChanged = true; canvas.save(); - if (mUseRoundnessSourceTypes && translation != 0f) { + if (translation != 0f) { clipPath.offset(translation, 0f); canvas.clipPath(clipPath); clipPath.offset(-translation, 0f); @@ -1444,40 +1422,30 @@ public class NotificationChildrenContainer extends ViewGroup @Override public void applyRoundnessAndInvalidate() { boolean last = true; - if (mUseRoundnessSourceTypes) { - if (mNotificationHeaderWrapper != null) { - mNotificationHeaderWrapper.requestTopRoundness( - /* value = */ getTopRoundness(), - /* sourceType = */ FROM_PARENT, - /* animate = */ false - ); - } - if (mNotificationHeaderWrapperLowPriority != null) { - mNotificationHeaderWrapperLowPriority.requestTopRoundness( - /* value = */ getTopRoundness(), - /* sourceType = */ FROM_PARENT, - /* animate = */ false - ); - } + if (mNotificationHeaderWrapper != null) { + mNotificationHeaderWrapper.requestTopRoundness( + /* value = */ getTopRoundness(), + /* sourceType = */ FROM_PARENT, + /* animate = */ false + ); + } + if (mNotificationHeaderWrapperLowPriority != null) { + mNotificationHeaderWrapperLowPriority.requestTopRoundness( + /* value = */ getTopRoundness(), + /* sourceType = */ FROM_PARENT, + /* animate = */ false + ); } for (int i = mAttachedChildren.size() - 1; i >= 0; i--) { ExpandableNotificationRow child = mAttachedChildren.get(i); if (child.getVisibility() == View.GONE) { continue; } - if (mUseRoundnessSourceTypes) { - child.requestRoundness( - /* top = */ 0f, - /* bottom = */ last ? getBottomRoundness() : 0f, - /* sourceType = */ FROM_PARENT, - /* animate = */ false); - } else { - child.requestRoundness( - /* top = */ 0f, - /* bottom = */ last ? getBottomRoundness() : 0f, - LegacySourceType.DefaultValue, - /* animate = */ isShown()); - } + child.requestRoundness( + /* top = */ 0f, + /* bottom = */ last ? getBottomRoundness() : 0f, + /* sourceType = */ FROM_PARENT, + /* animate = */ false); last = false; } Roundable.super.applyRoundnessAndInvalidate(); @@ -1537,15 +1505,6 @@ public class NotificationChildrenContainer extends ViewGroup return mNotificationHeaderWrapper; } - /** - * Enable the support for rounded corner based on the SourceType - * - * @param enabled true if is supported - */ - public void useRoundnessSourceTypes(boolean enabled) { - mUseRoundnessSourceTypes = enabled; - } - public String debugString() { return TAG + " { " + "visibility: " + getVisibility() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java index b6aec83ae600..fa1843e34305 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java @@ -16,22 +16,13 @@ package com.android.systemui.statusbar.notification.stack; -import android.content.res.Resources; -import android.util.MathUtils; - import androidx.annotation.NonNull; import com.android.systemui.Dumpable; -import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; -import com.android.systemui.statusbar.notification.LegacySourceType; -import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; import com.android.systemui.statusbar.notification.Roundable; import com.android.systemui.statusbar.notification.SourceType; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.logging.NotificationRoundnessLogger; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import java.io.PrintWriter; @@ -48,63 +39,27 @@ public class NotificationRoundnessManager implements Dumpable { private static final String TAG = "NotificationRoundnessManager"; private static final SourceType DISMISS_ANIMATION = SourceType.from("DismissAnimation"); - private final ExpandableView[] mFirstInSectionViews; - private final ExpandableView[] mLastInSectionViews; - private final ExpandableView[] mTmpFirstInSectionViews; - private final ExpandableView[] mTmpLastInSectionViews; - private final NotificationRoundnessLogger mNotifLogger; private final DumpManager mDumpManager; - private boolean mExpanded; private HashSet<ExpandableView> mAnimatedChildren; - private Runnable mRoundingChangedCallback; - private ExpandableNotificationRow mTrackedHeadsUp; - private float mAppearFraction; private boolean mRoundForPulsingViews; private boolean mIsClearAllInProgress; private ExpandableView mSwipedView = null; private Roundable mViewBeforeSwipedView = null; private Roundable mViewAfterSwipedView = null; - private boolean mUseRoundnessSourceTypes; @Inject - NotificationRoundnessManager( - NotificationSectionsFeatureManager sectionsFeatureManager, - NotificationRoundnessLogger notifLogger, - DumpManager dumpManager) { - int numberOfSections = sectionsFeatureManager.getNumberOfBuckets(); - mFirstInSectionViews = new ExpandableView[numberOfSections]; - mLastInSectionViews = new ExpandableView[numberOfSections]; - mTmpFirstInSectionViews = new ExpandableView[numberOfSections]; - mTmpLastInSectionViews = new ExpandableView[numberOfSections]; - mNotifLogger = notifLogger; + NotificationRoundnessManager(DumpManager dumpManager) { mDumpManager = dumpManager; - mUseRoundnessSourceTypes = true; - mDumpManager.registerDumpable(TAG, this); } @Override public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { - pw.println("mFirstInSectionViews: length=" + mFirstInSectionViews.length); - pw.println(dumpViews(mFirstInSectionViews)); - pw.println("mLastInSectionViews: length=" + mLastInSectionViews.length); - pw.println(dumpViews(mFirstInSectionViews)); - if (mTrackedHeadsUp != null) { - pw.println("trackedHeadsUp=" + mTrackedHeadsUp.getEntry()); - } pw.println("roundForPulsingViews=" + mRoundForPulsingViews); pw.println("isClearAllInProgress=" + mIsClearAllInProgress); } - public void updateView(ExpandableView view, boolean animate) { - if (mUseRoundnessSourceTypes) return; - boolean changed = updateViewWithoutCallback(view, animate); - if (changed) { - mRoundingChangedCallback.run(); - } - } - public boolean isViewAffectedBySwipe(ExpandableView expandableView) { return expandableView != null && (expandableView == mSwipedView @@ -112,58 +67,6 @@ public class NotificationRoundnessManager implements Dumpable { || expandableView == mViewAfterSwipedView); } - boolean updateViewWithoutCallback( - ExpandableView view, - boolean animate) { - if (mUseRoundnessSourceTypes) return false; - if (view == null - || view == mViewBeforeSwipedView - || view == mViewAfterSwipedView) { - return false; - } - - final boolean isTopChanged = view.requestTopRoundness( - getRoundnessDefaultValue(view, true /* top */), - LegacySourceType.DefaultValue, - animate); - - final boolean isBottomChanged = view.requestBottomRoundness( - getRoundnessDefaultValue(view, /* top = */ false), - LegacySourceType.DefaultValue, - animate); - - final boolean isFirstInSection = isFirstInSection(view); - final boolean isLastInSection = isLastInSection(view); - - view.setFirstInSection(isFirstInSection); - view.setLastInSection(isLastInSection); - - mNotifLogger.onCornersUpdated(view, isFirstInSection, - isLastInSection, isTopChanged, isBottomChanged); - - return (isFirstInSection || isLastInSection) && (isTopChanged || isBottomChanged); - } - - private boolean isFirstInSection(ExpandableView view) { - if (mUseRoundnessSourceTypes) return false; - for (int i = 0; i < mFirstInSectionViews.length; i++) { - if (view == mFirstInSectionViews[i]) { - return true; - } - } - return false; - } - - private boolean isLastInSection(ExpandableView view) { - if (mUseRoundnessSourceTypes) return false; - for (int i = mLastInSectionViews.length - 1; i >= 0; i--) { - if (view == mLastInSectionViews[i]) { - return true; - } - } - return false; - } - void setViewsAffectedBySwipe( Roundable viewBefore, ExpandableView viewSwiped, @@ -177,34 +80,27 @@ public class NotificationRoundnessManager implements Dumpable { if (mSwipedView != null) oldViews.add(mSwipedView); if (mViewAfterSwipedView != null) oldViews.add(mViewAfterSwipedView); - final SourceType source; - if (mUseRoundnessSourceTypes) { - source = DISMISS_ANIMATION; - } else { - source = LegacySourceType.OnDismissAnimation; - } - mViewBeforeSwipedView = viewBefore; if (viewBefore != null) { oldViews.remove(viewBefore); - viewBefore.requestRoundness(/* top = */ 0f, /* bottom = */ 1f, source); + viewBefore.requestRoundness(/* top = */ 0f, /* bottom = */ 1f, DISMISS_ANIMATION); } mSwipedView = viewSwiped; if (viewSwiped != null) { oldViews.remove(viewSwiped); - viewSwiped.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, source); + viewSwiped.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, DISMISS_ANIMATION); } mViewAfterSwipedView = viewAfter; if (viewAfter != null) { oldViews.remove(viewAfter); - viewAfter.requestRoundness(/* top = */ 1f, /* bottom = */ 0f, source); + viewAfter.requestRoundness(/* top = */ 1f, /* bottom = */ 0f, DISMISS_ANIMATION); } // After setting the current Views, reset the views that are still present in the set. for (Roundable oldView : oldViews) { - oldView.requestRoundnessReset(source); + oldView.requestRoundnessReset(DISMISS_ANIMATION); } } @@ -226,143 +122,6 @@ public class NotificationRoundnessManager implements Dumpable { return mRoundForPulsingViews; } - private float getRoundnessDefaultValue(Roundable view, boolean top) { - if (mUseRoundnessSourceTypes) return 0f; - - if (view == null) { - return 0f; - } - if (view == mViewBeforeSwipedView - || view == mSwipedView - || view == mViewAfterSwipedView) { - return 1f; - } - if (view instanceof ExpandableNotificationRow - && ((ExpandableNotificationRow) view).canViewBeCleared() - && mIsClearAllInProgress) { - return 1.0f; - } - if (view instanceof ExpandableView) { - ExpandableView expandableView = (ExpandableView) view; - if ((expandableView.isPinned() - || (expandableView.isHeadsUpAnimatingAway()) && !mExpanded)) { - return 1.0f; - } - if (isFirstInSection(expandableView) && top) { - return 1.0f; - } - if (isLastInSection(expandableView) && !top) { - return 1.0f; - } - - if (view == mTrackedHeadsUp) { - // If we're pushing up on a headsup the appear fraction is < 0 and it needs to - // still be rounded. - return MathUtils.saturate(1.0f - mAppearFraction); - } - if (expandableView.showingPulsing() && mRoundForPulsingViews) { - return 1.0f; - } - if (expandableView.isChildInGroup()) { - return 0f; - } - final Resources resources = expandableView.getResources(); - return resources.getDimension(R.dimen.notification_corner_radius_small) - / resources.getDimension(R.dimen.notification_corner_radius); - } - return 0f; - } - - public void setExpanded(float expandedHeight, float appearFraction) { - if (mUseRoundnessSourceTypes) return; - mExpanded = expandedHeight != 0.0f; - mAppearFraction = appearFraction; - if (mTrackedHeadsUp != null) { - updateView(mTrackedHeadsUp, false /* animate */); - } - } - - public void updateRoundedChildren(NotificationSection[] sections) { - if (mUseRoundnessSourceTypes) return; - boolean anyChanged = false; - for (int i = 0; i < sections.length; i++) { - mTmpFirstInSectionViews[i] = mFirstInSectionViews[i]; - mTmpLastInSectionViews[i] = mLastInSectionViews[i]; - mFirstInSectionViews[i] = sections[i].getFirstVisibleChild(); - mLastInSectionViews[i] = sections[i].getLastVisibleChild(); - } - anyChanged |= handleRemovedOldViews(sections, mTmpFirstInSectionViews, true); - anyChanged |= handleRemovedOldViews(sections, mTmpLastInSectionViews, false); - anyChanged |= handleAddedNewViews(sections, mTmpFirstInSectionViews, true); - anyChanged |= handleAddedNewViews(sections, mTmpLastInSectionViews, false); - if (anyChanged) { - mRoundingChangedCallback.run(); - } - - mNotifLogger.onSectionCornersUpdated(sections, anyChanged); - } - - private boolean handleRemovedOldViews( - NotificationSection[] sections, - ExpandableView[] oldViews, - boolean first) { - if (mUseRoundnessSourceTypes) return false; - boolean anyChanged = false; - for (ExpandableView oldView : oldViews) { - if (oldView != null) { - boolean isStillPresent = false; - boolean adjacentSectionChanged = false; - for (NotificationSection section : sections) { - ExpandableView newView = - (first ? section.getFirstVisibleChild() - : section.getLastVisibleChild()); - if (newView == oldView) { - isStillPresent = true; - if (oldView.isFirstInSection() != isFirstInSection(oldView) - || oldView.isLastInSection() != isLastInSection(oldView)) { - adjacentSectionChanged = true; - } - break; - } - } - if (!isStillPresent || adjacentSectionChanged) { - anyChanged = true; - if (!oldView.isRemoved()) { - updateViewWithoutCallback(oldView, oldView.isShown()); - } - } - } - } - return anyChanged; - } - - private boolean handleAddedNewViews( - NotificationSection[] sections, - ExpandableView[] oldViews, - boolean first) { - if (mUseRoundnessSourceTypes) return false; - boolean anyChanged = false; - for (NotificationSection section : sections) { - ExpandableView newView = - (first ? section.getFirstVisibleChild() : section.getLastVisibleChild()); - if (newView != null) { - boolean wasAlreadyPresent = false; - for (ExpandableView oldView : oldViews) { - if (oldView == newView) { - wasAlreadyPresent = true; - break; - } - } - if (!wasAlreadyPresent) { - anyChanged = true; - updateViewWithoutCallback(newView, - newView.isShown() && !mAnimatedChildren.contains(newView)); - } - } - } - return anyChanged; - } - public void setAnimatedChildren(HashSet<ExpandableView> animatedChildren) { mAnimatedChildren = animatedChildren; } @@ -376,51 +135,7 @@ public class NotificationRoundnessManager implements Dumpable { return mAnimatedChildren.contains(view); } - public void setOnRoundingChangedCallback(Runnable roundingChangedCallback) { - mRoundingChangedCallback = roundingChangedCallback; - } - - public void setTrackingHeadsUp(ExpandableNotificationRow row) { - ExpandableNotificationRow previous = mTrackedHeadsUp; - mTrackedHeadsUp = row; - if (previous != null) { - updateView(previous, true /* animate */); - } - } - public void setShouldRoundPulsingViews(boolean shouldRoundPulsingViews) { mRoundForPulsingViews = shouldRoundPulsingViews; } - - private String dumpViews(ExpandableView[] views) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < views.length; i++) { - if (views[i] == null) continue; - - sb.append("\t") - .append("[").append(i).append("] ") - .append("isPinned=").append(views[i].isPinned()).append(" ") - .append("isFirstInSection=").append(views[i].isFirstInSection()).append(" ") - .append("isLastInSection=").append(views[i].isLastInSection()).append(" "); - - if (views[i] instanceof ExpandableNotificationRow) { - sb.append("entry="); - dumpEntry(((ExpandableNotificationRow) views[i]).getEntry(), sb); - } - - sb.append("\n"); - } - return sb.toString(); - } - - private void dumpEntry(NotificationEntry entry, StringBuilder sb) { - sb.append("NotificationEntry{key=").append(entry.getKey()).append(" "); - - if (entry.getSection() != null) { - sb.append(" section=") - .append(entry.getSection().getLabel()); - } - - sb.append("}"); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt index 57b6dbce5398..fd064eeab2ab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt @@ -52,8 +52,6 @@ class NotificationSectionsManager @Inject internal constructor( @SilentHeader private val silentHeaderController: SectionHeaderController ) : SectionProvider { - private val useRoundnessSourceTypes = true - private val configurationListener = object : ConfigurationController.ConfigurationListener { override fun onLocaleListChanged() { reinflateViews() @@ -193,35 +191,33 @@ class NotificationSectionsManager @Inject internal constructor( isSectionChanged || changed } - if (useRoundnessSourceTypes) { - val newFirstChildren = sections.mapNotNull { it.firstVisibleChild } - val newLastChildren = sections.mapNotNull { it.lastVisibleChild } + val newFirstChildren = sections.mapNotNull { it.firstVisibleChild } + val newLastChildren = sections.mapNotNull { it.lastVisibleChild } - // Update the roundness of Views that weren't already in the first/last position - newFirstChildren.forEach { firstChild -> - val wasFirstChild = oldFirstChildren.remove(firstChild) - if (!wasFirstChild) { - val notAnimatedChild = !notificationRoundnessManager.isAnimatedChild(firstChild) - val animated = firstChild.isShown && notAnimatedChild - firstChild.requestTopRoundness(1f, SECTION, animated) - } + // Update the roundness of Views that weren't already in the first/last position + newFirstChildren.forEach { firstChild -> + val wasFirstChild = oldFirstChildren.remove(firstChild) + if (!wasFirstChild) { + val notAnimatedChild = !notificationRoundnessManager.isAnimatedChild(firstChild) + val animated = firstChild.isShown && notAnimatedChild + firstChild.requestTopRoundness(1f, SECTION, animated) } - newLastChildren.forEach { lastChild -> - val wasLastChild = oldLastChildren.remove(lastChild) - if (!wasLastChild) { - val notAnimatedChild = !notificationRoundnessManager.isAnimatedChild(lastChild) - val animated = lastChild.isShown && notAnimatedChild - lastChild.requestBottomRoundness(1f, SECTION, animated) - } + } + newLastChildren.forEach { lastChild -> + val wasLastChild = oldLastChildren.remove(lastChild) + if (!wasLastChild) { + val notAnimatedChild = !notificationRoundnessManager.isAnimatedChild(lastChild) + val animated = lastChild.isShown && notAnimatedChild + lastChild.requestBottomRoundness(1f, SECTION, animated) } + } - // The Views left in the set are no longer in the first/last position - oldFirstChildren.forEach { noMoreFirstChild -> - noMoreFirstChild.requestTopRoundness(0f, SECTION) - } - oldLastChildren.forEach { noMoreLastChild -> - noMoreLastChild.requestBottomRoundness(0f, SECTION) - } + // The Views left in the set are no longer in the first/last position + oldFirstChildren.forEach { noMoreFirstChild -> + noMoreFirstChild.requestTopRoundness(0f, SECTION) + } + oldLastChildren.forEach { noMoreLastChild -> + noMoreLastChild.requestBottomRoundness(0f, SECTION) } if (DEBUG) { 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 abcb825c3e18..edff8770479a 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 @@ -200,7 +200,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private Set<Integer> mDebugTextUsedYPositions; private final boolean mDebugRemoveAnimation; private final boolean mSimplifiedAppearFraction; - private final boolean mUseRoundnessSourceTypes; private final boolean mSensitiveRevealAnimEndabled; private boolean mAnimatedInsets; @@ -625,7 +624,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES); mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION); mSimplifiedAppearFraction = featureFlags.isEnabled(Flags.SIMPLIFIED_APPEAR_FRACTION); - mUseRoundnessSourceTypes = true; mSensitiveRevealAnimEndabled = featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM); setAnimatedInsetsEnabled(featureFlags.isEnabled(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS)); mSectionsManager = Dependency.get(NotificationSectionsManager.class); @@ -3134,10 +3132,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mAnimateNextSectionBoundsChange = false; } mAmbientState.setLastVisibleBackgroundChild(lastChild); - if (!mUseRoundnessSourceTypes) { - // TODO: Refactor SectionManager and put the RoundnessManager there. - mController.getNotificationRoundnessManager().updateRoundedChildren(mSections); - } mAnimateBottomOnLayout = false; invalidate(); } @@ -3584,9 +3578,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM) protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) { - StackScrollAlgorithm stackScrollAlgorithm = new StackScrollAlgorithm(context, this); - stackScrollAlgorithm.useRoundnessSourceTypes(mUseRoundnessSourceTypes); - return stackScrollAlgorithm; + return new StackScrollAlgorithm(context, this); } /** 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 d96a2cd4f760..7b046d6c9256 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 @@ -192,7 +192,6 @@ public class NotificationStackScrollLayoutController { private int mBarState; private HeadsUpAppearanceController mHeadsUpAppearanceController; private final FeatureFlags mFeatureFlags; - private final boolean mUseRoundnessSourceTypes; private final NotificationTargetsHelper mNotificationTargetsHelper; private final SecureSettings mSecureSettings; private final NotificationDismissibilityProvider mDismissibilityProvider; @@ -591,36 +590,12 @@ public class NotificationStackScrollLayoutController { } @Override - public void onHeadsUpPinned(NotificationEntry entry) { - if (!mUseRoundnessSourceTypes) { - mNotificationRoundnessManager.updateView( - entry.getRow(), - /* animate = */ false); - } - } - - @Override - public void onHeadsUpUnPinned(NotificationEntry entry) { - if (!mUseRoundnessSourceTypes) { - ExpandableNotificationRow row = entry.getRow(); - // update the roundedness posted, because we might be animating away the - // headsup soon, so no need to set the roundedness to 0 and then back to 1. - row.post(() -> mNotificationRoundnessManager.updateView(row, - true /* animate */)); - } - } - - @Override public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { long numEntries = mHeadsUpManager.getAllEntries().count(); NotificationEntry topEntry = mHeadsUpManager.getTopEntry(); mView.setNumHeadsUp(numEntries); mView.setTopHeadsUpEntry(topEntry); generateHeadsUpAnimation(entry, isHeadsUp); - if (!mUseRoundnessSourceTypes) { - ExpandableNotificationRow row = entry.getRow(); - mNotificationRoundnessManager.updateView(row, true /* animate */); - } } }; @@ -720,7 +695,6 @@ public class NotificationStackScrollLayoutController { mShadeController = shadeController; mNotifIconAreaController = notifIconAreaController; mFeatureFlags = featureFlags; - mUseRoundnessSourceTypes = true; mNotificationTargetsHelper = notificationTargetsHelper; mSecureSettings = secureSettings; mDismissibilityProvider = dismissibilityProvider; @@ -788,11 +762,6 @@ public class NotificationStackScrollLayoutController { mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener); - if (!mUseRoundnessSourceTypes) { - mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate); - mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded); - } - mVisibilityLocationProviderDelegator.setDelegate(this::isInVisibleLocation); mTunerService.addTunable( @@ -958,7 +927,6 @@ public class NotificationStackScrollLayoutController { public void setTrackingHeadsUp(ExpandableNotificationRow expandableNotificationRow) { mView.setTrackingHeadsUp(expandableNotificationRow); - mNotificationRoundnessManager.setTrackingHeadsUp(expandableNotificationRow); } public void wakeUpFromPulse() { @@ -1776,9 +1744,6 @@ public class NotificationStackScrollLayoutController { @Override public void bindRow(ExpandableNotificationRow row) { row.setHeadsUpAnimatingAwayListener(animatingAway -> { - if (!mUseRoundnessSourceTypes) { - mNotificationRoundnessManager.updateView(row, false); - } NotificationEntry entry = row.getEntry(); mHeadsUpAppearanceController.updateHeader(entry); mHeadsUpAppearanceController.updateHeadsUpAndPulsingRoundness(entry); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java index 2d0a6cf8e5a7..993c3801cecd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java @@ -68,7 +68,6 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc private boolean mIsExpanded; private boolean mPulsing; private final NotificationRoundnessManager mNotificationRoundnessManager; - private final boolean mUseRoundnessSourceTypes; NotificationSwipeHelper( Resources resources, @@ -80,7 +79,6 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc NotificationRoundnessManager notificationRoundnessManager) { super(callback, resources, viewConfiguration, falsingManager, featureFlags); mNotificationRoundnessManager = notificationRoundnessManager; - mUseRoundnessSourceTypes = true; mMenuListener = menuListener; mCallback = callback; mFalsingCheck = () -> resetExposedMenuView(true /* animate */, true /* force */); @@ -322,8 +320,7 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc protected void prepareDismissAnimation(View view, Animator anim) { super.prepareDismissAnimation(view, anim); - if (mUseRoundnessSourceTypes - && view instanceof ExpandableNotificationRow + if (view instanceof ExpandableNotificationRow && mNotificationRoundnessManager.isClearAllInProgress()) { ExpandableNotificationRow row = (ExpandableNotificationRow) view; anim.addListener(new AnimatorListenerAdapter() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt index cd0c1b153816..02662f409a87 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt @@ -4,7 +4,6 @@ import androidx.core.view.children import androidx.core.view.isVisible import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.statusbar.notification.Roundable import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView @@ -20,7 +19,6 @@ class NotificationTargetsHelper constructor( featureFlags: FeatureFlags, ) { - private val useRoundnessSourceTypes = true /** * This method looks for views that can be rounded (and implement [Roundable]) during a @@ -48,10 +46,6 @@ constructor( if (notificationParent != null && childrenContainer != null) { // We are inside a notification group - if (!useRoundnessSourceTypes) { - return RoundableTargets(null, null, null) - } - val visibleGroupChildren = childrenContainer.attachedChildren.filter { it.isVisible } val indexOfParentSwipedView = visibleGroupChildren.indexOf(viewSwiped) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index 306047388381..92d767a419f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -34,7 +34,6 @@ import com.android.systemui.flags.Flags; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.NotificationShelf; -import com.android.systemui.statusbar.notification.LegacySourceType; import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -75,7 +74,6 @@ public class StackScrollAlgorithm { private float mQuickQsOffsetHeight; private float mSmallCornerRadius; private float mLargeCornerRadius; - private boolean mUseRoundnessSourceTypes; public StackScrollAlgorithm( Context context, @@ -836,12 +834,8 @@ public class StackScrollAlgorithm { row.isLastInSection() ? 1f : (mSmallCornerRadius / mLargeCornerRadius); final float bottomValue = computeCornerRoundnessForPinnedHun(mHostView.getHeight(), ambientState.getStackY(), getMaxAllowedChildHeight(row), originalCornerRadius); - if (mUseRoundnessSourceTypes) { - row.requestBottomRoundness(bottomValue, STACK_SCROLL_ALGO); - row.addOnDetachResetRoundness(STACK_SCROLL_ALGO); - } else { - row.requestBottomRoundness(bottomValue, LegacySourceType.OnScroll); - } + row.requestBottomRoundness(bottomValue, STACK_SCROLL_ALGO); + row.addOnDetachResetRoundness(STACK_SCROLL_ALGO); } @VisibleForTesting @@ -979,14 +973,6 @@ public class StackScrollAlgorithm { this.mIsExpanded = isExpanded; } - /** - * Enable the support for rounded corner based on the SourceType - * @param enabled true if is supported - */ - public void useRoundnessSourceTypes(boolean enabled) { - mUseRoundnessSourceTypes = enabled; - } - public static class StackScrollAlgorithmState { /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 0c4821c003dd..f15dcc310837 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -76,7 +76,6 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar private final DarkIconDispatcher mDarkIconDispatcher; private final ShadeViewController mShadeViewController; private final NotificationRoundnessManager mNotificationRoundnessManager; - private final boolean mUseRoundnessSourceTypes; private final Consumer<ExpandableNotificationRow> mSetTrackingHeadsUp = this::setTrackingHeadsUp; private final BiConsumer<Float, Float> mSetExpandedHeight = this::setAppearFraction; @@ -124,7 +123,6 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar super(headsUpStatusBarView); mNotificationIconAreaController = notificationIconAreaController; mNotificationRoundnessManager = notificationRoundnessManager; - mUseRoundnessSourceTypes = true; mHeadsUpManager = headsUpManager; // We may be mid-HUN-expansion when this controller is re-created (for example, if the user @@ -405,21 +403,19 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar * @param entry target notification */ public void updateHeadsUpAndPulsingRoundness(NotificationEntry entry) { - if (mUseRoundnessSourceTypes) { - ExpandableNotificationRow row = entry.getRow(); - boolean isTrackedChild = row == mTrackedChild; - if (row.isPinned() || row.isHeadsUpAnimatingAway() || isTrackedChild) { - float roundness = MathUtils.saturate(1f - mAppearFraction); - row.requestRoundness(roundness, roundness, HEADS_UP); + ExpandableNotificationRow row = entry.getRow(); + boolean isTrackedChild = row == mTrackedChild; + if (row.isPinned() || row.isHeadsUpAnimatingAway() || isTrackedChild) { + float roundness = MathUtils.saturate(1f - mAppearFraction); + row.requestRoundness(roundness, roundness, HEADS_UP); + } else { + row.requestRoundnessReset(HEADS_UP); + } + if (mNotificationRoundnessManager.shouldRoundNotificationPulsing()) { + if (row.showingPulsing()) { + row.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, PULSING); } else { - row.requestRoundnessReset(HEADS_UP); - } - if (mNotificationRoundnessManager.shouldRoundNotificationPulsing()) { - if (row.showingPulsing()) { - row.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, PULSING); - } else { - row.requestRoundnessReset(PULSING); - } + row.requestRoundnessReset(PULSING); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java index 534edb97bc5d..a058bf88e2e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java @@ -25,6 +25,7 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARE import android.annotation.ColorInt; import android.content.Context; import android.graphics.Rect; +import android.util.Log; import android.view.InsetsFlags; import android.view.ViewDebug; import android.view.WindowInsetsController.Appearance; @@ -35,13 +36,17 @@ import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.util.Compile; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Date; import javax.inject.Inject; @@ -51,10 +56,14 @@ import javax.inject.Inject; @SysUISingleton public class LightBarController implements BatteryController.BatteryStateChangeCallback, Dumpable { + private static final String TAG = "LightBarController"; + private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG); + private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f; private final SysuiDarkIconDispatcher mStatusBarIconController; private final BatteryController mBatteryController; + private final boolean mUseNewLightBarLogic; private BiometricUnlockController mBiometricUnlockController; private LightBarTransitionsController mNavigationBarController; @@ -67,13 +76,17 @@ public class LightBarController implements BatteryController.BatteryStateChangeC private final int mLightIconColor; /** - * Whether the navigation bar should be light factoring in already how much alpha the scrim has + * Whether the navigation bar should be light factoring in already how much alpha the scrim has. + * "Light" refers to the background color of the navigation bar, so when this is true, + * it's referring to a state where the navigation bar icons are tinted dark. */ private boolean mNavigationLight; /** - * Whether the flags indicate that a light status bar is requested. This doesn't factor in the - * scrim alpha yet. + * Whether the flags indicate that a light navigation bar is requested. + * "Light" refers to the background color of the navigation bar, so when this is true, + * it's referring to a state where the navigation bar icons would be tinted dark. + * This doesn't factor in the scrim alpha yet. */ private boolean mHasLightNavigationBar; @@ -82,22 +95,34 @@ public class LightBarController implements BatteryController.BatteryStateChangeC * {@link #mNavigationLight} {@code false}. */ private boolean mForceDarkForScrim; + /** + * {@code true} if {@link #mHasLightNavigationBar} should be ignored and forcefully make + * {@link #mNavigationLight} {@code true}. + */ + private boolean mForceLightForScrim; private boolean mQsCustomizing; + private boolean mQsExpanded; + private boolean mGlobalActionsVisible; private boolean mDirectReplying; private boolean mNavbarColorManagedByIme; private boolean mIsCustomizingForBackNav; + private String mLastSetScrimStateLog; + private String mLastNavigationBarAppearanceChangedLog; + @Inject public LightBarController( Context ctx, DarkIconDispatcher darkIconDispatcher, BatteryController batteryController, NavigationModeController navModeController, + FeatureFlags featureFlags, DumpManager dumpManager, DisplayTracker displayTracker) { + mUseNewLightBarLogic = featureFlags.isEnabled(Flags.NEW_LIGHT_BAR_LOGIC); mDarkIconColor = ctx.getColor(R.color.dark_mode_icon_color_single_tone); mLightIconColor = ctx.getColor(R.color.light_mode_icon_color_single_tone); mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher; @@ -159,9 +184,42 @@ public class LightBarController implements BatteryController.BatteryStateChangeC final boolean last = mNavigationLight; mHasLightNavigationBar = isLight(appearance, navigationBarMode, APPEARANCE_LIGHT_NAVIGATION_BARS); - mNavigationLight = mHasLightNavigationBar - && (mDirectReplying && mNavbarColorManagedByIme || !mForceDarkForScrim) - && !mQsCustomizing; + if (mUseNewLightBarLogic) { + final boolean ignoreScrimForce = mDirectReplying && mNavbarColorManagedByIme; + final boolean darkForScrim = mForceDarkForScrim && !ignoreScrimForce; + final boolean lightForScrim = mForceLightForScrim && !ignoreScrimForce; + final boolean darkForQs = mQsCustomizing || mQsExpanded || mGlobalActionsVisible; + mNavigationLight = + ((mHasLightNavigationBar && !darkForScrim) || lightForScrim) && !darkForQs; + mLastNavigationBarAppearanceChangedLog = "onNavigationBarAppearanceChanged()" + + " appearance=" + appearance + + " nbModeChanged=" + nbModeChanged + + " navigationBarMode=" + navigationBarMode + + " navbarColorManagedByIme=" + navbarColorManagedByIme + + " mHasLightNavigationBar=" + mHasLightNavigationBar + + " ignoreScrimForce=" + ignoreScrimForce + + " darkForScrim=" + darkForScrim + + " lightForScrim=" + lightForScrim + + " darkForQs=" + darkForQs + + " mNavigationLight=" + mNavigationLight + + " last=" + last + + " timestamp=" + new Date(); + if (DEBUG) Log.d(TAG, mLastNavigationBarAppearanceChangedLog); + } else { + mNavigationLight = mHasLightNavigationBar + && (mDirectReplying && mNavbarColorManagedByIme || !mForceDarkForScrim) + && !mQsCustomizing; + mLastNavigationBarAppearanceChangedLog = "onNavigationBarAppearanceChanged()" + + " appearance=" + appearance + + " nbModeChanged=" + nbModeChanged + + " navigationBarMode=" + navigationBarMode + + " navbarColorManagedByIme=" + navbarColorManagedByIme + + " mHasLightNavigationBar=" + mHasLightNavigationBar + + " mNavigationLight=" + mNavigationLight + + " last=" + last + + " timestamp=" + new Date(); + if (DEBUG) Log.d(TAG, mLastNavigationBarAppearanceChangedLog); + } if (mNavigationLight != last) { updateNavigation(); } @@ -188,6 +246,20 @@ public class LightBarController implements BatteryController.BatteryStateChangeC reevaluate(); } + /** Set if Quick Settings is fully expanded, which affects notification scrim visibility */ + public void setQsExpanded(boolean expanded) { + if (mQsExpanded == expanded) return; + mQsExpanded = expanded; + reevaluate(); + } + + /** Set if Global Actions dialog is visible, which requires dark mode (light buttons) */ + public void setGlobalActionsVisible(boolean visible) { + if (mGlobalActionsVisible == visible) return; + mGlobalActionsVisible = visible; + reevaluate(); + } + /** * Controls the light status bar temporarily for back navigation. * @param appearance the custmoized appearance. @@ -225,16 +297,52 @@ public class LightBarController implements BatteryController.BatteryStateChangeC public void setScrimState(ScrimState scrimState, float scrimBehindAlpha, GradientColors scrimInFrontColor) { - boolean forceDarkForScrimLast = mForceDarkForScrim; - // For BOUNCER/BOUNCER_SCRIMMED cases, we assume that alpha is always below threshold. - // This enables IMEs to control the navigation bar color. - // For other cases, scrim should be able to veto the light navigation bar. - mForceDarkForScrim = scrimState != ScrimState.BOUNCER - && scrimState != ScrimState.BOUNCER_SCRIMMED - && scrimBehindAlpha >= NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD - && !scrimInFrontColor.supportsDarkText(); - if (mHasLightNavigationBar && (mForceDarkForScrim != forceDarkForScrimLast)) { - reevaluate(); + if (mUseNewLightBarLogic) { + boolean forceDarkForScrimLast = mForceDarkForScrim; + boolean forceLightForScrimLast = mForceLightForScrim; + final boolean forceForScrim = + scrimBehindAlpha >= NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD; + final boolean scrimColorIsLight = scrimInFrontColor.supportsDarkText(); + + mForceDarkForScrim = forceForScrim && !scrimColorIsLight; + mForceLightForScrim = forceForScrim && scrimColorIsLight; + if (mHasLightNavigationBar) { + if (mForceDarkForScrim != forceDarkForScrimLast) reevaluate(); + } else { + if (mForceLightForScrim != forceLightForScrimLast) reevaluate(); + } + mLastSetScrimStateLog = "setScrimState()" + + " scrimState=" + scrimState + + " scrimBehindAlpha=" + scrimBehindAlpha + + " scrimInFrontColor=" + scrimInFrontColor + + " forceForScrim=" + forceForScrim + + " scrimColorIsLight=" + scrimColorIsLight + + " mHasLightNavigationBar=" + mHasLightNavigationBar + + " mForceDarkForScrim=" + mForceDarkForScrim + + " mForceLightForScrim=" + mForceLightForScrim + + " timestamp=" + new Date(); + if (DEBUG) Log.d(TAG, mLastSetScrimStateLog); + } else { + boolean forceDarkForScrimLast = mForceDarkForScrim; + // For BOUNCER/BOUNCER_SCRIMMED cases, we assume that alpha is always below threshold. + // This enables IMEs to control the navigation bar color. + // For other cases, scrim should be able to veto the light navigation bar. + // NOTE: this was also wrong for S and has been removed in the new logic. + mForceDarkForScrim = scrimState != ScrimState.BOUNCER + && scrimState != ScrimState.BOUNCER_SCRIMMED + && scrimBehindAlpha >= NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD + && !scrimInFrontColor.supportsDarkText(); + if (mHasLightNavigationBar && (mForceDarkForScrim != forceDarkForScrimLast)) { + reevaluate(); + } + mLastSetScrimStateLog = "setScrimState()" + + " scrimState=" + scrimState + + " scrimBehindAlpha=" + scrimBehindAlpha + + " scrimInFrontColor=" + scrimInFrontColor + + " mHasLightNavigationBar=" + mHasLightNavigationBar + + " mForceDarkForScrim=" + mForceDarkForScrim + + " timestamp=" + new Date(); + if (DEBUG) Log.d(TAG, mLastSetScrimStateLog); } } @@ -309,16 +417,24 @@ public class LightBarController implements BatteryController.BatteryStateChangeC pw.print(mAppearanceRegions[i].toString()); pw.print(" isLight="); pw.println(isLight); } - pw.print(" mNavigationLight="); pw.print(mNavigationLight); + pw.print(" mNavigationLight="); pw.println(mNavigationLight); pw.print(" mHasLightNavigationBar="); pw.println(mHasLightNavigationBar); - + pw.println(); pw.print(" mStatusBarMode="); pw.print(mStatusBarMode); pw.print(" mNavigationBarMode="); pw.println(mNavigationBarMode); - - pw.print(" mForceDarkForScrim="); pw.print(mForceDarkForScrim); - pw.print(" mQsCustomizing="); pw.print(mQsCustomizing); + pw.println(); + pw.print(" mForceDarkForScrim="); pw.println(mForceDarkForScrim); + pw.print(" mForceLightForScrim="); pw.println(mForceLightForScrim); + pw.println(); + pw.print(" mQsCustomizing="); pw.println(mQsCustomizing); + pw.print(" mQsExpanded="); pw.println(mQsExpanded); + pw.print(" mGlobalActionsVisible="); pw.println(mGlobalActionsVisible); pw.print(" mDirectReplying="); pw.println(mDirectReplying); pw.print(" mNavbarColorManagedByIme="); pw.println(mNavbarColorManagedByIme); + pw.println(); + pw.println(" Recent Calculation Logs:"); + pw.print(" "); pw.println(mLastSetScrimStateLog); + pw.print(" "); pw.println(mLastNavigationBarAppearanceChangedLog); pw.println(); @@ -344,6 +460,7 @@ public class LightBarController implements BatteryController.BatteryStateChangeC private final DarkIconDispatcher mDarkIconDispatcher; private final BatteryController mBatteryController; private final NavigationModeController mNavModeController; + private final FeatureFlags mFeatureFlags; private final DumpManager mDumpManager; private final DisplayTracker mDisplayTracker; @@ -352,12 +469,14 @@ public class LightBarController implements BatteryController.BatteryStateChangeC DarkIconDispatcher darkIconDispatcher, BatteryController batteryController, NavigationModeController navModeController, + FeatureFlags featureFlags, DumpManager dumpManager, DisplayTracker displayTracker) { mDarkIconDispatcher = darkIconDispatcher; mBatteryController = batteryController; mNavModeController = navModeController; + mFeatureFlags = featureFlags; mDumpManager = dumpManager; mDisplayTracker = displayTracker; } @@ -365,7 +484,7 @@ public class LightBarController implements BatteryController.BatteryStateChangeC /** Create an {@link LightBarController} */ public LightBarController create(Context context) { return new LightBarController(context, mDarkIconDispatcher, mBatteryController, - mNavModeController, mDumpManager, mDisplayTracker); + mNavModeController, mFeatureFlags, mDumpManager, mDisplayTracker); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 006a029de8e0..b9a12e28b8ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -306,7 +306,7 @@ public class NotificationIconContainer extends ViewGroup { public void applyIconStates() { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); - ViewState childState = mIconStates.get(child); + IconState childState = mIconStates.get(child); if (childState != null) { childState.applyToView(child); } @@ -339,6 +339,7 @@ public class NotificationIconContainer extends ViewGroup { } } if (child instanceof StatusBarIconView) { + ((StatusBarIconView) child).updateIconDimens(); ((StatusBarIconView) child).setDozing(mDozing, false, 0); } } @@ -447,9 +448,14 @@ public class NotificationIconContainer extends ViewGroup { @VisibleForTesting boolean isOverflowing(boolean isLastChild, float translationX, float layoutEnd, float iconSize) { - // Layout end, as used here, does not include padding end. - final float overflowX = isLastChild ? layoutEnd : layoutEnd - iconSize; - return translationX >= overflowX; + if (isLastChild) { + return translationX + iconSize > layoutEnd; + } else { + // If the child is not the last child, we need to ensure that we have room for the next + // icon and the dot. The dot could be as large as an icon, so verify that we have room + // for 2 icons. + return translationX + iconSize * 2f > layoutEnd; + } } /** @@ -489,10 +495,7 @@ public class NotificationIconContainer extends ViewGroup { // First icon to overflow. if (firstOverflowIndex == -1 && isOverflowing) { firstOverflowIndex = i; - mVisualOverflowStart = layoutEnd - mIconSize; - if (forceOverflow || mIsStaticLayout) { - mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart); - } + mVisualOverflowStart = translationX; } final float drawingScale = mOnLockScreen && view instanceof StatusBarIconView ? ((StatusBarIconView) view).getIconScaleIncreased() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 51c56a021825..fdb772bc6c13 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -42,6 +42,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.internal.graphics.ColorUtils; +import com.android.internal.util.ContrastColorUtil; import com.android.internal.util.function.TriConsumer; import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.keyguard.KeyguardUpdateMonitor; @@ -249,6 +250,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private final TriConsumer<ScrimState, Float, GradientColors> mScrimStateListener; private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator; private final FeatureFlags mFeatureFlags; + private final boolean mUseNewLightBarLogic; private Consumer<Integer> mScrimVisibleListener; private boolean mBlankScreen; private boolean mScreenBlankingCallbackCalled; @@ -306,6 +308,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mScrimStateListener = lightBarController::setScrimState; mLargeScreenShadeInterpolator = largeScreenShadeInterpolator; mFeatureFlags = featureFlags; + mUseNewLightBarLogic = featureFlags.isEnabled(Flags.NEW_LIGHT_BAR_LOGIC); mDefaultScrimAlpha = BUSY_SCRIM_ALPHA; mKeyguardStateController = keyguardStateController; @@ -1159,7 +1162,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump if (mClipsQsScrim && mQsBottomVisible) { alpha = mNotificationsAlpha; } - mScrimStateListener.accept(mState, alpha, mScrimInFront.getColors()); + if (mUseNewLightBarLogic) { + mScrimStateListener.accept(mState, alpha, mColors); + } else { + // NOTE: This wasn't wrong, but it implied that each scrim might have different colors, + // when in fact they all share the same GradientColors instance, which we own. + mScrimStateListener.accept(mState, alpha, mScrimInFront.getColors()); + } } private void dispatchScrimsVisible() { @@ -1487,8 +1496,15 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump int accent = Utils.getColorAccent(mScrimBehind.getContext()).getDefaultColor(); mColors.setMainColor(background); mColors.setSecondaryColor(accent); - mColors.setSupportsDarkText( - ColorUtils.calculateContrast(mColors.getMainColor(), Color.WHITE) > 4.5); + if (mUseNewLightBarLogic) { + final boolean isBackgroundLight = !ContrastColorUtil.isColorDark(background); + mColors.setSupportsDarkText(isBackgroundLight); + } else { + // NOTE: This was totally backward, but LightBarController was flipping it back. + // There may be other consumers of this which would struggle though + mColors.setSupportsDarkText( + ColorUtils.calculateContrast(mColors.getMainColor(), Color.WHITE) > 4.5); + } mNeedsDrawableColorUpdate = true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index a8a834f1e8f4..678873c0165c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -203,8 +203,7 @@ public interface StatusBarIconController { @Override protected LayoutParams onCreateLayoutParams() { - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); + LinearLayout.LayoutParams lp = super.onCreateLayoutParams(); lp.setMargins(mIconHPadding, 0, mIconHPadding, 0); return lp; } @@ -370,7 +369,7 @@ public interface StatusBarIconController { private final MobileIconsViewModel mMobileIconsViewModel; protected final Context mContext; - protected final int mIconSize; + protected int mIconSize; // Whether or not these icons show up in dumpsys protected boolean mShouldLog = false; private StatusBarIconController mController; @@ -395,10 +394,10 @@ public interface StatusBarIconController { mStatusBarPipelineFlags = statusBarPipelineFlags; mMobileContextProvider = mobileContextProvider; mContext = group.getContext(); - mIconSize = mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_icon_size); mLocation = location; + reloadDimens(); + if (statusBarPipelineFlags.runNewMobileIconsBackend()) { // This starts the flow for the new pipeline, and will notify us of changes if // {@link StatusBarPipelineFlags#useNewMobileIcons} is also true. @@ -609,13 +608,9 @@ public interface StatusBarIconController { mGroup.removeAllViews(); } - protected void onDensityOrFontScaleChanged() { - for (int i = 0; i < mGroup.getChildCount(); i++) { - View child = mGroup.getChildAt(i); - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); - child.setLayoutParams(lp); - } + protected void reloadDimens() { + mIconSize = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_icon_size); } private void setHeightAndCenter(ImageView imageView, int height) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index 3a184239ac43..80d5651a65dc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -109,6 +109,7 @@ public class StatusBarIconControllerImpl implements Tunable, } group.setController(this); + group.reloadDimens(); mIconGroups.add(group); List<Slot> allSlots = mStatusBarIconList.getSlots(); for (int i = 0; i < allSlots.size(); i++) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java index 26c17674ab10..ddbfd43f9bf6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java @@ -22,6 +22,8 @@ import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; @@ -72,13 +74,16 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { // Any ignored icon will never be added as a child private ArrayList<String> mIgnoredSlots = new ArrayList<>(); + private Configuration mConfiguration; + public StatusIconContainer(Context context) { this(context, null); } public StatusIconContainer(Context context, AttributeSet attrs) { super(context, attrs); - initDimens(); + mConfiguration = new Configuration(context.getResources().getConfiguration()); + reloadDimens(); setWillNotDraw(!DEBUG_OVERFLOW); } @@ -95,7 +100,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { return mShouldRestrictIcons; } - private void initDimens() { + private void reloadDimens() { // This is the same value that StatusBarIconView uses mIconDotFrameWidth = getResources().getDimensionPixelSize( com.android.internal.R.dimen.status_bar_icon_size); @@ -211,6 +216,16 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { child.setTag(R.id.status_bar_view_state_tag, null); } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + final int configDiff = newConfig.diff(mConfiguration); + mConfiguration.setTo(newConfig); + if ((configDiff & (ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_FONT_SCALE)) != 0) { + reloadDimens(); + } + } + /** * Add a name of an icon slot to be ignored. It will not show up nor be measured * @param slotName name of the icon as it exists in @@ -348,13 +363,17 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { int totalVisible = mLayoutStates.size(); int maxVisible = totalVisible <= MAX_ICONS ? MAX_ICONS : MAX_ICONS - 1; - mUnderflowStart = 0; + // Init mUnderflowStart value with the offset to let the dot be placed next to battery icon. + // It to prevent if the underflow happens at rightest(totalVisible - 1) child then break the + // for loop with mUnderflowStart staying 0(initial value), causing the dot be placed at the + // leftest side. + mUnderflowStart = (int) Math.max(contentStart, width - getPaddingEnd() - mUnderflowWidth); int visible = 0; int firstUnderflowIndex = -1; for (int i = totalVisible - 1; i >= 0; i--) { StatusIconState state = mLayoutStates.get(i); // Allow room for underflow if we found we need it in onMeasure - if (mNeedsUnderflow && (state.getXTranslation() < (contentStart + mUnderflowWidth)) + if ((mNeedsUnderflow && (state.getXTranslation() < (contentStart + mUnderflowWidth))) || (mShouldRestrictIcons && (visible >= maxVisible))) { firstUnderflowIndex = i; break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java index 98cde2a049da..abedd3220c93 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java @@ -28,6 +28,11 @@ public interface CastController extends CallbackController<Callback>, Dumpable { void startCasting(CastDevice device); void stopCasting(CastDevice device); + /** + * @return whether we have a connected device. + */ + boolean hasConnectedCastDevice(); + public interface Callback { void onCastDevicesChanged(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java index 7290a1a82070..f7b601b9d284 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java @@ -217,6 +217,12 @@ public class CastControllerImpl implements CastController { } } + @Override + public boolean hasConnectedCastDevice() { + return getCastDevices().stream().anyMatch( + castDevice -> castDevice.state == CastDevice.STATE_CONNECTED); + } + private void setProjection(MediaProjectionInfo projection, boolean started) { boolean changed = false; final MediaProjectionInfo oldProjection = mProjection; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt index aa502bc48149..f61f3b7a70f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt @@ -34,6 +34,8 @@ constructor(@DeviceStateAutoRotationLog private val logBuffer: LogBuffer, contex private val halfFoldedStates = context.resources.getIntArray(R.array.config_halfFoldedDeviceStates) private val unfoldedStates = context.resources.getIntArray(R.array.config_openDeviceStates) + private val rearDisplayStates = + context.resources.getIntArray(R.array.config_rearDisplayDeviceStates) fun logListeningChange(listening: Boolean) { logBuffer.log(TAG, VERBOSE, { bool1 = listening }, { "setListening: $bool1" }) @@ -122,6 +124,7 @@ constructor(@DeviceStateAutoRotationLog private val logBuffer: LogBuffer, contex in foldedStates -> "Folded" in unfoldedStates -> "Unfolded" in halfFoldedStates -> "Half-Folded" + in rearDisplayStates -> "Rear display" -1 -> "Uninitialized" else -> "Unknown" } diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt index 412b3150489c..27aaa6828036 100644 --- a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt +++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt @@ -22,7 +22,6 @@ import android.content.Context import android.hardware.BatteryState import android.hardware.input.InputManager import android.hardware.input.InputSettings -import android.os.Build import android.os.Handler import android.util.ArrayMap import android.util.Log @@ -35,6 +34,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.log.DebugLogger.debugLog import com.android.systemui.shared.hardware.hasInputDevice import com.android.systemui.shared.hardware.isInternalStylusSource import java.util.concurrent.CopyOnWriteArrayList @@ -81,7 +81,7 @@ constructor( fun startListener() { handler.post { if (hasStarted) return@post - logDebug { "Listener has started." } + debugLog { "Listener has started." } hasStarted = true isInUsiSession = @@ -109,7 +109,7 @@ constructor( val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return - logDebug { + debugLog { "Stylus InputDevice added: $deviceId ${device.name}, " + "External: ${device.isExternal}" } @@ -134,7 +134,7 @@ constructor( val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return - logDebug { "Stylus InputDevice changed: $deviceId ${device.name}" } + debugLog { "Stylus InputDevice changed: $deviceId ${device.name}" } val currAddress: String? = device.bluetoothAddress val prevAddress: String? = inputDeviceAddressMap[deviceId] @@ -155,7 +155,7 @@ constructor( if (!hasStarted) return if (!inputDeviceAddressMap.contains(deviceId)) return - logDebug { "Stylus InputDevice removed: $deviceId" } + debugLog { "Stylus InputDevice removed: $deviceId" } unregisterBatteryListener(deviceId) @@ -180,7 +180,7 @@ constructor( val isCharging = String(value) == "true" - logDebug { + debugLog { "Charging state metadata changed for device $inputDeviceId " + "${device.address}: $isCharging" } @@ -199,7 +199,7 @@ constructor( handler.post { if (!hasStarted) return@post - logDebug { + debugLog { "Battery state changed for $deviceId. " + "batteryState present: ${batteryState.isPresent}, " + "capacity: ${batteryState.capacity}" @@ -247,7 +247,7 @@ constructor( if (!featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)) return if (InputSettings.isStylusEverUsed(context)) return - logDebug { "Stylus used for the first time." } + debugLog { "Stylus used for the first time." } InputSettings.setStylusEverUsed(context, true) executeStylusCallbacks { cb -> cb.onStylusFirstUsed() } } @@ -264,7 +264,7 @@ constructor( val hasBtConnection = if (inputDeviceBtSessionIdMap.isEmpty()) 0 else 1 if (batteryStateValid && usiSessionId == null) { - logDebug { "USI battery newly present, entering new USI session: $deviceId" } + debugLog { "USI battery newly present, entering new USI session: $deviceId" } usiSessionId = instanceIdSequence.newInstanceId() uiEventLogger.logWithInstanceIdAndPosition( StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_FIRST_DETECTED, @@ -274,7 +274,7 @@ constructor( hasBtConnection, ) } else if (!batteryStateValid && usiSessionId != null) { - logDebug { "USI battery newly absent, exiting USI session: $deviceId" } + debugLog { "USI battery newly absent, exiting USI session: $deviceId" } uiEventLogger.logWithInstanceIdAndPosition( StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_REMOVED, 0, @@ -291,7 +291,7 @@ constructor( btAddress: String, btConnected: Boolean ) { - logDebug { + debugLog { "Bluetooth stylus ${if (btConnected) "connected" else "disconnected"}:" + " $deviceId $btAddress" } @@ -386,9 +386,3 @@ constructor( val TAG = StylusManager::class.simpleName.orEmpty() } } - -private inline fun logDebug(message: () -> String) { - if (Build.IS_DEBUGGABLE) { - Log.d(StylusManager.TAG, message()) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt index 21b0efadb8d5..6eddd9eb7ad2 100644 --- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt +++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt @@ -26,7 +26,6 @@ import android.content.Intent import android.content.IntentFilter import android.hardware.BatteryState import android.hardware.input.InputManager -import android.os.Build import android.os.Bundle import android.os.Handler import android.os.UserHandle @@ -40,6 +39,7 @@ import com.android.internal.logging.UiEventLogger import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.log.DebugLogger.debugLog import com.android.systemui.shared.hardware.hasInputDevice import com.android.systemui.shared.hardware.isAnyStylusSource import com.android.systemui.util.NotificationChannels @@ -110,7 +110,7 @@ constructor( inputDeviceId = deviceId batteryCapacity = batteryState.capacity - logDebug { + debugLog { "Updating notification battery state to $batteryCapacity " + "for InputDevice $deviceId." } @@ -130,14 +130,14 @@ constructor( handler.post updateSuppressed@{ if (suppressed == suppress) return@updateSuppressed - logDebug { "Updating notification suppression to $suppress." } + debugLog { "Updating notification suppression to $suppress." } suppressed = suppress refresh() } } private fun hideNotification() { - logDebug { "Cancelling USI low battery notification." } + debugLog { "Cancelling USI low battery notification." } instanceId = null notificationManager.cancel(USI_NOTIFICATION_ID) } @@ -160,7 +160,7 @@ constructor( .setAutoCancel(true) .build() - logDebug { "Show or update USI low battery notification at $batteryCapacity." } + debugLog { "Show or update USI low battery notification at $batteryCapacity." } logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_SHOWN) notificationManager.notify(USI_NOTIFICATION_ID, notification) } @@ -188,12 +188,12 @@ constructor( override fun onReceive(context: Context, intent: Intent) { when (intent.action) { ACTION_DISMISSED_LOW_BATTERY -> { - logDebug { "USI low battery notification dismissed." } + debugLog { "USI low battery notification dismissed." } logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_DISMISSED) updateSuppression(true) } ACTION_CLICKED_LOW_BATTERY -> { - logDebug { "USI low battery notification clicked." } + debugLog { "USI low battery notification clicked." } logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_CLICKED) updateSuppression(true) if (inputDeviceId == null) return @@ -263,9 +263,3 @@ constructor( @VisibleForTesting const val KEY_SETTINGS_FRAGMENT_ARGS = ":settings:show_fragment_args" } } - -private inline fun logDebug(message: () -> String) { - if (Build.IS_DEBUGGABLE) { - Log.d(StylusUsiPowerUI.TAG, message()) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt index 6026d2cb6858..f0fa779ba0cf 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt +++ b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.systemui.theme import android.util.Pair @@ -24,68 +25,73 @@ class DynamicColors { @JvmField val ALL_DYNAMIC_COLORS_MAPPED: List<Pair<String, DynamicColor>> = arrayListOf( - Pair.create("primary_container", MDC.primaryContainer), - Pair.create("on_primary_container", MDC.onPrimaryContainer), - Pair.create("primary", MDC.primary), - Pair.create("on_primary", MDC.onPrimary), - Pair.create("secondary_container", MDC.secondaryContainer), - Pair.create("on_secondary_container", MDC.onSecondaryContainer), - Pair.create("secondary", MDC.secondary), - Pair.create("on_secondary", MDC.onSecondary), - Pair.create("tertiary_container", MDC.tertiaryContainer), - Pair.create("on_tertiary_container", MDC.onTertiaryContainer), - Pair.create("tertiary", MDC.tertiary), - Pair.create("on_tertiary", MDC.onTertiary), - Pair.create("background", MDC.background), - Pair.create("on_background", MDC.onBackground), - Pair.create("surface", MDC.surface), - Pair.create("on_surface", MDC.onSurface), - Pair.create("surface_container_low", MDC.surfaceContainerLow), - Pair.create("surface_container_lowest", MDC.surfaceContainerLowest), - Pair.create("surface_container", MDC.surfaceContainer), - Pair.create("surface_container_high", MDC.surfaceContainerHigh), - Pair.create("surface_container_highest", MDC.surfaceContainerHighest), - Pair.create("surface_bright", MDC.surfaceBright), - Pair.create("surface_dim", MDC.surfaceDim), - Pair.create("surface_variant", MDC.surfaceVariant), - Pair.create("on_surface_variant", MDC.onSurfaceVariant), - Pair.create("outline", MDC.outline), - Pair.create("outline_variant", MDC.outlineVariant), - Pair.create("error", MDC.error), - Pair.create("on_error", MDC.onError), - Pair.create("error_container", MDC.errorContainer), - Pair.create("on_error_container", MDC.onErrorContainer), - Pair.create("primary_fixed", MDC.primaryFixed), - Pair.create("primary_fixed_dim", MDC.primaryFixedDim), - Pair.create("on_primary_fixed", MDC.onPrimaryFixed), - Pair.create("on_primary_fixed_variant", MDC.onPrimaryFixedVariant), - Pair.create("secondary_fixed", MDC.secondaryFixed), - Pair.create("secondary_fixed_dim", MDC.secondaryFixedDim), - Pair.create("on_secondary_fixed", MDC.onSecondaryFixed), - Pair.create("on_secondary_fixed_variant", MDC.onSecondaryFixedVariant), - Pair.create("tertiary_fixed", MDC.tertiaryFixed), - Pair.create("tertiary_fixed_dim", MDC.tertiaryFixedDim), - Pair.create("on_tertiary_fixed", MDC.onTertiaryFixed), - Pair.create("on_tertiary_fixed_variant", MDC.onTertiaryFixedVariant), - Pair.create("control_activated", MDC.controlActivated), - Pair.create("control_normal", MDC.controlNormal), - Pair.create("control_highlight", MDC.controlHighlight), - Pair.create("text_primary_inverse", MDC.textPrimaryInverse), + Pair.create("primary_container", MDC.primaryContainer()), + Pair.create("on_primary_container", MDC.onPrimaryContainer()), + Pair.create("primary", MDC.primary()), + Pair.create("on_primary", MDC.onPrimary()), + Pair.create("secondary_container", MDC.secondaryContainer()), + Pair.create("on_secondary_container", MDC.onSecondaryContainer()), + Pair.create("secondary", MDC.secondary()), + Pair.create("on_secondary", MDC.onSecondary()), + Pair.create("tertiary_container", MDC.tertiaryContainer()), + Pair.create("on_tertiary_container", MDC.onTertiaryContainer()), + Pair.create("tertiary", MDC.tertiary()), + Pair.create("on_tertiary", MDC.onTertiary()), + Pair.create("background", MDC.background()), + Pair.create("on_background", MDC.onBackground()), + Pair.create("surface", MDC.surface()), + Pair.create("on_surface", MDC.onSurface()), + Pair.create("surface_container_low", MDC.surfaceContainerLow()), + Pair.create("surface_container_lowest", MDC.surfaceContainerLowest()), + Pair.create("surface_container", MDC.surfaceContainer()), + Pair.create("surface_container_high", MDC.surfaceContainerHigh()), + Pair.create("surface_container_highest", MDC.surfaceContainerHighest()), + Pair.create("surface_bright", MDC.surfaceBright()), + Pair.create("surface_dim", MDC.surfaceDim()), + Pair.create("surface_variant", MDC.surfaceVariant()), + Pair.create("on_surface_variant", MDC.onSurfaceVariant()), + Pair.create("outline", MDC.outline()), + Pair.create("error", MDC.error()), + Pair.create("on_error", MDC.onError()), + Pair.create("error_container", MDC.errorContainer()), + Pair.create("on_error_container", MDC.onErrorContainer()), + Pair.create("primary_fixed", MDC.primaryFixed()), + Pair.create("primary_fixed_dim", MDC.primaryFixedDim()), + Pair.create("on_primary_fixed", MDC.onPrimaryFixed()), + Pair.create("on_primary_fixed_variant", MDC.onPrimaryFixedVariant()), + Pair.create("secondary_fixed", MDC.secondaryFixed()), + Pair.create("secondary_fixed_dim", MDC.secondaryFixedDim()), + Pair.create("on_secondary_fixed", MDC.onSecondaryFixed()), + Pair.create("on_secondary_fixed_variant", MDC.onSecondaryFixedVariant()), + Pair.create("tertiary_fixed", MDC.tertiaryFixed()), + Pair.create("tertiary_fixed_dim", MDC.tertiaryFixedDim()), + Pair.create("on_tertiary_fixed", MDC.onTertiaryFixed()), + Pair.create("on_tertiary_fixed_variant", MDC.onTertiaryFixedVariant()), + Pair.create("control_activated", MDC.controlActivated()), + Pair.create("control_normal", MDC.controlNormal()), + Pair.create("control_highlight", MDC.controlHighlight()), + Pair.create("text_primary_inverse", MDC.textPrimaryInverse()), Pair.create( "text_secondary_and_tertiary_inverse", - MDC.textSecondaryAndTertiaryInverse + MDC.textSecondaryAndTertiaryInverse() + ), + Pair.create( + "text_primary_inverse_disable_only", + MDC.textPrimaryInverseDisableOnly() ), - Pair.create("text_primary_inverse_disable_only", MDC.textPrimaryInverseDisableOnly), Pair.create( "text_secondary_and_tertiary_inverse_disabled", - MDC.textSecondaryAndTertiaryInverseDisabled + MDC.textSecondaryAndTertiaryInverseDisabled() ), - Pair.create("text_hint_inverse", MDC.textHintInverse), - Pair.create("palette_key_color_primary", MDC.primaryPaletteKeyColor), - Pair.create("palette_key_color_secondary", MDC.secondaryPaletteKeyColor), - Pair.create("palette_key_color_tertiary", MDC.tertiaryPaletteKeyColor), - Pair.create("palette_key_color_neutral", MDC.neutralPaletteKeyColor), - Pair.create("palette_key_color_neutral_variant", MDC.neutralVariantPaletteKeyColor) + Pair.create("text_hint_inverse", MDC.textHintInverse()), + Pair.create("palette_key_color_primary", MDC.primaryPaletteKeyColor()), + Pair.create("palette_key_color_secondary", MDC.secondaryPaletteKeyColor()), + Pair.create("palette_key_color_tertiary", MDC.tertiaryPaletteKeyColor()), + Pair.create("palette_key_color_neutral", MDC.neutralPaletteKeyColor()), + Pair.create( + "palette_key_color_neutral_variant", + MDC.neutralVariantPaletteKeyColor() + ) ) } } diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index faae8df44ed9..43d15bcc22b3 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.systemui.theme; import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8; @@ -659,9 +660,9 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { && res.getColor(android.R.color.system_neutral2_500, theme) == mColorScheme.getNeutral2().getS500() && res.getColor(android.R.color.system_primary_container_dark, theme) - == MaterialDynamicColors.primaryContainer.getArgb(mDynamicSchemeDark) + == MaterialDynamicColors.primaryContainer().getArgb(mDynamicSchemeDark) && res.getColor(android.R.color.system_primary_container_light, theme) - == MaterialDynamicColors.primaryContainer.getArgb(mDynamicSchemeLight))) { + == MaterialDynamicColors.primaryContainer().getArgb(mDynamicSchemeLight))) { return false; } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index b3e7cb0c77eb..e60f9b65dc1c 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -33,6 +33,7 @@ import android.content.res.Configuration; import android.graphics.Rect; import android.inputmethodservice.InputMethodService; import android.os.IBinder; +import android.view.Display; import android.view.KeyEvent; import androidx.annotation.NonNull; @@ -347,12 +348,16 @@ public final class WMShell implements void initDesktopMode(DesktopMode desktopMode) { desktopMode.addVisibleTasksListener( new DesktopModeTaskRepository.VisibleTasksListener() { - @Override - public void onVisibilityChanged(boolean hasFreeformTasks) { - mSysUiState.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, hasFreeformTasks) - .commitUpdate(mDisplayTracker.getDefaultDisplayId()); - } - }, mSysUiMainExecutor); + @Override + public void onVisibilityChanged(int displayId, boolean hasFreeformTasks) { + if (displayId == Display.DEFAULT_DISPLAY) { + mSysUiState.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, + hasFreeformTasks) + .commitUpdate(mDisplayTracker.getDefaultDisplayId()); + } + // TODO(b/278084491): update sysui state for changes on other displays + } + }, mSysUiMainExecutor); } @Override diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml index e2b568cfea77..080be6d8cf25 100644 --- a/packages/SystemUI/tests/AndroidManifest.xml +++ b/packages/SystemUI/tests/AndroidManifest.xml @@ -196,6 +196,17 @@ android:exported="false" android:permission="com.android.systemui.permission.SELF" android:excludeFromRecents="true" /> + + <activity + android:name="com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity" + android:exported="false" + android:permission="com.android.systemui.permission.SELF" + android:excludeFromRecents="true" > + <intent-filter> + <action android:name="com.android.systemui.action.MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> </application> <instrumentation android:name="android.testing.TestableInstrumentation" diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java index f4df26dec89e..8a05a37ad0dd 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -682,8 +682,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { } @Test - public void testReinflateViewFlipper_asyncBouncerFlagOn() { - when(mFeatureFlags.isEnabled(Flags.ASYNC_INFLATE_BOUNCER)).thenReturn(true); + public void testReinflateViewFlipper() { KeyguardSecurityViewFlipperController.OnViewInflatedCallback onViewInflatedCallback = controller -> { }; diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 2962c14b813a..ddd9a084bbd2 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -151,7 +151,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; +import org.mockito.InOrder; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.MockitoSession; import org.mockito.internal.util.reflection.FieldSetter; @@ -2737,6 +2739,36 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { verifyFingerprintAuthenticateCall(); } + @Test + public void onTrustChangedCallbacksCalledBeforeOnTrustGrantedForCurrentUserCallback() { + // GIVEN device is interactive + deviceIsInteractive(); + + // GIVEN callback is registered + KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class); + mKeyguardUpdateMonitor.registerCallback(callback); + + // WHEN onTrustChanged enabled=true + mKeyguardUpdateMonitor.onTrustChanged( + true /* enabled */, + true /* newlyUnlocked */, + getCurrentUser() /* userId */, + TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD /* flags */, + null /* trustGrantedMessages */); + + // THEN onTrustChanged is called FIRST + final InOrder inOrder = Mockito.inOrder(callback); + inOrder.verify(callback).onTrustChanged(eq(getCurrentUser())); + + // AND THEN onTrustGrantedForCurrentUser callback called + inOrder.verify(callback).onTrustGrantedForCurrentUser( + eq(true) /* dismissKeyguard */, + eq(true) /* newlyUnlocked */, + eq(new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD)), + eq(null) /* message */ + ); + } + private void verifyFingerprintAuthenticateNeverCalled() { verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), any()); verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index a20875b5d9de..6e37ee791bd2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -40,14 +40,19 @@ import com.android.internal.widget.LockPatternUtils import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.FakePromptRepository +import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor +import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl +import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope import org.junit.After import org.junit.Ignore import org.junit.Rule @@ -87,13 +92,26 @@ class AuthContainerViewTest : SysuiTestCase() { @Mock lateinit var interactionJankMonitor: InteractionJankMonitor + private val testScope = TestScope(StandardTestDispatcher()) + private val fakeExecutor = FakeExecutor(FakeSystemClock()) private val biometricPromptRepository = FakePromptRepository() + private val rearDisplayStateRepository = FakeRearDisplayStateRepository() private val credentialInteractor = FakeCredentialInteractor() private val bpCredentialInteractor = BiometricPromptCredentialInteractor( Dispatchers.Main.immediate, biometricPromptRepository, credentialInteractor ) + private val displayStateInteractor = DisplayStateInteractorImpl( + testScope.backgroundScope, + mContext, + fakeExecutor, + rearDisplayStateRepository + ) + + private val authBiometricFingerprintViewModel = AuthBiometricFingerprintViewModel( + displayStateInteractor + ) private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor) private var authContainer: TestAuthContainerView? = null @@ -469,9 +487,10 @@ class AuthContainerViewTest : SysuiTestCase() { lockPatternUtils, interactionJankMonitor, { bpCredentialInteractor }, + { authBiometricFingerprintViewModel }, { credentialViewModel }, Handler(TestableLooper.get(this).looper), - FakeExecutor(FakeSystemClock()) + fakeExecutor ) { override fun postOnAnimation(runnable: Runnable) { runnable.run() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index 4f24b3ab5e09..a326cc7045dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -93,6 +93,7 @@ import com.android.systemui.RoboPilotTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor; import com.android.systemui.biometrics.domain.interactor.LogContextInteractor; +import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel; import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.statusbar.CommandQueue; @@ -172,6 +173,8 @@ public class AuthControllerTest extends SysuiTestCase { @Mock private BiometricPromptCredentialInteractor mBiometricPromptCredentialInteractor; @Mock + private AuthBiometricFingerprintViewModel mAuthBiometricFingerprintViewModel; + @Mock private CredentialViewModel mCredentialViewModel; @Mock private UdfpsUtils mUdfpsUtils; @@ -995,8 +998,9 @@ public class AuthControllerTest extends SysuiTestCase { () -> mSideFpsController, mDisplayManager, mWakefulnessLifecycle, mPanelInteractionDetector, mUserManager, mLockPatternUtils, mUdfpsLogger, mLogContextInteractor, () -> mBiometricPromptCredentialInteractor, - () -> mCredentialViewModel, mInteractionJankMonitor, mHandler, - mBackgroundExecutor, mVibratorHelper, mUdfpsUtils); + () -> mAuthBiometricFingerprintViewModel, () -> mCredentialViewModel, + mInteractionJankMonitor, mHandler, mBackgroundExecutor, mVibratorHelper, + mUdfpsUtils); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt index e6334cf2e4a1..40d9009ee81d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt @@ -55,6 +55,9 @@ import com.android.systemui.R import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestableContext +import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository +import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor +import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository @@ -66,7 +69,9 @@ import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestCoroutineScope +import kotlinx.coroutines.test.TestScope import org.junit.Before import org.junit.Rule import org.junit.Test @@ -90,6 +95,8 @@ import org.mockito.junit.MockitoJUnit private const val DISPLAY_ID = 2 private const val SENSOR_ID = 1 +private const val REAR_DISPLAY_MODE_DEVICE_STATE = 3 + @SmallTest @RoboPilotTest @RunWith(AndroidJUnit4::class) @@ -112,7 +119,12 @@ class SideFpsControllerTest : SysuiTestCase() { private lateinit var keyguardBouncerRepository: FakeKeyguardBouncerRepository private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor + private lateinit var displayStateInteractor: DisplayStateInteractor + private val executor = FakeExecutor(FakeSystemClock()) + private val rearDisplayStateRepository = FakeRearDisplayStateRepository() + private val testScope = TestScope(StandardTestDispatcher()) + private lateinit var overlayController: ISidefpsController private lateinit var sideFpsController: SideFpsController @@ -142,6 +154,13 @@ class SideFpsControllerTest : SysuiTestCase() { FakeDeviceEntryFingerprintAuthRepository(), FakeSystemClock(), ) + displayStateInteractor = + DisplayStateInteractorImpl( + testScope.backgroundScope, + context, + executor, + rearDisplayStateRepository + ) context.addMockSystemService(DisplayManager::class.java, displayManager) context.addMockSystemService(WindowManager::class.java, windowManager) @@ -168,6 +187,7 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation: Boolean = false, initInfo: DisplayInfo.() -> Unit = {}, windowInsets: WindowInsets = insetsForSmallNavbar(), + inRearDisplayMode: Boolean = false, block: () -> Unit ) { this.deviceConfig = deviceConfig @@ -228,6 +248,13 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation ) + val rearDisplayDeviceStates = + if (inRearDisplayMode) intArrayOf(REAR_DISPLAY_MODE_DEVICE_STATE) else intArrayOf() + sideFpsControllerContext.orCreateTestableResources.addOverride( + com.android.internal.R.array.config_rearDisplayDeviceStates, + rearDisplayDeviceStates + ) + sideFpsController = SideFpsController( sideFpsControllerContext, @@ -237,12 +264,14 @@ class SideFpsControllerTest : SysuiTestCase() { activityTaskManager, overviewProxyService, displayManager, + displayStateInteractor, executor, handler, alternateBouncerInteractor, TestCoroutineScope(), - dumpManager, + dumpManager ) + rearDisplayStateRepository.setIsInRearDisplayMode(inRearDisplayMode) overlayController = ArgumentCaptor.forClass(ISidefpsController::class.java) @@ -584,10 +613,62 @@ class SideFpsControllerTest : SysuiTestCase() { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + @Test + fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_0() = + testWithDisplay( + deviceConfig = DeviceConfig.Y_ALIGNED, + isReverseDefaultRotation = false, + { rotation = Surface.ROTATION_0 }, + inRearDisplayMode = true, + ) { + verifySfpsIndicator_notAdded_InRearDisplayMode() + } + + @Test + fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_90() = + testWithDisplay( + deviceConfig = DeviceConfig.Y_ALIGNED, + isReverseDefaultRotation = false, + { rotation = Surface.ROTATION_90 }, + inRearDisplayMode = true, + ) { + verifySfpsIndicator_notAdded_InRearDisplayMode() + } + + @Test + fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_180() = + testWithDisplay( + deviceConfig = DeviceConfig.Y_ALIGNED, + isReverseDefaultRotation = false, + { rotation = Surface.ROTATION_180 }, + inRearDisplayMode = true, + ) { + verifySfpsIndicator_notAdded_InRearDisplayMode() + } + + @Test + fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_270() = + testWithDisplay( + deviceConfig = DeviceConfig.Y_ALIGNED, + isReverseDefaultRotation = false, + { rotation = Surface.ROTATION_270 }, + inRearDisplayMode = true, + ) { + verifySfpsIndicator_notAdded_InRearDisplayMode() + } + private fun verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible: Boolean) { sideFpsController.overlayOffsets = sensorLocation } + private fun verifySfpsIndicator_notAdded_InRearDisplayMode() { + sideFpsController.overlayOffsets = sensorLocation + overlayController.show(SENSOR_ID, REASON_UNKNOWN) + executor.runAllReady() + + verify(windowManager, never()).addView(any(), any()) + } + fun alternateBouncerVisibility_showAndHideSideFpsUI() = testWithDisplay { // WHEN alternate bouncer is visible keyguardBouncerRepository.setAlternateVisible(true) @@ -624,7 +705,7 @@ class SideFpsControllerTest : SysuiTestCase() { * in other rotations have been omitted. */ @Test - fun verifiesIndicatorPlacementForXAlignedSensor_0() { + fun verifiesIndicatorPlacementForXAlignedSensor_0() = testWithDisplay( deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = false, @@ -641,7 +722,6 @@ class SideFpsControllerTest : SysuiTestCase() { assertThat(overlayViewParamsCaptor.value.x).isEqualTo(sensorLocation.sensorLocationX) assertThat(overlayViewParamsCaptor.value.y).isEqualTo(0) } - } /** * {@link SideFpsController#updateOverlayParams} calculates indicator placement for ROTATION_270 @@ -650,7 +730,7 @@ class SideFpsControllerTest : SysuiTestCase() { * correctly, tests for indicator placement in other rotations have been omitted. */ @Test - fun verifiesIndicatorPlacementForXAlignedSensor_InReverseDefaultRotation_270() { + fun verifiesIndicatorPlacementForXAlignedSensor_InReverseDefaultRotation_270() = testWithDisplay( deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = true, @@ -667,7 +747,6 @@ class SideFpsControllerTest : SysuiTestCase() { assertThat(overlayViewParamsCaptor.value.x).isEqualTo(sensorLocation.sensorLocationX) assertThat(overlayViewParamsCaptor.value.y).isEqualTo(0) } - } /** * {@link SideFpsController#updateOverlayParams} calculates indicator placement for ROTATION_0, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepositoryTest.kt new file mode 100644 index 000000000000..dfe8d36504d0 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepositoryTest.kt @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.data.repository + +import android.hardware.devicestate.DeviceStateManager +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.data.repository.RearDisplayStateRepository +import com.android.systemui.biometrics.data.repository.RearDisplayStateRepositoryImpl +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.withArgCaptor +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule + +private const val NORMAL_DISPLAY_MODE_DEVICE_STATE = 2 +private const val REAR_DISPLAY_MODE_DEVICE_STATE = 3 + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class RearDisplayStateRepositoryTest : SysuiTestCase() { + @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule() + @Mock private lateinit var deviceStateManager: DeviceStateManager + private lateinit var underTest: RearDisplayStateRepository + + private val testScope = TestScope(StandardTestDispatcher()) + private val fakeExecutor = FakeExecutor(FakeSystemClock()) + + @Captor + private lateinit var callbackCaptor: ArgumentCaptor<DeviceStateManager.DeviceStateCallback> + + @Before + fun setUp() { + val rearDisplayDeviceStates = intArrayOf(REAR_DISPLAY_MODE_DEVICE_STATE) + mContext.orCreateTestableResources.addOverride( + com.android.internal.R.array.config_rearDisplayDeviceStates, + rearDisplayDeviceStates + ) + + underTest = + RearDisplayStateRepositoryImpl( + testScope.backgroundScope, + mContext, + deviceStateManager, + fakeExecutor + ) + } + + @Test + fun updatesIsInRearDisplayMode_whenRearDisplayStateChanges() = + testScope.runTest { + val isInRearDisplayMode = collectLastValue(underTest.isInRearDisplayMode) + runCurrent() + + val callback = deviceStateManager.captureCallback() + + callback.onStateChanged(NORMAL_DISPLAY_MODE_DEVICE_STATE) + assertThat(isInRearDisplayMode()).isFalse() + + callback.onStateChanged(REAR_DISPLAY_MODE_DEVICE_STATE) + assertThat(isInRearDisplayMode()).isTrue() + } +} + +private fun DeviceStateManager.captureCallback() = + withArgCaptor<DeviceStateManager.DeviceStateCallback> { + verify(this@captureCallback).registerCallback(any(), capture()) + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt new file mode 100644 index 000000000000..2217c5c677d5 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt @@ -0,0 +1,84 @@ +package com.android.systemui.biometrics.domain.interactor + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.unfold.compat.ScreenSizeFoldProvider +import com.android.systemui.unfold.updates.FoldProvider +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.withArgCaptor +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class DisplayStateInteractorImplTest : SysuiTestCase() { + + @JvmField @Rule var mockitoRule = MockitoJUnit.rule() + + private val fakeExecutor = FakeExecutor(FakeSystemClock()) + private val testScope = TestScope(StandardTestDispatcher()) + private val rearDisplayStateRepository = FakeRearDisplayStateRepository() + + @Mock private lateinit var screenSizeFoldProvider: ScreenSizeFoldProvider + private lateinit var interactor: DisplayStateInteractorImpl + + @Before + fun setup() { + interactor = + DisplayStateInteractorImpl( + testScope.backgroundScope, + mContext, + fakeExecutor, + rearDisplayStateRepository + ) + interactor.setScreenSizeFoldProvider(screenSizeFoldProvider) + } + + @Test + fun isInRearDisplayModeChanges() = + testScope.runTest { + val isInRearDisplayMode = collectLastValue(interactor.isInRearDisplayMode) + + rearDisplayStateRepository.setIsInRearDisplayMode(false) + assertThat(isInRearDisplayMode()).isFalse() + + rearDisplayStateRepository.setIsInRearDisplayMode(true) + assertThat(isInRearDisplayMode()).isTrue() + } + + @Test + fun isFoldedChanges() = + testScope.runTest { + val isFolded = collectLastValue(interactor.isFolded) + runCurrent() + val callback = screenSizeFoldProvider.captureCallback() + + callback.onFoldUpdated(isFolded = true) + assertThat(isFolded()).isTrue() + + callback.onFoldUpdated(isFolded = false) + assertThat(isFolded()).isFalse() + } +} + +private fun FoldProvider.captureCallback() = + withArgCaptor<FoldProvider.FoldCallback> { + verify(this@captureCallback).registerCallback(capture(), any()) + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModelTest.kt new file mode 100644 index 000000000000..0c210e51cb90 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModelTest.kt @@ -0,0 +1,69 @@ +package com.android.systemui.biometrics.ui.viewmodel + +import android.content.res.Configuration +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository +import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor +import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class AuthBiometricFingerprintViewModelTest : SysuiTestCase() { + + private val rearDisplayStateRepository = FakeRearDisplayStateRepository() + private val testScope = TestScope(StandardTestDispatcher()) + private val fakeExecutor = FakeExecutor(FakeSystemClock()) + + private lateinit var interactor: DisplayStateInteractor + private lateinit var viewModel: AuthBiometricFingerprintViewModel + + @Before + fun setup() { + interactor = + DisplayStateInteractorImpl( + testScope.backgroundScope, + mContext, + fakeExecutor, + rearDisplayStateRepository + ) + viewModel = AuthBiometricFingerprintViewModel(interactor) + } + + @Test + fun iconUpdates_onConfigurationChanged() { + testScope.runTest { + runCurrent() + val testConfig = Configuration() + val folded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP - 1 + val unfolded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP + 1 + val currentIcon = collectLastValue(viewModel.iconAsset) + + testConfig.smallestScreenWidthDp = folded + viewModel.onConfigurationChanged(testConfig) + val foldedIcon = currentIcon() + + testConfig.smallestScreenWidthDp = unfolded + viewModel.onConfigurationChanged(testConfig) + val unfoldedIcon = currentIcon() + + assertThat(foldedIcon).isNotEqualTo(unfoldedIcon) + } + } +} + +internal const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600 diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt new file mode 100644 index 000000000000..21516d4917b5 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt @@ -0,0 +1,83 @@ +/* + * 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.clipboardoverlay + +import android.content.ContentResolver +import android.content.Context +import android.net.Uri +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.whenever +import java.io.IOException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class ClipboardImageLoaderTest : SysuiTestCase() { + @Mock private lateinit var mockContext: Context + + @Mock private lateinit var mockContentResolver: ContentResolver + + private lateinit var clipboardImageLoader: ClipboardImageLoader + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + } + + @Test + @Throws(IOException::class) + fun test_imageLoadSuccess() = runTest { + val testDispatcher = StandardTestDispatcher(this.testScheduler) + clipboardImageLoader = + ClipboardImageLoader(mockContext, testDispatcher, CoroutineScope(testDispatcher)) + val testUri = Uri.parse("testUri") + whenever(mockContext.contentResolver).thenReturn(mockContentResolver) + whenever(mockContext.resources).thenReturn(context.resources) + + clipboardImageLoader.load(testUri) + + verify(mockContentResolver).loadThumbnail(eq(testUri), any(), any()) + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + @Throws(IOException::class) + fun test_imageLoadFailure() = runTest { + val testDispatcher = StandardTestDispatcher(this.testScheduler) + clipboardImageLoader = + ClipboardImageLoader(mockContext, testDispatcher, CoroutineScope(testDispatcher)) + val testUri = Uri.parse("testUri") + whenever(mockContext.contentResolver).thenReturn(mockContentResolver) + whenever(mockContext.resources).thenReturn(context.resources) + + val res = clipboardImageLoader.load(testUri) + + verify(mockContentResolver).loadThumbnail(eq(testUri), any(), any()) + assertNull(res) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java index fe5fa1fdd39f..39fb7b4cda2c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java @@ -25,6 +25,7 @@ import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBO import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHOWN_EXPANDED; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHOWN_MINIMIZED; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED; +import static com.android.systemui.flags.Flags.CLIPBOARD_IMAGE_TIMEOUT; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -90,6 +91,8 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Mock private ClipboardOverlayUtils mClipboardUtils; @Mock + private ClipboardImageLoader mClipboardImageLoader; + @Mock private UiEventLogger mUiEventLogger; private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext); private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); @@ -120,6 +123,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { mSampleClipData = new ClipData("Test", new String[]{"text/plain"}, new ClipData.Item("Test Item")); + mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, true); // turned off for legacy tests mOverlayController = new ClipboardOverlayController( mContext, @@ -131,6 +135,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { mFeatureFlags, mClipboardUtils, mExecutor, + mClipboardImageLoader, mUiEventLogger); verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture()); mCallbacks = mOverlayCallbacksCaptor.getValue(); @@ -142,6 +147,69 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { } @Test + public void test_setClipData_invalidImageData_legacy() { + ClipData clipData = new ClipData("", new String[]{"image/png"}, + new ClipData.Item(Uri.parse(""))); + mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); + + mOverlayController.setClipData(clipData, ""); + + verify(mClipboardOverlayView, times(1)).showDefaultTextPreview(); + verify(mClipboardOverlayView, times(1)).showShareChip(); + verify(mClipboardOverlayView, times(1)).getEnterAnimation(); + } + + @Test + public void test_setClipData_nonImageUri_legacy() { + ClipData clipData = new ClipData("", new String[]{"resource/png"}, + new ClipData.Item(Uri.parse(""))); + mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); + + mOverlayController.setClipData(clipData, ""); + + verify(mClipboardOverlayView, times(1)).showDefaultTextPreview(); + verify(mClipboardOverlayView, times(1)).showShareChip(); + verify(mClipboardOverlayView, times(1)).getEnterAnimation(); + } + + @Test + public void test_setClipData_textData_legacy() { + mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); + mOverlayController.setClipData(mSampleClipData, "abc"); + + verify(mClipboardOverlayView, times(1)).showTextPreview("Test Item", false); + verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_SHOWN_EXPANDED, 0, "abc"); + verify(mClipboardOverlayView, times(1)).showShareChip(); + verify(mClipboardOverlayView, times(1)).getEnterAnimation(); + } + + @Test + public void test_setClipData_sensitiveTextData_legacy() { + mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); + ClipDescription description = mSampleClipData.getDescription(); + PersistableBundle b = new PersistableBundle(); + b.putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true); + description.setExtras(b); + ClipData data = new ClipData(description, mSampleClipData.getItemAt(0)); + mOverlayController.setClipData(data, ""); + + verify(mClipboardOverlayView, times(1)).showTextPreview("••••••", true); + verify(mClipboardOverlayView, times(1)).showShareChip(); + verify(mClipboardOverlayView, times(1)).getEnterAnimation(); + } + + @Test + public void test_setClipData_repeatedCalls_legacy() { + when(mAnimator.isRunning()).thenReturn(true); + mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); + + mOverlayController.setClipData(mSampleClipData, ""); + mOverlayController.setClipData(mSampleClipData, ""); + + verify(mClipboardOverlayView, times(1)).getEnterAnimation(); + } + + @Test public void test_setClipData_invalidImageData() { ClipData clipData = new ClipData("", new String[]{"image/png"}, new ClipData.Item(Uri.parse(""))); diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt index c3506e80966b..54142590b453 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.controls.ui +import android.app.ActivityOptions import android.app.PendingIntent import android.testing.AndroidTestingRunner import android.testing.TestableLooper @@ -24,7 +25,10 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastSender import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.capture import com.android.wm.shell.taskview.TaskView +import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -69,6 +73,25 @@ class DetailDialogTest : SysuiTestCase() { verify(taskView).startActivity(eq(pendingIntent), any(), any(), any()) } + @Test + fun testActivityOptionsAllowBal() { + // GIVEN the dialog is created with a PendingIntent + val dialog = createDialog(pendingIntent) + + // WHEN the TaskView is initialized + dialog.stateCallback.onInitialized() + + val optionsCaptor = argumentCaptor<ActivityOptions>() + + // THEN the ActivityOptions have the correct flags + verify(taskView).startActivity(any(), any(), capture(optionsCaptor), any()) + + assertThat(optionsCaptor.value.pendingIntentBackgroundActivityStartMode) + .isEqualTo(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) + assertThat(optionsCaptor.value.isPendingIntentBackgroundActivityLaunchAllowedByPermission) + .isTrue() + } + private fun createDialog(pendingIntent: PendingIntent): DetailDialog { return DetailDialog( mContext, diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java index 8795ac013bd3..c9ee1e8ef5b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java @@ -70,6 +70,7 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.phone.CentralSurfaces; +import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.telephony.TelephonyListenerManager; @@ -115,6 +116,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { @Mock private MetricsLogger mMetricsLogger; @Mock private SysuiColorExtractor mColorExtractor; @Mock private IStatusBarService mStatusBarService; + @Mock private LightBarController mLightBarController; @Mock private NotificationShadeWindowController mNotificationShadeWindowController; @Mock private IWindowManager mWindowManager; @Mock private Executor mBackgroundExecutor; @@ -166,6 +168,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { mMetricsLogger, mColorExtractor, mStatusBarService, + mLightBarController, mNotificationShadeWindowController, mWindowManager, mBackgroundExecutor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java index b9cfc6550966..e981d627b582 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java @@ -25,13 +25,11 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.IActivityTaskManager; import android.app.IApplicationThread; import android.app.ProfilerInfo; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -59,13 +57,14 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidJUnit4.class) public class WorkLockActivityControllerTest extends SysuiTestCase { - private static final int USER_ID = 333; + private static final int TASK_USER_ID = 333; + private static final int PROFILE_USER_ID = 555; private static final int TASK_ID = 444; private static final ActivityManager.RunningTaskInfo TASK_INFO = new ActivityManager.RunningTaskInfo(); static { - TASK_INFO.userId = USER_ID; + TASK_INFO.userId = TASK_USER_ID; TASK_INFO.taskId = TASK_ID; } @@ -101,10 +100,10 @@ public class WorkLockActivityControllerTest extends SysuiTestCase { setActivityStartCode(TASK_ID, true /*taskOverlay*/, ActivityManager.START_SUCCESS); // And the controller receives a message saying the profile is locked, - mTaskStackListener.onTaskProfileLocked(TASK_INFO); + mTaskStackListener.onTaskProfileLocked(TASK_INFO, PROFILE_USER_ID); // The overlay should start and the task the activity started in should not be removed. - verifyStartActivity(TASK_ID, true /*taskOverlay*/); + verifyStartActivity(TASK_ID, true /*taskOverlay*/, PROFILE_USER_ID); verify(mIActivityTaskManager, never()).removeTask(anyInt() /*taskId*/); } @@ -114,11 +113,11 @@ public class WorkLockActivityControllerTest extends SysuiTestCase { setActivityStartCode(TASK_ID, true /*taskOverlay*/, ActivityManager.START_CLASS_NOT_FOUND); // And the controller receives a message saying the profile is locked, - mTaskStackListener.onTaskProfileLocked(TASK_INFO); + mTaskStackListener.onTaskProfileLocked(TASK_INFO, PROFILE_USER_ID); // The task the activity started in should be removed to prevent the locked task from // being shown. - verifyStartActivity(TASK_ID, true /*taskOverlay*/); + verifyStartActivity(TASK_ID, true /*taskOverlay*/, PROFILE_USER_ID); verify(mIActivityTaskManager).removeTask(TASK_ID); } @@ -141,12 +140,13 @@ public class WorkLockActivityControllerTest extends SysuiTestCase { eq(ActivityManager.getCurrentUser())); } - private void verifyStartActivity(int taskId, boolean taskOverlay) throws Exception { + private void verifyStartActivity(int taskId, boolean taskOverlay, int profileUserId) + throws Exception { verify(mIActivityTaskManager).startActivityAsUser( eq((IApplicationThread) null), eq((String) null), eq((String) null), - any(Intent.class), + argThat(hasUserId(profileUserId)), eq((String) null), eq((IBinder) null), eq((String) null), @@ -157,24 +157,15 @@ public class WorkLockActivityControllerTest extends SysuiTestCase { eq(ActivityManager.getCurrentUser())); } - private static ArgumentMatcher<Intent> hasComponent(final Context context, - final Class<? extends Activity> activityClass) { - return new ArgumentMatcher<Intent>() { - @Override - public boolean matches(Intent intent) { - return new ComponentName(context, activityClass).equals(intent.getComponent()); - } - }; + private static ArgumentMatcher<Intent> hasUserId(int userId) { + return intent -> intent.getIntExtra(Intent.EXTRA_USER_ID, -1) == userId; } private static ArgumentMatcher<Bundle> hasOptions(final int taskId, final boolean overlay) { - return new ArgumentMatcher<Bundle>() { - @Override - public boolean matches(Bundle item) { - final ActivityOptions options = ActivityOptions.fromBundle(item); - return (options.getLaunchTaskId() == taskId) - && (options.getTaskOverlay() == overlay); - } + return item -> { + final ActivityOptions options = ActivityOptions.fromBundle(item); + return (options.getLaunchTaskId() == taskId) + && (options.getTaskOverlay() == overlay); }; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt index a003e1d9d1f3..43e430fd7fa2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt @@ -19,7 +19,9 @@ package com.android.systemui.log.table import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.log.table.TableChange.Companion.IS_INITIAL_PREFIX +import com.android.systemui.log.table.TableChange.Companion.MAX_STRING_LENGTH import com.google.common.truth.Truth.assertThat +import junit.framework.Assert.assertTrue import org.junit.Test @SmallTest @@ -326,4 +328,152 @@ class TableChangeTest : SysuiTestCase() { assertThat(underTest.getVal()).doesNotContain(IS_INITIAL_PREFIX) } + + @Test + fun constructor_columnAndValueTooLong_truncated() { + val underTest = + TableChange( + columnPrefix = "P".repeat(MAX_STRING_LENGTH + 10), + columnName = "N".repeat(MAX_STRING_LENGTH + 10), + type = TableChange.DataType.STRING, + str = "V".repeat(MAX_STRING_LENGTH + 10), + ) + + assertThat(underTest.getName()).contains("P".repeat(MAX_STRING_LENGTH)) + assertThat(underTest.getName()).doesNotContain("P".repeat(MAX_STRING_LENGTH + 1)) + assertThat(underTest.getName()).contains("N".repeat(MAX_STRING_LENGTH)) + assertThat(underTest.getName()).doesNotContain("N".repeat(MAX_STRING_LENGTH + 1)) + assertThat(underTest.getVal()).isEqualTo("V".repeat(MAX_STRING_LENGTH)) + } + + @Test + fun constructor_columnNameNotTooLong_noReallocation() { + val inputColumnName = "fakeName" + val inputValue = "fakeValue" + val underTest = + TableChange( + columnPrefix = "", + columnName = inputColumnName, + type = TableChange.DataType.STRING, + str = inputValue, + ) + + // Use referential equality to verify we didn't reallocate a new string when the string is + // *not* too long. + assertTrue(underTest.getColumnName() === inputColumnName) + } + + @Test + fun reset_columnPrefixTooLong_truncated() { + val underTest = TableChange() + + underTest.reset( + timestamp = 1L, + columnPrefix = "P".repeat(MAX_STRING_LENGTH + 10), + columnName = "name", + isInitial = false, + ) + + assertThat(underTest.getName()).contains("P".repeat(MAX_STRING_LENGTH)) + assertThat(underTest.getName()).doesNotContain("P".repeat(MAX_STRING_LENGTH + 1)) + } + + @Test + fun reset_columnNameTooLong_truncated() { + val underTest = TableChange() + + underTest.reset( + timestamp = 1L, + columnPrefix = "prefix", + columnName = "N".repeat(MAX_STRING_LENGTH + 10), + isInitial = false, + ) + + assertThat(underTest.getName()).contains("N".repeat(MAX_STRING_LENGTH)) + assertThat(underTest.getName()).doesNotContain("N".repeat(MAX_STRING_LENGTH + 1)) + } + + @Test + fun reset_columnNameNotTooLong_noReallocation() { + val underTest = TableChange() + val shortColumnName = "shortColumnName" + + underTest.reset( + timestamp = 1L, + columnPrefix = "prefix", + columnName = shortColumnName, + isInitial = false, + ) + + // Use referential equality to verify we didn't reallocate a new string when the string is + // *not* too long. + assertTrue(underTest.getColumnName() === shortColumnName) + } + + @Test + fun setString_valueTooLong_truncated() { + val underTest = TableChange() + + underTest.set("V".repeat(MAX_STRING_LENGTH + 1)) + + assertThat(underTest.getVal()).isEqualTo("V".repeat(MAX_STRING_LENGTH)) + } + + @Test + fun updateTo_newColumnPrefixTooLong_truncated() { + val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName") + underTest.set(42) + + val new = + TableChange( + columnPrefix = "P".repeat(MAX_STRING_LENGTH + 10), + columnName = "name", + ) + underTest.updateTo(new) + + assertThat(underTest.getName()).contains("P".repeat(MAX_STRING_LENGTH)) + assertThat(underTest.getName()).doesNotContain("P".repeat(MAX_STRING_LENGTH + 1)) + } + + @Test + fun updateTo_newColumnNameTooLong_truncated() { + val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName") + underTest.set(42) + + val new = + TableChange( + columnPrefix = "prefix", + columnName = "N".repeat(MAX_STRING_LENGTH + 10), + ) + underTest.updateTo(new) + + assertThat(underTest.getName()).contains("N".repeat(MAX_STRING_LENGTH)) + assertThat(underTest.getName()).doesNotContain("N".repeat(MAX_STRING_LENGTH + 1)) + } + + @Test + fun updateTo_columnNameNotTooLong_noReallocation() { + val underTest = TableChange() + val shortColumnName = "shortColumnName" + val new = TableChange(columnPrefix = "prefix", columnName = shortColumnName) + + underTest.updateTo(new) + + // Use referential equality to verify we didn't reallocate a new string when the string is + // *not* too long. + assertTrue(underTest.getColumnName() === shortColumnName) + } + + @Test + fun updateTo_newValTooLong_truncated() { + val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName") + underTest.set("value") + + val new = TableChange() + new.set("V".repeat(MAX_STRING_LENGTH + 10)) + + underTest.updateTo(new) + + assertThat(underTest.getVal()).isEqualTo("V".repeat(MAX_STRING_LENGTH)) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt index a2b2322899b7..f867fc7bf84a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.log.table import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.log.table.TableChange.Companion.IS_INITIAL_PREFIX +import com.android.systemui.log.table.TableChange.Companion.MAX_STRING_LENGTH import com.android.systemui.plugins.log.LogLevel import com.android.systemui.plugins.log.LogcatEchoTracker import com.android.systemui.util.mockito.any @@ -576,6 +577,112 @@ class TableLogBufferTest : SysuiTestCase() { } @Test + fun dumpChanges_tooLongColumnPrefix_viaLogChange_truncated() { + underTest.logChange( + prefix = "P".repeat(MAX_STRING_LENGTH + 10), + columnName = "name", + value = true, + ) + + val dumpedString = dumpChanges() + + assertThat(dumpedString).contains("P".repeat(MAX_STRING_LENGTH)) + assertThat(dumpedString).doesNotContain("P".repeat(MAX_STRING_LENGTH + 1)) + } + + @Test + fun dumpChanges_tooLongColumnPrefix_viaLogDiffs_truncated() { + val prevDiffable = object : TestDiffable() {} + val nextDiffable = + object : TestDiffable() { + override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) { + row.logChange("status", "value") + } + } + + // WHEN the column prefix is too large + underTest.logDiffs( + columnPrefix = "P".repeat(MAX_STRING_LENGTH + 10), + prevDiffable, + nextDiffable, + ) + + val dumpedString = dumpChanges() + + // THEN it's truncated to the max length + assertThat(dumpedString).contains("P".repeat(MAX_STRING_LENGTH)) + assertThat(dumpedString).doesNotContain("P".repeat(MAX_STRING_LENGTH + 1)) + } + + @Test + fun dumpChanges_tooLongColumnName_viaLogChange_truncated() { + underTest.logChange( + prefix = "prefix", + columnName = "N".repeat(MAX_STRING_LENGTH + 10), + value = 10, + ) + + val dumpedString = dumpChanges() + + assertThat(dumpedString).contains("N".repeat(MAX_STRING_LENGTH)) + assertThat(dumpedString).doesNotContain("N".repeat(MAX_STRING_LENGTH + 1)) + } + + @Test + fun dumpChanges_tooLongColumnName_viaLogDiffs_truncated() { + val prevDiffable = object : TestDiffable() {} + val nextDiffable = + object : TestDiffable() { + override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) { + // WHEN the column name is too large + row.logChange(columnName = "N".repeat(MAX_STRING_LENGTH + 10), "value") + } + } + + underTest.logDiffs(columnPrefix = "prefix", prevDiffable, nextDiffable) + + val dumpedString = dumpChanges() + + // THEN it's truncated to the max length + assertThat(dumpedString).contains("N".repeat(MAX_STRING_LENGTH)) + assertThat(dumpedString).doesNotContain("N".repeat(MAX_STRING_LENGTH + 1)) + } + + @Test + fun dumpChanges_tooLongValue_viaLogChange_truncated() { + underTest.logChange( + prefix = "prefix", + columnName = "name", + value = "V".repeat(MAX_STRING_LENGTH + 10), + ) + + val dumpedString = dumpChanges() + + assertThat(dumpedString).contains("V".repeat(MAX_STRING_LENGTH)) + assertThat(dumpedString).doesNotContain("V".repeat(MAX_STRING_LENGTH + 1)) + } + + @Test + fun dumpChanges_tooLongValue_viaLogDiffs_truncated() { + val prevDiffable = object : TestDiffable() {} + val nextDiffable = + object : TestDiffable() { + override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) { + // WHEN the value is too large + row.logChange("columnName", value = "V".repeat(MAX_STRING_LENGTH + 10)) + } + } + + underTest.logDiffs(columnPrefix = "prefix", prevDiffable, nextDiffable) + + val dumpedString = dumpChanges() + + // THEN it's truncated to the max length + assertThat(dumpedString).contains("V".repeat(MAX_STRING_LENGTH)) + assertThat(dumpedString).doesNotContain("V".repeat(MAX_STRING_LENGTH + 1)) + } + + @Test fun dumpChanges_rotatesIfBufferIsFull() { lateinit var valToDump: String diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt index 0a1db60e075c..d428db7b9dda 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt @@ -40,7 +40,6 @@ import android.testing.TestableLooper.RunWithLooper import androidx.media.utils.MediaConstants import androidx.test.filters.SmallTest import com.android.internal.logging.InstanceId -import com.android.internal.statusbar.IStatusBarService import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.InstanceIdSequenceFake import com.android.systemui.R @@ -131,7 +130,6 @@ class MediaDataManagerTest : SysuiTestCase() { @Mock lateinit var activityStarter: ActivityStarter @Mock lateinit var smartspaceManager: SmartspaceManager @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor - @Mock lateinit var statusBarService: IStatusBarService lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider @Mock lateinit var mediaSmartspaceTarget: SmartspaceTarget @Mock private lateinit var mediaRecommendationItem: SmartspaceAction @@ -194,8 +192,7 @@ class MediaDataManagerTest : SysuiTestCase() { mediaFlags = mediaFlags, logger = logger, smartspaceManager = smartspaceManager, - keyguardUpdateMonitor = keyguardUpdateMonitor, - statusBarService = statusBarService, + keyguardUpdateMonitor = keyguardUpdateMonitor ) verify(tunerService) .addTunable(capture(tunableCaptor), eq(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION)) @@ -520,136 +517,19 @@ class MediaDataManagerTest : SysuiTestCase() { } @Test - fun testOnNotificationAdded_emptyTitle_notLoaded() { - // GIVEN that the manager has a notification with an empty title. + fun testOnNotificationRemoved_emptyTitle_notConverted() { + // GIVEN that the manager has a notification with a resume action and empty title. whenever(controller.metadata) .thenReturn( metadataBuilder .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE) .build() ) - mediaDataManager.onNotificationAdded(KEY, mediaNotification) - - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - verify(statusBarService) - .onNotificationError( - eq(PACKAGE_NAME), - eq(mediaNotification.tag), - eq(mediaNotification.id), - eq(mediaNotification.uid), - eq(mediaNotification.initialPid), - eq(MEDIA_TITLE_ERROR_MESSAGE), - eq(mediaNotification.user.identifier) - ) - verify(listener, never()) - .onMediaDataLoaded( - eq(KEY), - eq(null), - capture(mediaDataCaptor), - eq(true), - eq(0), - eq(false) - ) - verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any()) - verify(logger, never()).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), any()) - } - - @Test - fun testOnNotificationAdded_blankTitle_notLoaded() { - // GIVEN that the manager has a notification with a blank title. - whenever(controller.metadata) - .thenReturn( - metadataBuilder - .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE) - .build() - ) - mediaDataManager.onNotificationAdded(KEY, mediaNotification) - - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - verify(statusBarService) - .onNotificationError( - eq(PACKAGE_NAME), - eq(mediaNotification.tag), - eq(mediaNotification.id), - eq(mediaNotification.uid), - eq(mediaNotification.initialPid), - eq(MEDIA_TITLE_ERROR_MESSAGE), - eq(mediaNotification.user.identifier) - ) - verify(listener, never()) - .onMediaDataLoaded( - eq(KEY), - eq(null), - capture(mediaDataCaptor), - eq(true), - eq(0), - eq(false) - ) - verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any()) - verify(logger, never()).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), any()) - } - - @Test - fun testOnNotificationUpdated_invalidTitle_logMediaRemoved() { - addNotificationAndLoad() - val data = mediaDataCaptor.value - - verify(listener) - .onMediaDataLoaded( - eq(KEY), - eq(null), - capture(mediaDataCaptor), - eq(true), - eq(0), - eq(false) - ) - - reset(listener) - whenever(controller.metadata) - .thenReturn( - metadataBuilder - .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE) - .build() - ) - mediaDataManager.onNotificationAdded(KEY, mediaNotification) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - verify(statusBarService) - .onNotificationError( - eq(PACKAGE_NAME), - eq(mediaNotification.tag), - eq(mediaNotification.id), - eq(mediaNotification.uid), - eq(mediaNotification.initialPid), - eq(MEDIA_TITLE_ERROR_MESSAGE), - eq(mediaNotification.user.identifier) - ) - verify(listener, never()) - .onMediaDataLoaded( - eq(KEY), - eq(null), - capture(mediaDataCaptor), - eq(true), - eq(0), - eq(false) - ) - verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId)) - } - - @Test - fun testOnNotificationRemoved_emptyTitle_notConverted() { - // GIVEN that the manager has a notification with a resume action and empty title. addNotificationAndLoad() val data = mediaDataCaptor.value val instanceId = data.instanceId assertThat(data.resumption).isFalse() - mediaDataManager.onMediaDataLoaded( - KEY, - null, - data.copy(song = SESSION_EMPTY_TITLE, resumeAction = Runnable {}) - ) + mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) // WHEN the notification is removed reset(listener) @@ -674,15 +554,17 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testOnNotificationRemoved_blankTitle_notConverted() { // GIVEN that the manager has a notification with a resume action and blank title. + whenever(controller.metadata) + .thenReturn( + metadataBuilder + .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE) + .build() + ) addNotificationAndLoad() val data = mediaDataCaptor.value val instanceId = data.instanceId assertThat(data.resumption).isFalse() - mediaDataManager.onMediaDataLoaded( - KEY, - null, - data.copy(song = SESSION_BLANK_TITLE, resumeAction = Runnable {}) - ) + mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) // WHEN the notification is removed reset(listener) diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/DynamicColorTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/DynamicColorTest.java index fb5197eff73f..4ce32d2c1f00 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/monet/DynamicColorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/monet/DynamicColorTest.java @@ -17,18 +17,16 @@ package com.android.systemui.monet; import static com.android.systemui.monet.utils.ArgbSubject.assertThat; - import static org.junit.Assert.assertTrue; - import androidx.test.filters.SmallTest; -import com.android.systemui.SysuiTestCase; import com.android.systemui.monet.contrast.Contrast; import com.android.systemui.monet.dynamiccolor.DynamicColor; import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors; import com.android.systemui.monet.dynamiccolor.ToneDeltaConstraint; import com.android.systemui.monet.dynamiccolor.TonePolarity; +import com.android.systemui.SysuiTestCase; import com.android.systemui.monet.hct.Hct; import com.android.systemui.monet.scheme.DynamicScheme; import com.android.systemui.monet.scheme.SchemeContent; @@ -36,182 +34,174 @@ import com.android.systemui.monet.scheme.SchemeFidelity; import com.android.systemui.monet.scheme.SchemeMonochrome; import com.android.systemui.monet.scheme.SchemeTonalSpot; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +@Ignore("b/279581953") @SmallTest @RunWith(JUnit4.class) -public final class DynamicColorTest extends SysuiTestCase { - - @Test - public void fromArgbNoBackground_doesntChangeForContrast() { - final int blueArgb = 0xff0000ff; - final DynamicColor dynamicColor = DynamicColor.fromArgb(blueArgb); - - final SchemeTonalSpot standardContrast = new SchemeTonalSpot(Hct.fromInt(blueArgb), false, - 0.0); - assertThat(dynamicColor.getArgb(standardContrast)).isSameColorAs(blueArgb); - - final SchemeTonalSpot minContrast = new SchemeTonalSpot(Hct.fromInt(blueArgb), false, -1.0); - assertThat(dynamicColor.getArgb(minContrast)).isSameColorAs(blueArgb); - - final SchemeTonalSpot maxContrast = new SchemeTonalSpot(Hct.fromInt(blueArgb), false, 1.0); - assertThat(dynamicColor.getArgb(maxContrast)).isSameColorAs(blueArgb); - } - - @Test - public void toneDeltaConstraintNoPreference_evaluatesCorrectly() { - final int blueArgb = 0xff0000ff; - final int redArgb = 0xffff0000; - final DynamicColor otherDynamicColor = DynamicColor.fromArgb(redArgb); - final DynamicColor dynamicColor = - DynamicColor.fromArgb( - blueArgb, - (s) -> 30.0, - null, - (s) -> new ToneDeltaConstraint(30, otherDynamicColor, - TonePolarity.NO_PREFERENCE)); - final SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(blueArgb), false, 0.0); - assertThat(dynamicColor.getArgb(scheme)).isSameColorAs(0xff0000ef); - } - - @Test - public void dynamicColor_withOpacity() { - final DynamicColor dynamicColor = - new DynamicColor( - s -> 0.0, - s -> 0.0, - s -> s.isDark ? 100.0 : 0.0, - s -> s.isDark ? 0.20 : 0.12, - null, - scheme -> - DynamicColor.toneMinContrastDefault( - (s) -> s.isDark ? 100.0 : 0.0, null, scheme, null), - scheme -> - DynamicColor.toneMaxContrastDefault( - (s) -> s.isDark ? 100.0 : 0.0, null, scheme, null), - null); - final SchemeTonalSpot lightScheme = new SchemeTonalSpot(Hct.fromInt(0xff4285f4), false, - 0.0); - assertThat(dynamicColor.getArgb(lightScheme)).isSameColorAs(0x1f000000); - - final SchemeTonalSpot darkScheme = new SchemeTonalSpot(Hct.fromInt(0xff4285f4), true, 0.0); - assertThat(dynamicColor.getArgb(darkScheme)).isSameColorAs(0x33ffffff); - } - - @Test - public void respectsContrast() { - final Hct[] seedColors = - new Hct[]{ - Hct.fromInt(0xFFFF0000), - Hct.fromInt(0xFFFFFF00), - Hct.fromInt(0xFF00FF00), - Hct.fromInt(0xFF0000FF) - }; - - final double[] contrastLevels = {-1.0, -0.5, 0.0, 0.5, 1.0}; - - for (Hct seedColor : seedColors) { - for (double contrastLevel : contrastLevels) { - for (boolean isDark : new boolean[]{false, true}) { - final DynamicScheme[] schemes = - new DynamicScheme[]{ - new SchemeContent(seedColor, isDark, contrastLevel), - new SchemeMonochrome(seedColor, isDark, contrastLevel), - new SchemeTonalSpot(seedColor, isDark, contrastLevel), - new SchemeFidelity(seedColor, isDark, contrastLevel) - }; - for (final DynamicScheme scheme : schemes) { - assertTrue( - pairSatisfiesContrast( - scheme, MaterialDynamicColors.onPrimary, - MaterialDynamicColors.primary)); - assertTrue( - pairSatisfiesContrast( - scheme, - MaterialDynamicColors.onPrimaryContainer, - MaterialDynamicColors.primaryContainer)); - assertTrue( - pairSatisfiesContrast( - scheme, MaterialDynamicColors.onSecondary, - MaterialDynamicColors.secondary)); - assertTrue( - pairSatisfiesContrast( - scheme, - MaterialDynamicColors.onSecondaryContainer, - MaterialDynamicColors.secondaryContainer)); - assertTrue( - pairSatisfiesContrast( - scheme, MaterialDynamicColors.onTertiary, - MaterialDynamicColors.tertiary)); - assertTrue( - pairSatisfiesContrast( - scheme, - MaterialDynamicColors.onTertiaryContainer, - MaterialDynamicColors.tertiaryContainer)); - assertTrue( - pairSatisfiesContrast( - scheme, MaterialDynamicColors.onError, - MaterialDynamicColors.error)); - assertTrue( - pairSatisfiesContrast( - scheme, - MaterialDynamicColors.onErrorContainer, - MaterialDynamicColors.errorContainer)); - assertTrue( - pairSatisfiesContrast( - scheme, MaterialDynamicColors.onBackground, - MaterialDynamicColors.background)); - assertTrue( - pairSatisfiesContrast( - scheme, - MaterialDynamicColors.onSurfaceVariant, - MaterialDynamicColors.surfaceVariant)); - assertTrue( - pairSatisfiesContrast( - scheme, - MaterialDynamicColors.onSurfaceInverse, - MaterialDynamicColors.surfaceInverse)); - } - } - } +public final class DynamicColorTest extends SysuiTestCase{ + + private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors(); + + @Test + public void fromArgbNoBackground_doesntChangeForContrast() { + final int blueArgb = 0xff0000ff; + final DynamicColor dynamicColor = DynamicColor.fromArgb(blueArgb); + + final SchemeTonalSpot standardContrast = new SchemeTonalSpot(Hct.fromInt(blueArgb), false, 0.0); + assertThat(dynamicColor.getArgb(standardContrast)).isSameColorAs(blueArgb); + + final SchemeTonalSpot minContrast = new SchemeTonalSpot(Hct.fromInt(blueArgb), false, -1.0); + assertThat(dynamicColor.getArgb(minContrast)).isSameColorAs(blueArgb); + + final SchemeTonalSpot maxContrast = new SchemeTonalSpot(Hct.fromInt(blueArgb), false, 1.0); + assertThat(dynamicColor.getArgb(maxContrast)).isSameColorAs(blueArgb); + } + + @Test + public void toneDeltaConstraintNoPreference_evaluatesCorrectly() { + final int blueArgb = 0xff0000ff; + final int redArgb = 0xffff0000; + final DynamicColor otherDynamicColor = DynamicColor.fromArgb(redArgb); + final DynamicColor dynamicColor = + DynamicColor.fromArgb( + blueArgb, + (s) -> 30.0, + null, + (s) -> new ToneDeltaConstraint(30, otherDynamicColor, TonePolarity.NO_PREFERENCE)); + final SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(blueArgb), false, 0.0); + assertThat(dynamicColor.getArgb(scheme)).isSameColorAs(0xff0000ef); + } + + @Test + public void dynamicColor_withOpacity() { + final DynamicColor dynamicColor = + new DynamicColor( + s -> 0.0, + s -> 0.0, + s -> s.isDark ? 100.0 : 0.0, + s -> s.isDark ? 0.20 : 0.12, + null, + scheme -> + DynamicColor.toneMinContrastDefault( + (s) -> s.isDark ? 100.0 : 0.0, null, scheme, null), + scheme -> + DynamicColor.toneMaxContrastDefault( + (s) -> s.isDark ? 100.0 : 0.0, null, scheme, null), + null); + final SchemeTonalSpot lightScheme = new SchemeTonalSpot(Hct.fromInt(0xff4285f4), false, 0.0); + assertThat(dynamicColor.getArgb(lightScheme)).isSameColorAs(0x1f000000); + + final SchemeTonalSpot darkScheme = new SchemeTonalSpot(Hct.fromInt(0xff4285f4), true, 0.0); + assertThat(dynamicColor.getArgb(darkScheme)).isSameColorAs(0x33ffffff); + } + + @Test + public void respectsContrast() { + final Hct[] seedColors = + new Hct[] { + Hct.fromInt(0xFFFF0000), + Hct.fromInt(0xFFFFFF00), + Hct.fromInt(0xFF00FF00), + Hct.fromInt(0xFF0000FF) + }; + + final double[] contrastLevels = {-1.0, -0.5, 0.0, 0.5, 1.0}; + + for (Hct seedColor : seedColors) { + for (double contrastLevel : contrastLevels) { + for (boolean isDark : new boolean[] {false, true}) { + final DynamicScheme[] schemes = + new DynamicScheme[] { + new SchemeContent(seedColor, isDark, contrastLevel), + new SchemeMonochrome(seedColor, isDark, contrastLevel), + new SchemeTonalSpot(seedColor, isDark, contrastLevel), + new SchemeFidelity(seedColor, isDark, contrastLevel) + }; + for (final DynamicScheme scheme : schemes) { + assertTrue( + pairSatisfiesContrast(scheme, dynamicColors.onPrimary(), dynamicColors.primary())); + assertTrue( + pairSatisfiesContrast( + scheme, dynamicColors.onPrimaryContainer(), dynamicColors.primaryContainer())); + assertTrue( + pairSatisfiesContrast( + scheme, dynamicColors.onSecondary(), dynamicColors.secondary())); + assertTrue( + pairSatisfiesContrast( + scheme, + dynamicColors.onSecondaryContainer(), + dynamicColors.secondaryContainer())); + assertTrue( + pairSatisfiesContrast( + scheme, dynamicColors.onTertiary(), dynamicColors.tertiary())); + assertTrue( + pairSatisfiesContrast( + scheme, + dynamicColors.onTertiaryContainer(), + dynamicColors.tertiaryContainer())); + assertTrue( + pairSatisfiesContrast(scheme, dynamicColors.onError(), dynamicColors.error())); + assertTrue( + pairSatisfiesContrast( + scheme, dynamicColors.onErrorContainer(), dynamicColors.errorContainer())); + assertTrue( + pairSatisfiesContrast( + scheme, dynamicColors.onBackground(), dynamicColors.background())); + assertTrue( + pairSatisfiesContrast( + scheme, dynamicColors.onSurfaceVariant(), dynamicColors.surfaceVariant())); + assertTrue( + pairSatisfiesContrast( + scheme, dynamicColors.inverseOnSurface(), dynamicColors.inverseSurface())); + } } + } } - - @Test - public void valuesAreCorrect() { - // Checks that the values of certain dynamic colors match Dart results. - assertThat( - MaterialDynamicColors.onPrimaryContainer.getArgb( - new SchemeFidelity(Hct.fromInt(0xFFFF0000), false, 0.5))) - .isSameColorAs(0xFFFFE5E1); - assertThat( - MaterialDynamicColors.onSecondaryContainer.getArgb( - new SchemeContent(Hct.fromInt(0xFF0000FF), false, 0.5))) - .isSameColorAs(0xFFFFFCFF); - assertThat( - MaterialDynamicColors.onTertiaryContainer.getArgb( - new SchemeContent(Hct.fromInt(0xFFFFFF00), true, -0.5))) - .isSameColorAs(0xFF616600); - assertThat( - MaterialDynamicColors.surfaceInverse.getArgb( - new SchemeContent(Hct.fromInt(0xFF0000FF), false, 0.0))) - .isSameColorAs(0xFF464652); - assertThat( - MaterialDynamicColors.primaryInverse.getArgb( - new SchemeContent(Hct.fromInt(0xFFFF0000), false, -0.5))) - .isSameColorAs(0xFFFF8C7A); - assertThat( - MaterialDynamicColors.outlineVariant.getArgb( - new SchemeContent(Hct.fromInt(0xFFFFFF00), true, 0.0))) - .isSameColorAs(0xFF484831); - } - - private boolean pairSatisfiesContrast(DynamicScheme scheme, DynamicColor fg, DynamicColor bg) { - double fgTone = fg.getHct(scheme).getTone(); - double bgTone = bg.getHct(scheme).getTone(); - double minimumRequirement = scheme.contrastLevel >= 0.0 ? 4.5 : 3.0; - return Contrast.ratioOfTones(fgTone, bgTone) >= minimumRequirement; - } + } + + @Test + public void valuesAreCorrect() { + // Checks that the values of certain dynamic colors match Dart results. + assertThat( + dynamicColors + .onPrimaryContainer() + .getArgb(new SchemeFidelity(Hct.fromInt(0xFFFF0000), false, 0.5))) + .isSameColorAs(0xFFFFE5E1); + assertThat( + dynamicColors + .onSecondaryContainer() + .getArgb(new SchemeContent(Hct.fromInt(0xFF0000FF), false, 0.5))) + .isSameColorAs(0xFFFFFCFF); + assertThat( + dynamicColors + .onTertiaryContainer() + .getArgb(new SchemeContent(Hct.fromInt(0xFFFFFF00), true, -0.5))) + .isSameColorAs(0xFF616600); + assertThat( + dynamicColors + .inverseSurface() + .getArgb(new SchemeContent(Hct.fromInt(0xFF0000FF), false, 0.0))) + .isSameColorAs(0xFF2F2F3B); + assertThat( + dynamicColors + .inversePrimary() + .getArgb(new SchemeContent(Hct.fromInt(0xFFFF0000), false, -0.5))) + .isSameColorAs(0xFFFF907F); + assertThat( + dynamicColors + .outlineVariant() + .getArgb(new SchemeContent(Hct.fromInt(0xFFFFFF00), true, 0.0))) + .isSameColorAs(0xFF484831); + } + + private boolean pairSatisfiesContrast(DynamicScheme scheme, DynamicColor fg, DynamicColor bg) { + double fgTone = fg.getHct(scheme).getTone(); + double bgTone = bg.getHct(scheme).getTone(); + double minimumRequirement = scheme.contrastLevel >= 0.0 ? 4.5 : 3.0; + return Contrast.ratioOfTones(fgTone, bgTone) >= minimumRequirement; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeContentTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeContentTest.java index 1ddfc4d4af5b..704aed81e9c4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeContentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeContentTest.java @@ -25,246 +25,229 @@ import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors; import com.android.systemui.monet.hct.Hct; import com.android.systemui.monet.scheme.SchemeContent; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +@Ignore("b/279581953") @SmallTest @RunWith(JUnit4.class) public final class SchemeContentTest extends SysuiTestCase { + private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors(); + @Test public void testKeyColors() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff080CFF); - assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff656DD3); - assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff81009F); - assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff767684); - assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff757589); } @Test public void lightTheme_minContrast_primary() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xFF1218FF); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xFF1218FF); } @Test public void lightTheme_standardContrast_primary() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xFF0001C3); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xFF0001C3); } @Test public void lightTheme_maxContrast_primary() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xFF000181); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xFF000181); } @Test public void lightTheme_minContrast_primaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xFF5660FF); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xFF5660FF); } @Test public void lightTheme_standardContrast_primaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xFF2D36FF); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xFF2D36FF); } @Test public void lightTheme_maxContrast_primaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xFF0000E3); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xFF0000E3); } @Test public void lightTheme_minContrast_tertiaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, -1.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xFFB042CC); + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xFFB042CC); } @Test public void lightTheme_standardContrast_tertiaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 0.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xFF9221AF); + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xFF9221AF); } @Test public void lightTheme_maxContrast_tertiaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 1.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xFF73008E); + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xFF73008E); } @Test public void lightTheme_minContrast_objectionableTertiaryContainerLightens() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF850096), false, -1.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xFFD03A71); + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xFFD03A71); } @Test public void lightTheme_standardContrast_objectionableTertiaryContainerLightens() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF850096), false, 0.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xFFAC1B57); + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xFFAC1B57); } @Test public void lightTheme_maxContrast_objectionableTertiaryContainerDarkens() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF850096), false, 1.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xFF870040); + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xFF870040); } @Test public void lightTheme_minContrast_onPrimaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xFFCBCDFF); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xFFCBCDFF); } @Test public void lightTheme_standardContrast_onPrimaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xFFCECFFF); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xFFCECFFF); } @Test public void lightTheme_maxContrast_onPrimaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xFFD6D6FF); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xFFD6D6FF); } @Test public void lightTheme_minContrast_surface() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, -1); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xFFFBF8FF); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xFFFBF8FF); } @Test public void lightTheme_standardContrast_surface() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xFFFBF8FF); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xFFFBF8FF); } @Test public void lightTheme_maxContrast_surface() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xFFFBF8FF); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xFFFBF8FF); } @Test public void darkTheme_minContrast_primary() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xFF5660FF); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xFF5660FF); } @Test public void darkTheme_standardContrast_primary() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xFFBEC2FF); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xFFBEC2FF); } @Test public void darkTheme_maxContrast_primary() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xFFF6F4FF); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xFFF6F4FF); } @Test public void darkTheme_minContrast_primaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xFF0000E6); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xFF0000E6); } @Test public void darkTheme_standardContrast_primaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xFF0000E6); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xFF0000E6); } @Test public void darkTheme_maxContrast_primaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xFFC4C6FF); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xFFC4C6FF); } @Test public void darkTheme_minContrast_onPrimaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xFF7A83FF); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xFF7A83FF); } @Test public void darkTheme_standardContrast_onPrimaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xFFA4AAFF); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xFFA4AAFF); } @Test public void darkTheme_maxContrast_onPrimaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xFF0001C6); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xFF0001C6); } @Test public void darkTheme_minContrast_onTertiaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xFFCF60EA); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xFFCF60EA); } @Test public void darkTheme_standardContrast_onTertiaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xFFEB8CFF); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xFFEB8CFF); } @Test public void darkTheme_maxContrast_onTertiaryContainer() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xFF63007B); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xFF63007B); } @Test public void darkTheme_minContrast_surface() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, -1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xFF12121D); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xFF12121D); } @Test public void darkTheme_standardContrast_surface() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xFF12121D); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xFF12121D); } @Test public void darkTheme_maxContrast_surface() { SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xFF12121D); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xFF12121D); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeExpressiveTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeExpressiveTest.java index 31e8711c6d8c..32a589d0ea6e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeExpressiveTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeExpressiveTest.java @@ -25,183 +25,175 @@ import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors; import com.android.systemui.monet.hct.Hct; import com.android.systemui.monet.scheme.SchemeExpressive; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +@Ignore("b/279581953") @SmallTest @RunWith(JUnit4.class) public final class SchemeExpressiveTest extends SysuiTestCase { + private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors(); + @Test public void testKeyColors() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff35855F); - assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff8C6D8C); - assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff806EA1); - assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff79757F); - assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff7A7585); } @Test public void lightTheme_minContrast_primary() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff32835D); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffad603c); } @Test public void lightTheme_standardContrast_primary() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff146C48); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff924b28); } @Test public void lightTheme_maxContrast_primary() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff002818); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff401400); } @Test public void lightTheme_minContrast_primaryContainer() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xffA2F4C6); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffffdbcc); } @Test public void lightTheme_standardContrast_primaryContainer() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xffA2F4C6); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffffdbcc); } @Test public void lightTheme_maxContrast_primaryContainer() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff004D31); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff6f3010); } @Test public void lightTheme_minContrast_onPrimaryContainer() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff1e724e); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff99512e); } @Test public void lightTheme_standardContrast_onPrimaryContainer() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff002112); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff351000); } @Test public void lightTheme_maxContrast_onPrimaryContainer() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff9aebbe); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffffd0bc); } @Test public void lightTheme_minContrast_surface() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffdf7ff); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff); } @Test public void lightTheme_standardContrast_surface() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffdf7ff); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff); } @Test public void lightTheme_maxContrast_surface() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffdf7ff); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff); } @Test public void darkTheme_minContrast_primary() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff32835d); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffad603c); } @Test public void darkTheme_standardContrast_primary() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff87d7ab); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffffb595); } @Test public void darkTheme_maxContrast_primary() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xffd5ffe4); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xfffff3ee); } @Test public void darkTheme_minContrast_primaryContainer() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff005234); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff743413); } @Test public void darkTheme_standardContrast_primaryContainer() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff005234); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff743413); } @Test public void darkTheme_maxContrast_primaryContainer() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff8bdbaf); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffffbb9e); } @Test public void darkTheme_minContrast_onPrimaryContainer() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff76c59b); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xfff99f75); } @Test public void darkTheme_standardContrast_onPrimaryContainer() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xffa2f4c6); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffffdbcc); } @Test public void darkTheme_maxContrast_onPrimaryContainer() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff004229); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff622706); } @Test public void darkTheme_minContrast_surface() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff14121a); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131a); } @Test public void darkTheme_standardContrast_surface() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff14121a); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131a); } @Test public void darkTheme_maxContrast_surface() { SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff14121a); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131a); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeFidelityTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeFidelityTest.java index 511f83bb0140..6844a92185e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeFidelityTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeFidelityTest.java @@ -25,245 +25,230 @@ import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors; import com.android.systemui.monet.hct.Hct; import com.android.systemui.monet.scheme.SchemeFidelity; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +@Ignore("b/279581953") @SmallTest @RunWith(JUnit4.class) public final class SchemeFidelityTest extends SysuiTestCase { + + private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors(); + @Test public void testKeyColors() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff080CFF); - assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff656DD3); - assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff9D0002); - assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff767684); - assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff757589); } @Test public void lightTheme_minContrast_primary() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff1218ff); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff1218ff); } @Test public void lightTheme_standardContrast_primary() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff0001c3); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff0001c3); } @Test public void lightTheme_maxContrast_primary() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff000181); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff000181); } @Test public void lightTheme_minContrast_primaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff5660ff); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff5660ff); } @Test public void lightTheme_standardContrast_primaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff2d36ff); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff2d36ff); } @Test public void lightTheme_maxContrast_primaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff0000e3); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff0000e3); } @Test public void lightTheme_minContrast_tertiaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xffd93628); + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xffd93628); } @Test public void lightTheme_standardContrast_tertiaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xffb31910); + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xffb31910); } @Test public void lightTheme_maxContrast_tertiaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xff8c0002); + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xff8c0002); } @Test public void lightTheme_minContrast_objectionableTertiaryContainerLightens() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff850096), false, -1.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xffbcac5a); + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xffbcac5a); } @Test public void lightTheme_standardContrast_objectionableTertiaryContainerLightens() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff850096), false, 0.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xffbcac5a); + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xffbcac5a); } @Test public void lightTheme_maxContrast_objectionableTertiaryContainerDarkens() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff850096), false, 1.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xff4d4300); + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xff4d4300); } @Test public void lightTheme_minContrast_onPrimaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xffcbcdff); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffcbcdff); } @Test public void lightTheme_standardContrast_onPrimaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xffcecfff); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffcecfff); } @Test public void lightTheme_maxContrast_onPrimaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xffd6d6ff); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffd6d6ff); } @Test public void lightTheme_minContrast_surface() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff); } @Test public void lightTheme_standardContrast_surface() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff); } @Test public void lightTheme_maxContrast_surface() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff); } @Test public void darkTheme_minContrast_primary() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff5660ff); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff5660ff); } @Test public void darkTheme_standardContrast_primary() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xffbec2ff); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffbec2ff); } @Test public void darkTheme_maxContrast_primary() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xfff6f4ff); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xfff6f4ff); } @Test public void darkTheme_minContrast_primaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff0000e6); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff0000e6); } @Test public void darkTheme_standardContrast_primaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff0000e6); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff0000e6); } @Test public void darkTheme_maxContrast_primaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xffc4c6ff); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffc4c6ff); } @Test public void darkTheme_minContrast_onPrimaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff7a83ff); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff7a83ff); } @Test public void darkTheme_standardContrast_onPrimaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xffa4aaff); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffa4aaff); } @Test public void darkTheme_maxContrast_onPrimaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff0001c6); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff0001c6); } @Test public void darkTheme_minContrast_onTertiaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xfffe513e); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xfffe513e); } @Test public void darkTheme_standardContrast_onTertiaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xffFF9181); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xffFF9181); } @Test public void darkTheme_maxContrast_onTertiaryContainer() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xff790001); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xff790001); } @Test public void darkTheme_minContrast_surface() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12121d); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12121d); } @Test public void darkTheme_standardContrast_surface() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12121d); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12121d); } @Test public void darkTheme_maxContrast_surface() { SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12121d); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12121d); } + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeFruitSaladTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeFruitSaladTest.java index 4cf14d5455d3..4bca2c4f08f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeFruitSaladTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeFruitSaladTest.java @@ -25,226 +25,230 @@ import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors; import com.android.systemui.monet.hct.Hct; import com.android.systemui.monet.scheme.SchemeFruitSalad; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +@Ignore("b/279581953") @SmallTest @RunWith(JUnit4.class) public final class SchemeFruitSaladTest extends SysuiTestCase { + private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors(); + @Test public void testKeyColors() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff0091C0); - assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff3A7E9E); - assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff6E72AC); - assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff777682); - assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff75758B); } @Test public void lightTheme_minContrast_primary() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff007ea7); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff007ea7); } @Test public void lightTheme_standardContrast_primary() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff006688); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff006688); } @Test public void lightTheme_maxContrast_primary() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff002635); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff002635); } @Test public void lightTheme_minContrast_primaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs( 0xffC2E8FF); } @Test public void lightTheme_standardContrast_primaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs( 0xffC2E8FF); } @Test public void lightTheme_maxContrast_primaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs( 0xff004862); } @Test public void lightTheme_minContrast_tertiaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs( 0xffE0E0FF); } @Test public void lightTheme_standardContrast_tertiaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs( 0xffE0E0FF); } @Test public void lightTheme_maxContrast_tertiaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs( 0xFF3A3E74); } @Test public void lightTheme_minContrast_onPrimaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs( 0xff006C90); } @Test public void lightTheme_standardContrast_onPrimaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs( 0xff001E2B); } @Test public void lightTheme_maxContrast_onPrimaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs( 0xffACE1FF); } @Test public void lightTheme_minContrast_surface() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff); } @Test public void lightTheme_standardContrast_surface() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff); } @Test public void lightTheme_maxContrast_surface() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff); } @Test public void darkTheme_minContrast_primary() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff007EA7); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff007EA7); } @Test public void darkTheme_standardContrast_primary() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xFF76D1FF); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xFF76D1FF); } @Test public void darkTheme_maxContrast_primary() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xFFECF7FF); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xFFECF7FF); } @Test public void darkTheme_minContrast_primaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs( 0xFF004D67); } @Test public void darkTheme_standardContrast_primaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs( 0xFF004D67); } @Test public void darkTheme_maxContrast_primaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs( 0xFF83D5FF); } @Test public void darkTheme_minContrast_onPrimaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs( 0xff55C0F2); } @Test public void darkTheme_standardContrast_onPrimaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs( 0xffC2E8FF); } @Test public void darkTheme_maxContrast_onPrimaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs( 0xff003E54); } @Test public void darkTheme_minContrast_onTertiaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs( 0xffADB0EF); } @Test public void darkTheme_standardContrast_onTertiaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs( 0xffe0e0ff); } @Test public void darkTheme_maxContrast_onTertiaryContainer() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs( 0xff30346A); } @Test public void darkTheme_minContrast_surface() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12131c); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131c); } @Test public void darkTheme_standardContrast_surface() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12131c); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131c); } @Test public void darkTheme_maxContrast_surface() { SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12131c); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131c); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeMonochromeTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeMonochromeTest.java index 3eca4dcf7baa..d5114222c2a0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeMonochromeTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeMonochromeTest.java @@ -16,6 +16,7 @@ package com.android.systemui.monet; +import static com.google.common.truth.Truth.assertThat; import static com.android.systemui.monet.utils.ArgbSubject.assertThat; import androidx.test.filters.SmallTest; @@ -25,204 +26,227 @@ import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors; import com.android.systemui.monet.hct.Hct; import com.android.systemui.monet.scheme.SchemeMonochrome; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +@Ignore("b/279581953") @SmallTest @RunWith(JUnit4.class) public final class SchemeMonochromeTest extends SysuiTestCase { + private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors(); + @Test public void testKeyColors() { SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff070707); - assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff070707); - assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff070707); - assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff070707); - assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff070707); } @Test public void lightTheme_minContrast_primary() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff747474); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, -1); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff3c3c3c); } @Test public void lightTheme_standardContrast_primary() { SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff5e5e5e); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff000000); } @Test public void lightTheme_maxContrast_primary() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff222222); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 1); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff000000); } @Test public void lightTheme_minContrast_primaryContainer() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xffe2e2e2); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, -1); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff5f5f5f); } @Test public void lightTheme_standardContrast_primaryContainer() { SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xffe2e2e2); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff3b3b3b); } @Test public void lightTheme_maxContrast_primaryContainer() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff434343); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 1); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff3a3a3a); } @Test public void lightTheme_minContrast_onPrimaryContainer() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff646464); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, -1); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffd9d9d9); } @Test public void lightTheme_standardContrast_onPrimaryContainer() { SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff1b1b1b); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffffffff); } @Test public void lightTheme_maxContrast_onPrimaryContainer() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xffdadada); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 1); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffcdcdcd); } @Test public void lightTheme_minContrast_surface() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfff9f9f9); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, -1); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfff9f9f9); } @Test public void lightTheme_standardContrast_surface() { SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfff9f9f9); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfff9f9f9); } @Test public void lightTheme_maxContrast_surface() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfff9f9f9); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 1); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfff9f9f9); } @Test public void darkTheme_minContrast_primary() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff747474); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffcccccc); } @Test public void darkTheme_standardContrast_primary() { SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xffc6c6c6); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffffffff); } @Test public void darkTheme_maxContrast_primary() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xfff5f5f5); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffffffff); } @Test public void darkTheme_minContrast_primaryContainer() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff474747); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffa3a3a3); } @Test public void darkTheme_standardContrast_primaryContainer() { SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff474747); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffd4d4d4); } @Test public void darkTheme_maxContrast_primaryContainer() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xffcbcbcb); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffd5d5d5); } @Test public void darkTheme_minContrast_onPrimaryContainer() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xffb5b5b5); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff393939); } @Test public void darkTheme_standardContrast_onPrimaryContainer() { SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xffe2e2e2); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff000000); } @Test public void darkTheme_maxContrast_onPrimaryContainer() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff393939); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff404040); } @Test public void darkTheme_minContrast_onTertiaryContainer() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xffb5b5b5); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xffd1d1d1); } @Test public void darkTheme_standardContrast_onTertiaryContainer() { SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xffe2e2e2); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xff000000); } @Test public void darkTheme_maxContrast_onTertiaryContainer() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xff393939); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xff393939); } @Test public void darkTheme_minContrast_surface() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131313); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131313); } @Test public void darkTheme_standardContrast_surface() { SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131313); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131313); } @Test public void darkTheme_maxContrast_surface() { - SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131313); + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131313); + } + + @Test + public void darkTheme_monochromeSpec() { + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 0.0); + assertThat(dynamicColors.primary().getHct(scheme).getTone()).isWithin(1).of(100); + assertThat(dynamicColors.onPrimary().getHct(scheme).getTone()).isWithin(1).of(10); + assertThat(dynamicColors.primaryContainer().getHct(scheme).getTone()).isWithin(1).of(85); + assertThat(dynamicColors.onPrimaryContainer().getHct(scheme).getTone()).isWithin(1).of(0); + assertThat(dynamicColors.secondary().getHct(scheme).getTone()).isWithin(1).of(80); + assertThat(dynamicColors.onSecondary().getHct(scheme).getTone()).isWithin(1).of(10); + assertThat(dynamicColors.secondaryContainer().getHct(scheme).getTone()).isWithin(1).of(30); + assertThat(dynamicColors.onSecondaryContainer().getHct(scheme).getTone()).isWithin(1).of(90); + assertThat(dynamicColors.tertiary().getHct(scheme).getTone()).isWithin(1).of(90); + assertThat(dynamicColors.onTertiary().getHct(scheme).getTone()).isWithin(1).of(10); + assertThat(dynamicColors.tertiaryContainer().getHct(scheme).getTone()).isWithin(1).of(60); + assertThat(dynamicColors.onTertiaryContainer().getHct(scheme).getTone()).isWithin(1).of(0); + } + + @Test + public void lightTheme_monochromeSpec() { + SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 0.0); + assertThat(dynamicColors.primary().getHct(scheme).getTone()).isWithin(1).of(0); + assertThat(dynamicColors.onPrimary().getHct(scheme).getTone()).isWithin(1).of(90); + assertThat(dynamicColors.primaryContainer().getHct(scheme).getTone()).isWithin(1).of(25); + assertThat(dynamicColors.onPrimaryContainer().getHct(scheme).getTone()).isWithin(1).of(100); + assertThat(dynamicColors.secondary().getHct(scheme).getTone()).isWithin(1).of(40); + assertThat(dynamicColors.onSecondary().getHct(scheme).getTone()).isWithin(1).of(100); + assertThat(dynamicColors.secondaryContainer().getHct(scheme).getTone()).isWithin(1).of(85); + assertThat(dynamicColors.onSecondaryContainer().getHct(scheme).getTone()).isWithin(1).of(10); + assertThat(dynamicColors.tertiary().getHct(scheme).getTone()).isWithin(1).of(25); + assertThat(dynamicColors.onTertiary().getHct(scheme).getTone()).isWithin(1).of(90); + assertThat(dynamicColors.tertiaryContainer().getHct(scheme).getTone()).isWithin(1).of(49); + assertThat(dynamicColors.onTertiaryContainer().getHct(scheme).getTone()).isWithin(1).of(100); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeNeutralTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeNeutralTest.java index 71e9f4da79cb..4f3fc7d50b61 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeNeutralTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeNeutralTest.java @@ -25,204 +25,193 @@ import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors; import com.android.systemui.monet.hct.Hct; import com.android.systemui.monet.scheme.SchemeNeutral; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +@Ignore("b/279581953") @SmallTest @RunWith(JUnit4.class) public final class SchemeNeutralTest extends SysuiTestCase { + private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors(); + @Test public void testKeyColors() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff767685); - assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff777680); - assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff75758B); - assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff787678); - assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff787678); } @Test public void lightTheme_minContrast_primary() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff737383); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff737383); } @Test public void lightTheme_standardContrast_primary() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff5d5d6c); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff5d5d6c); } @Test public void lightTheme_maxContrast_primary() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff21212e); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff21212e); } @Test public void lightTheme_minContrast_primaryContainer() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xffe2e1f3); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffe2e1f3); } @Test public void lightTheme_standardContrast_primaryContainer() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xffe2e1f3); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffe2e1f3); } @Test public void lightTheme_maxContrast_primaryContainer() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff414250); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff414250); } @Test public void lightTheme_minContrast_onPrimaryContainer() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff636372); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff636372); } @Test public void lightTheme_standardContrast_onPrimaryContainer() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff1a1b27); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff1a1b27); } @Test public void lightTheme_maxContrast_onPrimaryContainer() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xffd9d8ea); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffd9d8ea); } @Test public void lightTheme_minContrast_surface() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffcf8fa); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffcf8fa); } @Test public void lightTheme_standardContrast_surface() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffcf8fa); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffcf8fa); } @Test public void lightTheme_maxContrast_surface() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffcf8fa); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffcf8fa); } @Test public void darkTheme_minContrast_primary() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff737383); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff737383); } @Test public void darkTheme_standardContrast_primary() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xffc6c5d6); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffc6c5d6); } @Test public void darkTheme_maxContrast_primary() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xfff6f4ff); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xfff6f4ff); } @Test public void darkTheme_minContrast_primaryContainer() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff454654); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff454654); } @Test public void darkTheme_standardContrast_primaryContainer() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff454654); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff454654); } @Test public void darkTheme_maxContrast_primaryContainer() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xffcac9da); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffcac9da); } @Test public void darkTheme_minContrast_onPrimaryContainer() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xffb5b3c4); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffb5b3c4); } @Test public void darkTheme_standardContrast_onPrimaryContainer() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xffe2e1f3); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffe2e1f3); } @Test public void darkTheme_maxContrast_onPrimaryContainer() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff373846); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff373846); } @Test public void darkTheme_minContrast_onTertiaryContainer() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xffb3b3cb); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xffb3b3cb); } @Test public void darkTheme_standardContrast_onTertiaryContainer() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xffe1e0f9); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xffe1e0f9); } @Test public void darkTheme_maxContrast_onTertiaryContainer() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xff37374b); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xff37374b); } @Test public void darkTheme_minContrast_surface() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131315); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131315); } @Test public void darkTheme_standardContrast_surface() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131315); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131315); } @Test public void darkTheme_maxContrast_surface() { SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131315); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131315); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeRainbowTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeRainbowTest.java index cc6f044eb09c..ece3f9a16c48 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeRainbowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeRainbowTest.java @@ -25,225 +25,229 @@ import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors; import com.android.systemui.monet.hct.Hct; import com.android.systemui.monet.scheme.SchemeRainbow; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +@Ignore("b/279581953") @SmallTest @RunWith(JUnit4.class) public final class SchemeRainbowTest extends SysuiTestCase { + private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors(); + @Test public void testKeyColors() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff696FC4); - assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff75758B); - assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff936B84); - assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff070707); - assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff070707); } @Test public void lightTheme_minContrast_primary() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff676DC1); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff676DC1); } @Test public void lightTheme_standardContrast_primary() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff5056A9); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff5056A9); } @Test public void lightTheme_maxContrast_primary() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff0F136A); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff0F136A); } @Test public void lightTheme_minContrast_primaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs( 0xffE0E0FF); } @Test public void lightTheme_standardContrast_primaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs( 0xffE0E0FF); } @Test public void lightTheme_maxContrast_primaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs( 0xff34398B); } @Test public void lightTheme_minContrast_tertiaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs( 0xffffd8ee); } @Test public void lightTheme_standardContrast_tertiaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs( 0xffffd8ee); } @Test public void lightTheme_maxContrast_tertiaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs( 0xff5A384E); } @Test public void lightTheme_minContrast_onPrimaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs( 0xff565CB0); } @Test public void lightTheme_standardContrast_onPrimaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs( 0xff050865); } @Test public void lightTheme_maxContrast_onPrimaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs( 0xffd6d6ff); } @Test public void lightTheme_minContrast_surface() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfff9f9f9); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfff9f9f9); } @Test public void lightTheme_standardContrast_surface() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfff9f9f9); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfff9f9f9); } @Test public void lightTheme_maxContrast_surface() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfff9f9f9); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfff9f9f9); } @Test public void darkTheme_minContrast_primary() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff676DC1); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff676DC1); } @Test public void darkTheme_standardContrast_primary() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xffbec2ff); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffbec2ff); } @Test public void darkTheme_maxContrast_primary() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xfff6f4ff); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xfff6f4ff); } @Test public void darkTheme_minContrast_primaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs( 0xff383E8F); } @Test public void darkTheme_standardContrast_primaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs( 0xff383E8F); } @Test public void darkTheme_maxContrast_primaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs( 0xffc4c6ff); } @Test public void darkTheme_minContrast_onPrimaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs( 0xffa9afff); } @Test public void darkTheme_standardContrast_onPrimaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs( 0xffe0e0ff); } @Test public void darkTheme_maxContrast_onPrimaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs( 0xff292f81); } @Test public void darkTheme_minContrast_onTertiaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs( 0xffd5a8c3); } @Test public void darkTheme_standardContrast_onTertiaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs( 0xffffd8ee); } @Test public void darkTheme_maxContrast_onTertiaryContainer() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs( 0xff4f2e44); } @Test public void darkTheme_minContrast_surface() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131313); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131313); } @Test public void darkTheme_standardContrast_surface() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131313); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131313); } @Test public void darkTheme_maxContrast_surface() { SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131313); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131313); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeTonalSpotTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeTonalSpotTest.java index e4880d9df858..01d199b02f26 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeTonalSpotTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeTonalSpotTest.java @@ -25,348 +25,338 @@ import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors; import com.android.systemui.monet.hct.Hct; import com.android.systemui.monet.scheme.SchemeTonalSpot; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +@Ignore("b/279581953") @SmallTest @RunWith(JUnit4.class) public final class SchemeTonalSpotTest extends SysuiTestCase { + private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors(); + @Test public void testKeyColors() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff6E72AC); - assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff75758B); - assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff936B84); - assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff78767A); - assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff777680); } + @Test public void lightTheme_minContrast_primary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff6c70aa); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff6a6fb1); } @Test public void lightTheme_standardContrast_primary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff555992); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff545999); } @Test public void lightTheme_maxContrast_primary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff181c51); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff161a59); } @Test public void lightTheme_minContrast_primaryContainer() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xffe0e0ff); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffe0e0ff); } @Test public void lightTheme_standardContrast_primaryContainer() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xffe0e0ff); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffe0e0ff); } @Test public void lightTheme_maxContrast_primaryContainer() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff3a3e74); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff383c7c); } @Test public void lightTheme_minContrast_onPrimaryContainer() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff5C5F98); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff5a5fa0); } @Test public void lightTheme_standardContrast_onPrimaryContainer() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff11144B); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff0e1253); } @Test public void lightTheme_maxContrast_onPrimaryContainer() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xffd6d6ff); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffd6d6ff); } @Test public void lightTheme_minContrast_surface() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xffFCF8FD); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff); } @Test public void lightTheme_standardContrast_surface() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xffFCF8FD); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff); } @Test public void lightTheme_maxContrast_surface() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xffFCF8FD); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff); } @Test public void lightTheme_minContrast_onSurface() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.onSurface.getArgb(scheme)).isSameColorAs(0xff605E62); + assertThat(dynamicColors.onSurface().getArgb(scheme)).isSameColorAs(0xff5f5e65); } @Test public void lightTheme_standardContrast_onSurface() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.onSurface.getArgb(scheme)).isSameColorAs(0xff1B1B1F); + assertThat(dynamicColors.onSurface().getArgb(scheme)).isSameColorAs(0xff1b1b21); } @Test public void lightTheme_maxContrast_onSurface() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.onSurface.getArgb(scheme)).isSameColorAs(0xff1B1A1E); + assertThat(dynamicColors.onSurface().getArgb(scheme)).isSameColorAs(0xff1a1a20); } @Test public void lightTheme_minContrast_onSecondary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.onSecondary.getArgb(scheme)).isSameColorAs(0xffcfcfe7); + assertThat(dynamicColors.onSecondary().getArgb(scheme)).isSameColorAs(0xffcfcfe7); } @Test public void lightTheme_standardContrast_onSecondary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.onSecondary.getArgb(scheme)).isSameColorAs(0xffffffff); + assertThat(dynamicColors.onSecondary().getArgb(scheme)).isSameColorAs(0xffffffff); } @Test public void lightTheme_maxContrast_onSecondary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.onSecondary.getArgb(scheme)).isSameColorAs(0xffababc3); + assertThat(dynamicColors.onSecondary().getArgb(scheme)).isSameColorAs(0xffababc3); } @Test public void lightTheme_minContrast_onTertiary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.onTertiary.getArgb(scheme)).isSameColorAs(0xfff3c3df); + assertThat(dynamicColors.onTertiary().getArgb(scheme)).isSameColorAs(0xfff3c3df); } @Test public void lightTheme_standardContrast_onTertiary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.onTertiary.getArgb(scheme)).isSameColorAs(0xffffffff); + assertThat(dynamicColors.onTertiary().getArgb(scheme)).isSameColorAs(0xffffffff); } @Test public void lightTheme_maxContrast_onTertiary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.onTertiary.getArgb(scheme)).isSameColorAs(0xffcda0bb); + assertThat(dynamicColors.onTertiary().getArgb(scheme)).isSameColorAs(0xffcda0bb); } @Test public void lightTheme_minContrast_onError() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.onError.getArgb(scheme)).isSameColorAs(0xffffc2bb); + assertThat(dynamicColors.onError().getArgb(scheme)).isSameColorAs(0xffffc2bb); } @Test public void lightTheme_standardContrast_onError() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.onError.getArgb(scheme)).isSameColorAs(0xffffffff); + assertThat(dynamicColors.onError().getArgb(scheme)).isSameColorAs(0xffffffff); } @Test public void lightTheme_maxContrast_onError() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.onError.getArgb(scheme)).isSameColorAs(0xffff8d80); + assertThat(dynamicColors.onError().getArgb(scheme)).isSameColorAs(0xffff8d80); } @Test public void darkTheme_minContrast_primary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff6C70AA); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff6a6fb1); } @Test public void darkTheme_standardContrast_primary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xffbec2ff); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffbec2ff); } @Test public void darkTheme_maxContrast_primary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xfff6f4ff); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xfff6f4ff); } @Test public void darkTheme_minContrast_primaryContainer() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff3E4278); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff3c4180); } @Test public void darkTheme_standardContrast_primaryContainer() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff3E4278); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff3c4180); } @Test public void darkTheme_maxContrast_primaryContainer() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xffc4c6ff); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffc4c6ff); } @Test public void darkTheme_minContrast_onPrimaryContainer() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xffadb0ef); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffabaff7); } @Test public void darkTheme_standardContrast_onPrimaryContainer() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xffe0e0ff); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffe0e0ff); } @Test public void darkTheme_maxContrast_onPrimaryContainer() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff30346A); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff2e3271); } @Test public void darkTheme_minContrast_onTertiaryContainer() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xffd5a8c3); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xffd5a8c3); } @Test public void darkTheme_standardContrast_onTertiaryContainer() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xffffd8ee); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xffffd8ee); } @Test public void darkTheme_maxContrast_onTertiaryContainer() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xff4f2e44); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xff4f2e44); } @Test public void darkTheme_minContrast_onSecondary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onSecondary.getArgb(scheme)).isSameColorAs(0xfffffbff); + assertThat(dynamicColors.onSecondary().getArgb(scheme)).isSameColorAs(0xfffffbff); } @Test public void darkTheme_standardContrast_onSecondary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onSecondary.getArgb(scheme)).isSameColorAs(0xff2e2f42); + assertThat(dynamicColors.onSecondary().getArgb(scheme)).isSameColorAs(0xff2e2f42); } @Test public void darkTheme_maxContrast_onSecondary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onSecondary.getArgb(scheme)).isSameColorAs(0xff505165); + assertThat(dynamicColors.onSecondary().getArgb(scheme)).isSameColorAs(0xff505165); } @Test public void darkTheme_minContrast_onTertiary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onTertiary.getArgb(scheme)).isSameColorAs(0xfffffbff); + assertThat(dynamicColors.onTertiary().getArgb(scheme)).isSameColorAs(0xfffffbff); } @Test public void darkTheme_standardContrast_onTertiary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onTertiary.getArgb(scheme)).isSameColorAs(0xff46263b); + assertThat(dynamicColors.onTertiary().getArgb(scheme)).isSameColorAs(0xff46263b); } @Test public void darkTheme_maxContrast_onTertiary() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onTertiary.getArgb(scheme)).isSameColorAs(0xff6b485f); + assertThat(dynamicColors.onTertiary().getArgb(scheme)).isSameColorAs(0xff6b485f); } @Test public void darkTheme_minContrast_onError() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onError.getArgb(scheme)).isSameColorAs(0xfffffbff); + assertThat(dynamicColors.onError().getArgb(scheme)).isSameColorAs(0xfffffbff); } @Test public void darkTheme_standardContrast_onError() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onError.getArgb(scheme)).isSameColorAs(0xff690005); + assertThat(dynamicColors.onError().getArgb(scheme)).isSameColorAs(0xff690005); } @Test public void darkTheme_maxContrast_onError() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onError.getArgb(scheme)).isSameColorAs(0xffa80710); + assertThat(dynamicColors.onError().getArgb(scheme)).isSameColorAs(0xffa80710); } @Test public void darkTheme_minContrast_surface() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131316); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131318); } @Test public void darkTheme_standardContrast_surface() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131316); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131318); } @Test public void darkTheme_maxContrast_surface() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131316); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131318); } @Test public void darkTheme_minContrast_onSurface() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onSurface.getArgb(scheme)).isSameColorAs(0xffa4a2a6); + assertThat(dynamicColors.onSurface().getArgb(scheme)).isSameColorAs(0xffa4a2a9); } @Test public void darkTheme_standardContrast_onSurface() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onSurface.getArgb(scheme)).isSameColorAs(0xffe5e1e6); + assertThat(dynamicColors.onSurface().getArgb(scheme)).isSameColorAs(0xffe4e1e9); } @Test public void darkTheme_maxContrast_onSurface() { SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onSurface.getArgb(scheme)).isSameColorAs(0xffe6e2e7); + assertThat(dynamicColors.onSurface().getArgb(scheme)).isSameColorAs(0xffe5e2ea); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeVibrantTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeVibrantTest.java index e5963a2f39fe..0fb53eb6066c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeVibrantTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeVibrantTest.java @@ -25,204 +25,193 @@ import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors; import com.android.systemui.monet.hct.Hct; import com.android.systemui.monet.scheme.SchemeVibrant; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +@Ignore("b/279581953") @SmallTest @RunWith(JUnit4.class) public final class SchemeVibrantTest extends SysuiTestCase { + private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors(); + @Test public void testKeyColors() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff080CFF); - assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff7B7296); - assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff886C9D); - assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff777682); - assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme)) + assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme)) .isSameColorAs(0xff767685); } @Test public void lightTheme_minContrast_primary() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff5660ff); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff5660ff); } @Test public void lightTheme_standardContrast_primary() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff343dff); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff343dff); } @Test public void lightTheme_maxContrast_primary() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff000181); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff000181); } @Test public void lightTheme_minContrast_primaryContainer() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xffe0e0ff); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffe0e0ff); } @Test public void lightTheme_standardContrast_primaryContainer() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xffe0e0ff); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffe0e0ff); } @Test public void lightTheme_maxContrast_primaryContainer() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff0000e3); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff0000e3); } @Test public void lightTheme_minContrast_onPrimaryContainer() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff3e47ff); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff3e47ff); } @Test public void lightTheme_standardContrast_onPrimaryContainer() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff00006e); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff00006e); } @Test public void lightTheme_maxContrast_onPrimaryContainer() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xffd6d6ff); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffd6d6ff); } @Test public void lightTheme_minContrast_surface() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, -1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff); } @Test public void lightTheme_standardContrast_surface() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff); } @Test public void lightTheme_maxContrast_surface() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff); } @Test public void darkTheme_minContrast_primary() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff5660ff); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff5660ff); } @Test public void darkTheme_standardContrast_primary() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xffbec2ff); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffbec2ff); } @Test public void darkTheme_maxContrast_primary() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xfff6f4ff); + assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xfff6f4ff); } @Test public void darkTheme_minContrast_primaryContainer() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff0000ef); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff0000ef); } @Test public void darkTheme_standardContrast_primaryContainer() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xff0000ef); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff0000ef); } @Test public void darkTheme_maxContrast_primaryContainer() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs( - 0xffc4c6ff); + assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffc4c6ff); } @Test public void darkTheme_minContrast_onPrimaryContainer() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xffa9afff); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffa9afff); } @Test public void darkTheme_standardContrast_onPrimaryContainer() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xffe0e0ff); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffe0e0ff); } @Test public void darkTheme_maxContrast_onPrimaryContainer() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs( - 0xff0001c6); + assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff0001c6); } @Test public void darkTheme_minContrast_onTertiaryContainer() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xffc9a9df); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xffc9a9df); } @Test public void darkTheme_standardContrast_onTertiaryContainer() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xfff2daff); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xfff2daff); } @Test public void darkTheme_maxContrast_onTertiaryContainer() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs( - 0xff472e5b); + assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xff472e5b); } @Test public void darkTheme_minContrast_surface() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, -1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12131C); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131a); } @Test public void darkTheme_standardContrast_surface() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 0.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12131C); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131a); } @Test public void darkTheme_maxContrast_surface() { SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 1.0); - assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12131C); + assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131a); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt new file mode 100644 index 000000000000..36b913fc3e7b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.notetask + +import android.content.Context +import android.content.Intent +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import androidx.test.rule.ActivityTestRule +import androidx.test.runner.intercepting.SingleActivityFactory +import com.android.dx.mockito.inline.extended.ExtendedMockito.verify +import com.android.systemui.SysuiTestCase +import com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity.Companion.ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE +import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@SmallTest +@TestableLooper.RunWithLooper +class LaunchNotesRoleSettingsTrampolineActivityTest : SysuiTestCase() { + + @Mock lateinit var noteTaskController: NoteTaskController + + @Rule + @JvmField + val activityRule = + ActivityTestRule<LaunchNotesRoleSettingsTrampolineActivity>( + /* activityFactory= */ object : + SingleActivityFactory<LaunchNotesRoleSettingsTrampolineActivity>( + LaunchNotesRoleSettingsTrampolineActivity::class.java + ) { + override fun create(intent: Intent?) = + LaunchNotesRoleSettingsTrampolineActivity(noteTaskController) + }, + /* initialTouchMode= */ false, + /* launchActivity= */ false, + ) + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + } + + @After + fun tearDown() { + activityRule.finishActivity() + } + + @Test + fun startActivity_noAction_shouldLaunchNotesRoleSettingTaskWithNullEntryPoint() { + activityRule.launchActivity(/* startIntent= */ null) + + verify(noteTaskController).startNotesRoleSetting(any(Context::class.java), eq(null)) + } + + @Test + fun startActivity_quickAffordanceAction_shouldLaunchNotesRoleSettingTaskWithQuickAffordanceEntryPoint() { // ktlint-disable max-line-length + activityRule.launchActivity(Intent(ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE)) + + verify(noteTaskController) + .startNotesRoleSetting(any(Context::class.java), eq(QUICK_AFFORDANCE)) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt index 5dbcd33ab0e6..5f897050099a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt @@ -47,6 +47,9 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.notetask.NoteTaskController.Companion.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE import com.android.systemui.notetask.NoteTaskController.Companion.SETTINGS_CREATE_NOTE_TASK_SHORTCUT_COMPONENT import com.android.systemui.notetask.NoteTaskController.Companion.SHORTCUT_ID +import com.android.systemui.notetask.NoteTaskEntryPoint.APP_CLIPS +import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE +import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity import com.android.systemui.notetask.shortcut.LaunchNoteTaskManagedProfileProxyActivity import com.android.systemui.settings.FakeUserTracker @@ -493,7 +496,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { ) .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL) - createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE) + createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE) verifyZeroInteractions(context, bubbles, eventLogger) } @@ -509,7 +512,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { ) .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL) - createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE) + createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE) verifyZeroInteractions(context, bubbles, eventLogger) } @@ -525,7 +528,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { ) .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL) - createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE) + createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE) verifyNoteTaskOpenInBubbleInUser(userTracker.userHandle) } @@ -541,7 +544,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { ) .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL) - createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE) + createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE) verifyNoteTaskOpenInBubbleInUser(userTracker.userHandle) } @@ -553,7 +556,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true) userTracker.set(listOf(mainUserInfo), mainAndWorkProfileUsers.indexOf(mainUserInfo)) - createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE) + createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE) verifyNoteTaskOpenInBubbleInUser(mainUserInfo.userHandle) } @@ -563,7 +566,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true) userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo)) - createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE) + createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE) verifyNoteTaskOpenInBubbleInUser(workUserInfo.userHandle) } @@ -734,6 +737,129 @@ internal class NoteTaskControllerTest : SysuiTestCase() { } // endregion + // region getUserForHandlingNotesTaking + @Test + fun getUserForHandlingNotesTaking_cope_quickAffordance_shouldReturnWorkProfileUser() { + whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true) + userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo)) + + val user = createNoteTaskController().getUserForHandlingNotesTaking(QUICK_AFFORDANCE) + + assertThat(user).isEqualTo(UserHandle.of(workUserInfo.id)) + } + + @Test + fun getUserForHandlingNotesTaking_cope_tailButton_shouldReturnWorkProfileUser() { + whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true) + userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo)) + + val user = createNoteTaskController().getUserForHandlingNotesTaking(TAIL_BUTTON) + + assertThat(user).isEqualTo(UserHandle.of(workUserInfo.id)) + } + + @Test + fun getUserForHandlingNotesTaking_cope_appClip_shouldReturnCurrentUser() { + whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true) + userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo)) + + val user = createNoteTaskController().getUserForHandlingNotesTaking(APP_CLIPS) + + assertThat(user).isEqualTo(UserHandle.of(mainUserInfo.id)) + } + + @Test + fun getUserForHandlingNotesTaking_noManagement_quickAffordance_shouldReturnCurrentUser() { + userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo)) + + val user = createNoteTaskController().getUserForHandlingNotesTaking(QUICK_AFFORDANCE) + + assertThat(user).isEqualTo(UserHandle.of(mainUserInfo.id)) + } + + @Test + fun getUserForHandlingNotesTaking_noManagement_tailButton_shouldReturnCurrentUser() { + userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo)) + + val user = createNoteTaskController().getUserForHandlingNotesTaking(TAIL_BUTTON) + + assertThat(user).isEqualTo(UserHandle.of(mainUserInfo.id)) + } + + @Test + fun getUserForHandlingNotesTaking_noManagement_appClip_shouldReturnCurrentUser() { + userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo)) + + val user = createNoteTaskController().getUserForHandlingNotesTaking(APP_CLIPS) + + assertThat(user).isEqualTo(UserHandle.of(mainUserInfo.id)) + } + // endregion + + // startregion startNotesRoleSetting + @Test + fun startNotesRoleSetting_cope_quickAffordance_shouldStartNoteRoleIntentWithWorkProfileUser() { + whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true) + userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo)) + + createNoteTaskController().startNotesRoleSetting(context, QUICK_AFFORDANCE) + + val intentCaptor = argumentCaptor<Intent>() + val userCaptor = argumentCaptor<UserHandle>() + verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor)) + intentCaptor.value.let { intent -> + assertThat(intent).hasAction(Intent.ACTION_MANAGE_DEFAULT_APP) + } + assertThat(userCaptor.value).isEqualTo(UserHandle.of(workUserInfo.id)) + } + + @Test + fun startNotesRoleSetting_cope_nullEntryPoint_shouldStartNoteRoleIntentWithCurrentUser() { + whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true) + userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo)) + + createNoteTaskController().startNotesRoleSetting(context, entryPoint = null) + + val intentCaptor = argumentCaptor<Intent>() + val userCaptor = argumentCaptor<UserHandle>() + verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor)) + intentCaptor.value.let { intent -> + assertThat(intent).hasAction(Intent.ACTION_MANAGE_DEFAULT_APP) + } + assertThat(userCaptor.value).isEqualTo(UserHandle.of(mainUserInfo.id)) + } + + @Test + fun startNotesRoleSetting_noManagement_quickAffordance_shouldStartNoteRoleIntentWithCurrentUser() { // ktlint-disable max-line-length + userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo)) + + createNoteTaskController().startNotesRoleSetting(context, QUICK_AFFORDANCE) + + val intentCaptor = argumentCaptor<Intent>() + val userCaptor = argumentCaptor<UserHandle>() + verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor)) + intentCaptor.value.let { intent -> + assertThat(intent).hasAction(Intent.ACTION_MANAGE_DEFAULT_APP) + } + assertThat(userCaptor.value).isEqualTo(UserHandle.of(mainUserInfo.id)) + } + + @Test + fun startNotesRoleSetting_noManagement_nullEntryPoint_shouldStartNoteRoleIntentWithCurrentUser() { // ktlint-disable max-line-length + userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo)) + + createNoteTaskController().startNotesRoleSetting(context, entryPoint = null) + + val intentCaptor = argumentCaptor<Intent>() + val userCaptor = argumentCaptor<UserHandle>() + verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor)) + intentCaptor.value.let { intent -> + assertThat(intent).hasAction(Intent.ACTION_MANAGE_DEFAULT_APP) + } + assertThat(userCaptor.value).isEqualTo(UserHandle.of(mainUserInfo.id)) + } + // endregion + private companion object { const val NOTE_TASK_SHORT_LABEL = "Notetaking" const val NOTE_TASK_ACTIVITY_NAME = "NoteTaskActivity" diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt index 42ef2b5ad3ab..452658004733 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt @@ -18,7 +18,12 @@ package com.android.systemui.notetask.quickaffordance +import android.app.role.RoleManager +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import android.content.pm.PackageManager.ApplicationInfoFlags import android.hardware.input.InputSettings +import android.os.UserHandle import android.os.UserManager import android.test.suitebuilder.annotation.SmallTest import android.testing.AndroidTestingRunner @@ -31,11 +36,18 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository +import com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity.Companion.ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE import com.android.systemui.notetask.NoteTaskController import com.android.systemui.notetask.NoteTaskEntryPoint +import com.android.systemui.notetask.NoteTaskInfoResolver +import com.android.systemui.shared.customization.data.content.CustomizationProviderContract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR import com.android.systemui.stylus.StylusManager +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow @@ -45,6 +57,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.anyString import org.mockito.Mockito.verify import org.mockito.MockitoSession import org.mockito.quality.Strictness @@ -58,6 +71,8 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { @Mock lateinit var stylusManager: StylusManager @Mock lateinit var repository: KeyguardQuickAffordanceRepository @Mock lateinit var userManager: UserManager + @Mock lateinit var roleManager: RoleManager + @Mock lateinit var packageManager: PackageManager private lateinit var mockitoSession: MockitoSession @@ -69,6 +84,23 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { .mockStatic(InputSettings::class.java) .strictness(Strictness.LENIENT) .startMocking() + + whenever( + packageManager.getApplicationInfoAsUser( + anyString(), + any(ApplicationInfoFlags::class.java), + any(UserHandle::class.java) + ) + ) + .thenReturn(ApplicationInfo()) + whenever(controller.getUserForHandlingNotesTaking(any())).thenReturn(UserHandle.SYSTEM) + whenever( + roleManager.getRoleHoldersAsUser( + eq(RoleManager.ROLE_NOTES), + any(UserHandle::class.java) + ) + ) + .thenReturn(listOf("com.google.test.notes")) } @After @@ -85,6 +117,9 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { keyguardMonitor = mock(), lazyRepository = { repository }, isEnabled = isEnabled, + backgroundExecutor = FakeExecutor(FakeSystemClock()), + roleManager = roleManager, + noteTaskInfoResolver = NoteTaskInfoResolver(roleManager, packageManager) ) private fun createLockScreenStateVisible(): LockScreenState = @@ -112,6 +147,27 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { } @Test + fun lockScreenState_stylusUsed_userUnlocked_isSelected_noDefaultNotesAppSet_shouldEmitHidden() = + runTest { + TestConfig() + .setStylusEverUsed(true) + .setUserUnlocked(true) + .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>()) + whenever( + roleManager.getRoleHoldersAsUser( + eq(RoleManager.ROLE_NOTES), + any(UserHandle::class.java) + ) + ) + .thenReturn(emptyList()) + + val underTest = createUnderTest() + val actual by collectLastValue(underTest.lockScreenState) + + assertThat(actual).isEqualTo(LockScreenState.Hidden) + } + + @Test fun lockScreenState_stylusUnused_userUnlocked_isSelected_shouldEmitHidden() = runTest { TestConfig() .setStylusEverUsed(false) @@ -217,6 +273,39 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE) } + // region getPickerScreenState + @Test + fun getPickerScreenState_defaultNoteAppSet_shouldReturnDefault() = runTest { + val underTest = createUnderTest(isEnabled = true) + + assertThat(underTest.getPickerScreenState()) + .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default()) + } + + @Test + fun getPickerScreenState_nodefaultNoteAppSet_shouldReturnDisable() = runTest { + val underTest = createUnderTest(isEnabled = true) + whenever( + roleManager.getRoleHoldersAsUser( + eq(RoleManager.ROLE_NOTES), + any(UserHandle::class.java) + ) + ) + .thenReturn(emptyList()) + + assertThat(underTest.getPickerScreenState()) + .isEqualTo( + KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( + listOf("Select a default notes app to use the notetaking shortcut"), + actionText = "Select app", + actionComponentName = + "${context.packageName}$COMPONENT_NAME_SEPARATOR" + + "$ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE" + ) + ) + } + // endregion + private inner class TestConfig { fun setStylusEverUsed(value: Boolean) = also { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java index 88d7e9cb4de4..2dc78a323f55 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java @@ -587,14 +587,14 @@ public class QSSecurityFooterTest extends SysuiTestCase { assertEquals(addLink(mContext.getString(R.string.monitoring_description_two_named_vpns, VPN_PACKAGE, VPN_PACKAGE_2)), mFooterUtils.getVpnMessage(false, true, VPN_PACKAGE, VPN_PACKAGE_2)); - assertEquals(addLink(mContext.getString(R.string.monitoring_description_named_vpn, - VPN_PACKAGE)), + assertEquals(addLink(mContext.getString( + R.string.monitoring_description_managed_device_named_vpn, VPN_PACKAGE)), mFooterUtils.getVpnMessage(true, false, VPN_PACKAGE, null)); assertEquals(addLink(mContext.getString(R.string.monitoring_description_named_vpn, VPN_PACKAGE)), mFooterUtils.getVpnMessage(false, false, VPN_PACKAGE, null)); - assertEquals(addLink(mContext.getString(R.string.monitoring_description_named_vpn, - VPN_PACKAGE_2)), + assertEquals(addLink(mContext.getString( + R.string.monitoring_description_managed_device_named_vpn, VPN_PACKAGE_2)), mFooterUtils.getVpnMessage(true, true, null, VPN_PACKAGE_2)); assertEquals(addLink(mContext.getString( R.string.monitoring_description_managed_profile_named_vpn, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java index e106741499ab..41545fc2abfd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java @@ -30,6 +30,7 @@ import android.os.Handler; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.testing.TestableResources; import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; @@ -94,14 +95,18 @@ public class RotationLockTileTest extends SysuiTestCase { private RotationLockController mController; private TestableLooper mTestableLooper; private RotationLockTile mLockTile; + private TestableResources mTestableResources; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mTestableLooper = TestableLooper.get(this); + mTestableResources = mContext.getOrCreateTestableResources(); when(mHost.getContext()).thenReturn(mContext); when(mHost.getUserContext()).thenReturn(mContext); + mTestableResources.addOverride(com.android.internal.R.bool.config_allowRotationResolver, + true); mController = new RotationLockControllerImpl(mRotationPolicyWrapper, mDeviceStateRotationLockSettingController, DEFAULT_SETTINGS); @@ -208,6 +213,32 @@ public class RotationLockTileTest extends SysuiTestCase { } @Test + public void testSecondaryString_rotationResolverDisabled_isEmpty() { + mTestableResources.addOverride(com.android.internal.R.bool.config_allowRotationResolver, + false); + mLockTile = new RotationLockTile( + mHost, + mUiEventLogger, + mTestableLooper.getLooper(), + new Handler(mTestableLooper.getLooper()), + new FalsingManagerFake(), + mMetricsLogger, + mStatusBarStateController, + mActivityStarter, + mQSLogger, + mController, + mPrivacyManager, + mBatteryController, + new FakeSettings() + ); + + mLockTile.refreshState(); + mTestableLooper.processAllMessages(); + + assertEquals("", mLockTile.getState().secondaryLabel.toString()); + } + + @Test public void testIcon_whenDisabled_isOffState() { QSTile.BooleanState state = new QSTile.BooleanState(); disableAutoRotation(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java index a1d78cbba7f9..7c30843bb70b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java @@ -18,7 +18,6 @@ package com.android.systemui.screenrecord; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; @@ -94,6 +93,7 @@ public class RecordingServiceTest extends SysuiTestCase { doReturn(mContext.getUserId()).when(mRecordingService).getUserId(); doReturn(mContext.getPackageName()).when(mRecordingService).getPackageName(); doReturn(mContext.getContentResolver()).when(mRecordingService).getContentResolver(); + doReturn(mContext.getResources()).when(mRecordingService).getResources(); // Mock notifications doNothing().when(mRecordingService).createRecordingNotification(); @@ -101,7 +101,7 @@ public class RecordingServiceTest extends SysuiTestCase { doReturn(mNotification).when(mRecordingService).createSaveNotification(any()); doNothing().when(mRecordingService).createErrorNotification(); doNothing().when(mRecordingService).showErrorToast(anyInt()); - doNothing().when(mRecordingService).stopForeground(anyBoolean()); + doNothing().when(mRecordingService).stopForeground(anyInt()); doNothing().when(mRecordingService).startForeground(anyInt(), any()); doReturn(mScreenMediaRecorder).when(mRecordingService).getRecorder(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index 1edc63c4417e..9a8ec88e992a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -151,6 +151,7 @@ import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.KeyguardStatusBarView; import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController; +import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.ScrimController; @@ -158,6 +159,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; import com.android.systemui.statusbar.phone.TapAgainViewController; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; +import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -239,6 +241,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent; @Mock protected KeyguardClockSwitchController mKeyguardClockSwitchController; @Mock protected KeyguardStatusBarViewController mKeyguardStatusBarViewController; + @Mock protected LightBarController mLightBarController; @Mock protected NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; @Mock protected NotificationShadeDepthController mNotificationShadeDepthController; @@ -306,6 +309,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected ActivityStarter mActivityStarter; @Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; @Mock protected ShadeRepository mShadeRepository; + @Mock private CastController mCastController; protected final int mMaxUdfpsBurnInOffsetY = 5; protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor; @@ -653,6 +657,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mNotificationRemoteInputManager, mShadeExpansionStateManager, mStatusBarKeyguardViewManager, + mLightBarController, mNotificationStackScrollLayoutController, mLockscreenShadeTransitionController, mNotificationShadeDepthController, @@ -675,7 +680,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mInteractionJankMonitor, mShadeLog, mKeyguardFaceAuthInteractor, - mShadeRepository + mShadeRepository, + mCastController ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java index 8a9161e6d46d..1cf38732fdb2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java @@ -83,10 +83,12 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.KeyguardStatusBarView; +import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; +import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.KeyguardStateController; import dagger.Lazy; @@ -132,6 +134,7 @@ public class QuickSettingsControllerTest extends SysuiTestCase { @Mock private PulseExpansionHandler mPulseExpansionHandler; @Mock private NotificationRemoteInputManager mNotificationRemoteInputManager; @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + @Mock private LightBarController mLightBarController; @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController; @Mock private NotificationShadeDepthController mNotificationShadeDepthController; @@ -155,6 +158,7 @@ public class QuickSettingsControllerTest extends SysuiTestCase { @Mock private ShadeLogger mShadeLogger; @Mock private DumpManager mDumpManager; @Mock private UiEventLogger mUiEventLogger; + @Mock private CastController mCastController; private SysuiStatusBarStateController mStatusBarStateController; @@ -221,6 +225,7 @@ public class QuickSettingsControllerTest extends SysuiTestCase { mNotificationRemoteInputManager, mShadeExpansionStateManager, mStatusBarKeyguardViewManager, + mLightBarController, mNotificationStackScrollLayoutController, mLockscreenShadeTransitionController, mNotificationShadeDepthController, @@ -243,7 +248,8 @@ public class QuickSettingsControllerTest extends SysuiTestCase { mInteractionJankMonitor, mShadeLogger, mock(KeyguardFaceAuthInteractor.class), - mock(ShadeRepository.class) + mock(ShadeRepository.class), + mCastController ); mFragmentListener = mQsController.getQsFragmentListener(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt index beaf3009125b..c49f179c925e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt @@ -356,6 +356,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { @Test fun ignoreShadeBlurUntilHidden_schedulesFrame() { notificationShadeDepthController.blursDisabledForAppLaunch = true + verify(blurUtils).prepareBlur(any(), anyInt()) verify(choreographer) .postFrameCallback(eq(notificationShadeDepthController.updateBlurCallback)) } @@ -419,6 +420,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { notificationShadeDepthController.updateBlurCallback.doFrame(0) verify(notificationShadeWindowController).setBackgroundBlurRadius(eq(0)) verify(wallpaperController).setNotificationShadeZoom(eq(1f)) + verify(blurUtils).prepareBlur(any(), eq(0)) verify(blurUtils).applyBlur(eq(viewRootImpl), eq(0), eq(false)) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 9186c35e2b47..4c83194783ab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -617,24 +617,9 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { } @Test - public void applyRoundnessAndInv_should_be_immediately_applied_on_childrenContainer_legacy() - throws Exception { - ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); - group.useRoundnessSourceTypes(false); - Assert.assertEquals(0f, group.getBottomRoundness(), 0.001f); - Assert.assertEquals(0f, group.getChildrenContainer().getBottomRoundness(), 0.001f); - - group.requestBottomRoundness(1f, SourceType.from(""), false); - - Assert.assertEquals(1f, group.getBottomRoundness(), 0.001f); - Assert.assertEquals(1f, group.getChildrenContainer().getBottomRoundness(), 0.001f); - } - - @Test public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_childrenContainer() throws Exception { ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); - group.useRoundnessSourceTypes(true); Assert.assertEquals(0f, group.getBottomRoundness(), 0.001f); Assert.assertEquals(0f, group.getChildrenContainer().getBottomRoundness(), 0.001f); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java index e41929f7d578..be976a1c9eaf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java @@ -25,7 +25,6 @@ import android.view.View; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.LegacySourceType; import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; @@ -158,55 +157,7 @@ public class NotificationChildrenContainerTest extends SysuiTestCase { } @Test - public void addNotification_shouldResetOnScrollRoundness() throws Exception { - ExpandableNotificationRow row = mNotificationTestHelper.createRowWithRoundness( - /* topRoundness = */ 1f, - /* bottomRoundness = */ 1f, - /* sourceType = */ LegacySourceType.OnScroll); - - mChildrenContainer.addNotification(row, 0); - - Assert.assertEquals(0f, row.getTopRoundness(), /* delta = */ 0f); - Assert.assertEquals(0f, row.getBottomRoundness(), /* delta = */ 0f); - } - - @Test - public void addNotification_shouldNotResetOtherRoundness() throws Exception { - ExpandableNotificationRow row1 = mNotificationTestHelper.createRowWithRoundness( - /* topRoundness = */ 1f, - /* bottomRoundness = */ 1f, - /* sourceType = */ LegacySourceType.DefaultValue); - ExpandableNotificationRow row2 = mNotificationTestHelper.createRowWithRoundness( - /* topRoundness = */ 1f, - /* bottomRoundness = */ 1f, - /* sourceType = */ LegacySourceType.OnDismissAnimation); - - mChildrenContainer.addNotification(row1, 0); - mChildrenContainer.addNotification(row2, 0); - - Assert.assertEquals(1f, row1.getTopRoundness(), /* delta = */ 0f); - Assert.assertEquals(1f, row1.getBottomRoundness(), /* delta = */ 0f); - Assert.assertEquals(1f, row2.getTopRoundness(), /* delta = */ 0f); - Assert.assertEquals(1f, row2.getBottomRoundness(), /* delta = */ 0f); - } - - @Test - public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_last_child_legacy() { - mChildrenContainer.useRoundnessSourceTypes(false); - List<ExpandableNotificationRow> children = mChildrenContainer.getAttachedChildren(); - ExpandableNotificationRow notificationRow = children.get(children.size() - 1); - Assert.assertEquals(0f, mChildrenContainer.getBottomRoundness(), 0.001f); - Assert.assertEquals(0f, notificationRow.getBottomRoundness(), 0.001f); - - mChildrenContainer.requestBottomRoundness(1f, SourceType.from(""), false); - - Assert.assertEquals(1f, mChildrenContainer.getBottomRoundness(), 0.001f); - Assert.assertEquals(1f, notificationRow.getBottomRoundness(), 0.001f); - } - - @Test public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_last_child() { - mChildrenContainer.useRoundnessSourceTypes(true); List<ExpandableNotificationRow> children = mChildrenContainer.getAttachedChildren(); ExpandableNotificationRow notificationRow = children.get(children.size() - 1); Assert.assertEquals(0f, mChildrenContainer.getBottomRoundness(), 0.001f); @@ -220,8 +171,6 @@ public class NotificationChildrenContainerTest extends SysuiTestCase { @Test public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_header() { - mChildrenContainer.useRoundnessSourceTypes(true); - NotificationHeaderViewWrapper header = mChildrenContainer.getNotificationHeaderWrapper(); Assert.assertEquals(0f, header.getTopRoundness(), 0.001f); @@ -232,7 +181,6 @@ public class NotificationChildrenContainerTest extends SysuiTestCase { @Test public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_headerLowPriority() { - mChildrenContainer.useRoundnessSourceTypes(true); mChildrenContainer.setIsLowPriority(true); NotificationHeaderViewWrapper header = mChildrenContainer.getNotificationHeaderWrapper(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt index b1d3daa27eae..05b70ebfbb3e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt @@ -12,7 +12,6 @@ import com.android.systemui.flags.Flags import com.android.systemui.shade.transition.LargeScreenShadeInterpolator import com.android.systemui.statusbar.NotificationShelf import com.android.systemui.statusbar.StatusBarIconView -import com.android.systemui.statusbar.notification.LegacySourceType import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.row.NotificationTestHelper @@ -359,39 +358,6 @@ class NotificationShelfTest : SysuiTestCase() { ) } - @Test - fun resetOnScrollRoundness_shouldSetOnScrollTo0() { - val row: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness( - /* topRoundness = */ 1f, - /* bottomRoundness = */ 1f, - /* sourceType = */ LegacySourceType.OnScroll) - - NotificationShelf.resetLegacyOnScrollRoundness(row) - - assertEquals(0f, row.topRoundness) - assertEquals(0f, row.bottomRoundness) - } - - @Test - fun resetOnScrollRoundness_shouldNotResetOtherRoundness() { - val row1: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness( - /* topRoundness = */ 1f, - /* bottomRoundness = */ 1f, - /* sourceType = */ LegacySourceType.DefaultValue) - val row2: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness( - /* topRoundness = */ 1f, - /* bottomRoundness = */ 1f, - /* sourceType = */ LegacySourceType.OnDismissAnimation) - - NotificationShelf.resetLegacyOnScrollRoundness(row1) - NotificationShelf.resetLegacyOnScrollRoundness(row2) - - assertEquals(1f, row1.topRoundness) - assertEquals(1f, row1.bottomRoundness) - assertEquals(1f, row2.topRoundness) - assertEquals(1f, row2.bottomRoundness) - } - private fun setFractionToShade(fraction: Float) { whenever(ambientState.fractionToShade).thenReturn(fraction) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java index 529519a6246e..a50155628027 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java @@ -22,21 +22,31 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARE import static junit.framework.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.graphics.Color; import android.graphics.Rect; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.annotation.ColorInt; import androidx.test.filters.SmallTest; +import com.android.internal.colorextraction.ColorExtractor.GradientColors; +import com.android.internal.util.ContrastColorUtil; import com.android.internal.view.AppearanceRegion; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FakeFeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.statusbar.policy.BatteryController; @@ -53,13 +63,25 @@ import java.util.ArrayList; @TestableLooper.RunWithLooper public class LightBarControllerTest extends SysuiTestCase { + private static final GradientColors COLORS_LIGHT = makeColors(Color.WHITE); + private static final GradientColors COLORS_DARK = makeColors(Color.BLACK); + private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); private LightBarTransitionsController mLightBarTransitionsController; + private LightBarTransitionsController mNavBarController; private SysuiDarkIconDispatcher mStatusBarIconController; private LightBarController mLightBarController; + /** Allow testing with NEW_LIGHT_BAR_LOGIC flag in different states */ + protected boolean testNewLightBarLogic() { + return false; + } + @Before public void setup() { + mFeatureFlags.set(Flags.NEW_LIGHT_BAR_LOGIC, testNewLightBarLogic()); mStatusBarIconController = mock(SysuiDarkIconDispatcher.class); + mNavBarController = mock(LightBarTransitionsController.class); + when(mNavBarController.supportsIconTintForNavMode(anyInt())).thenReturn(true); mLightBarTransitionsController = mock(LightBarTransitionsController.class); when(mStatusBarIconController.getTransitionsController()).thenReturn( mLightBarTransitionsController); @@ -68,10 +90,19 @@ public class LightBarControllerTest extends SysuiTestCase { mStatusBarIconController, mock(BatteryController.class), mock(NavigationModeController.class), + mFeatureFlags, mock(DumpManager.class), new FakeDisplayTracker(mContext)); } + private static GradientColors makeColors(@ColorInt int bgColor) { + GradientColors colors = new GradientColors(); + colors.setMainColor(bgColor); + colors.setSecondaryColor(bgColor); + colors.setSupportsDarkText(!ContrastColorUtil.isColorDark(bgColor)); + return colors; + } + @Test public void testOnStatusBarAppearanceChanged_multipleStacks_allStacksLight() { final Rect firstBounds = new Rect(0, 0, 1, 1); @@ -177,4 +208,54 @@ public class LightBarControllerTest extends SysuiTestCase { false /* navbarColorManagedByIme */); verify(mLightBarTransitionsController).setIconsDark(eq(false), anyBoolean()); } + + @Test + public void validateNavBarChangesUpdateIcons() { + assumeTrue(testNewLightBarLogic()); // Only run in the new suite + + // On the launcher in dark mode buttons are light + mLightBarController.setScrimState(ScrimState.UNLOCKED, 0f, COLORS_DARK); + mLightBarController.onNavigationBarAppearanceChanged( + 0, /* nbModeChanged = */ true, + MODE_TRANSPARENT, /* navbarColorManagedByIme = */ false); + verifyNavBarIconsUnchanged(); // no changes yet; not attached + + // Initial state is set when controller is set + mLightBarController.setNavigationBar(mNavBarController); + verifyNavBarIconsDarkSetTo(false); + + // Changing the color of the transparent scrim has no effect + mLightBarController.setScrimState(ScrimState.UNLOCKED, 0f, COLORS_LIGHT); + verifyNavBarIconsUnchanged(); // still light + + // Showing the notification shade with white scrim requires dark icons + mLightBarController.setScrimState(ScrimState.UNLOCKED, 1f, COLORS_LIGHT); + verifyNavBarIconsDarkSetTo(true); + + // Expanded QS always provides a black background, so icons become light again + mLightBarController.setQsExpanded(true); + verifyNavBarIconsDarkSetTo(false); + + // Tapping the QS tile to change to dark theme has no effect in this state + mLightBarController.setScrimState(ScrimState.UNLOCKED, 1f, COLORS_DARK); + verifyNavBarIconsUnchanged(); // still light + + // collapsing QS in dark mode doesn't affect button color + mLightBarController.setQsExpanded(false); + verifyNavBarIconsUnchanged(); // still light + + // Closing the shade has no affect + mLightBarController.setScrimState(ScrimState.UNLOCKED, 0f, COLORS_DARK); + verifyNavBarIconsUnchanged(); // still light + } + + private void verifyNavBarIconsUnchanged() { + verify(mNavBarController, never()).setIconsDark(anyBoolean(), anyBoolean()); + } + + private void verifyNavBarIconsDarkSetTo(boolean iconsDark) { + verify(mNavBarController).setIconsDark(eq(iconsDark), anyBoolean()); + verify(mNavBarController, never()).setIconsDark(eq(!iconsDark), anyBoolean()); + clearInvocations(mNavBarController); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerWithNewLogicTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerWithNewLogicTest.kt new file mode 100644 index 000000000000..d9c2cfa5ea7f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerWithNewLogicTest.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.phone + +import androidx.test.filters.SmallTest +import com.android.systemui.flags.Flags.NEW_LIGHT_BAR_LOGIC + +/** + * This file only needs to live as long as [NEW_LIGHT_BAR_LOGIC] does. When we delete that flag, we + * can roll this back into the old test. + */ +@SmallTest +class LightBarControllerWithNewLogicTest : LightBarControllerTest() { + override fun testNewLightBarLogic(): Boolean = true +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt index b80b825d87dc..c282c1ef0cf6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt @@ -21,6 +21,8 @@ import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT +import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue @@ -49,7 +51,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateWidthFor_oneIcon_widthForOneIcon() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 1f), /* actual= */ 30f) @@ -59,7 +61,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateWidthFor_fourIcons_widthForFourIcons() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 4f), /* actual= */ 60f) @@ -69,7 +71,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateWidthFor_fiveIcons_widthForFourIcons() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 5f), /* actual= */ 60f) } @@ -78,7 +80,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateIconXTranslations_shortShelfOneIcon_atCorrectXWithoutOverflowDot() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) val icon = mockStatusBarIcon() iconContainer.addView(icon) @@ -99,7 +101,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateIconXTranslations_shortShelfFourIcons_atCorrectXWithoutOverflowDot() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) val iconOne = mockStatusBarIcon() val iconTwo = mockStatusBarIcon() @@ -128,7 +130,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateIconXTranslations_shortShelfFiveIcons_atCorrectXWithOverflowDot() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) val iconOne = mockStatusBarIcon() val iconTwo = mockStatusBarIcon() @@ -154,6 +156,55 @@ class NotificationIconContainerTest : SysuiTestCase() { } @Test + fun calculateIconXTranslations_givenWidthEnoughForThreeIcons_atCorrectXWithoutOverflowDot() { + iconContainer.setActualPaddingStart(0f) + iconContainer.setActualPaddingEnd(0f) + iconContainer.setActualLayoutWidth(30) + iconContainer.setIconSize(10) + + val iconOne = mockStatusBarIcon() + val iconTwo = mockStatusBarIcon() + val iconThree = mockStatusBarIcon() + + iconContainer.addView(iconOne) + iconContainer.addView(iconTwo) + iconContainer.addView(iconThree) + assertEquals(3, iconContainer.childCount) + + iconContainer.calculateIconXTranslations() + assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation) + assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation) + assertEquals(20f, iconContainer.getIconState(iconThree).xTranslation) + assertFalse(iconContainer.areIconsOverflowing()) + } + + @Test + fun calculateIconXTranslations_givenWidthNotEnoughForFourIcons_atCorrectXWithOverflowDot() { + iconContainer.setActualPaddingStart(0f) + iconContainer.setActualPaddingEnd(0f) + iconContainer.setActualLayoutWidth(35) + iconContainer.setIconSize(10) + + val iconOne = mockStatusBarIcon() + val iconTwo = mockStatusBarIcon() + val iconThree = mockStatusBarIcon() + val iconFour = mockStatusBarIcon() + + iconContainer.addView(iconOne) + iconContainer.addView(iconTwo) + iconContainer.addView(iconThree) + iconContainer.addView(iconFour) + assertEquals(4, iconContainer.childCount) + + iconContainer.calculateIconXTranslations() + assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation) + assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation) + assertEquals(STATE_DOT, iconContainer.getIconState(iconThree).visibleState) + assertEquals(STATE_HIDDEN, iconContainer.getIconState(iconFour).visibleState) + assertTrue(iconContainer.areIconsOverflowing()) + } + + @Test fun shouldForceOverflow_appearingAboveSpeedBump_true() { val forceOverflow = iconContainer.shouldForceOverflow( /* i= */ 1, @@ -161,7 +212,7 @@ class NotificationIconContainerTest : SysuiTestCase() { /* iconAppearAmount= */ 1f, /* maxVisibleIcons= */ 5 ) - assertTrue(forceOverflow); + assertTrue(forceOverflow) } @Test @@ -172,7 +223,7 @@ class NotificationIconContainerTest : SysuiTestCase() { /* iconAppearAmount= */ 0f, /* maxVisibleIcons= */ 5 ) - assertTrue(forceOverflow); + assertTrue(forceOverflow) } @Test @@ -183,7 +234,7 @@ class NotificationIconContainerTest : SysuiTestCase() { /* iconAppearAmount= */ 0f, /* maxVisibleIcons= */ 5 ) - assertFalse(forceOverflow); + assertFalse(forceOverflow) } @Test @@ -210,6 +261,17 @@ class NotificationIconContainerTest : SysuiTestCase() { } @Test + fun isOverflowing_lastChildXGreaterThanDotX_true() { + val isOverflowing = iconContainer.isOverflowing( + /* isLastChild= */ true, + /* translationX= */ 9f, + /* layoutEnd= */ 10f, + /* iconSize= */ 2f, + ) + assertTrue(isOverflowing) + } + + @Test fun isOverflowing_lastChildXGreaterThanLayoutEnd_true() { val isOverflowing = iconContainer.isOverflowing( /* isLastChild= */ true, @@ -253,7 +315,7 @@ class NotificationIconContainerTest : SysuiTestCase() { assertTrue(isOverflowing) } - private fun mockStatusBarIcon() : StatusBarIconView { + private fun mockStatusBarIcon(): StatusBarIconView { val iconView = mock(StatusBarIconView::class.java) whenever(iconView.width).thenReturn(10) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java index db50163ce6a7..b8e4306a319d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java @@ -1,6 +1,7 @@ package com.android.systemui.statusbar.policy; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -125,4 +126,20 @@ public class CastControllerImplTest extends SysuiTestCase { fail("Concurrent modification exception"); } } + + @Test + public void hasConnectedCastDevice_connected() { + CastController.CastDevice castDevice = new CastController.CastDevice(); + castDevice.state = CastController.CastDevice.STATE_CONNECTED; + mController.startCasting(castDevice); + assertTrue(mController.hasConnectedCastDevice()); + } + + @Test + public void hasConnectedCastDevice_notConnected() { + CastController.CastDevice castDevice = new CastController.CastDevice(); + castDevice.state = CastController.CastDevice.STATE_CONNECTING; + mController.startCasting(castDevice); + assertTrue(mController.hasConnectedCastDevice()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 1510ee89dcd9..47a86b1fca5c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -380,7 +380,7 @@ public class BubblesTest extends SysuiTestCase { mPositioner, mock(DisplayController.class), mOneHandedOptional, - mock(DragAndDropController.class), + Optional.of(mock(DragAndDropController.class)), syncExecutor, mock(Handler.class), mTaskViewTransitions, diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java index c3bb7716d9a9..14c3f3c4ada5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java @@ -69,7 +69,7 @@ public class TestableBubbleController extends BubbleController { BubblePositioner positioner, DisplayController displayController, Optional<OneHandedController> oneHandedOptional, - DragAndDropController dragAndDropController, + Optional<DragAndDropController> dragAndDropController, ShellExecutor shellMainExecutor, Handler shellMainHandler, TaskViewTransitions taskViewTransitions, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeRearDisplayStateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeRearDisplayStateRepository.kt new file mode 100644 index 000000000000..fd9139165c85 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeRearDisplayStateRepository.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.biometrics.data.repository + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class FakeRearDisplayStateRepository : RearDisplayStateRepository { + private val _isInRearDisplayMode = MutableStateFlow<Boolean>(false) + override val isInRearDisplayMode: StateFlow<Boolean> = _isInRearDisplayMode.asStateFlow() + + fun setIsInRearDisplayMode(isInRearDisplayMode: Boolean) { + _isInRearDisplayMode.value = isInRearDisplayMode + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java index f6b24da9b821..84ace7cc9540 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java @@ -51,4 +51,9 @@ public class FakeCastController extends BaseLeakChecker<Callback> implements Cas public void stopCasting(CastDevice device) { } + + @Override + public boolean hasConnectedCastDevice() { + return false; + } } diff --git a/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java b/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java deleted file mode 100644 index 715697d82cad..000000000000 --- a/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.autofill; - -import static android.service.autofill.FillRequest.INVALID_REQUEST_ID; - -import static com.android.server.autofill.Helper.sVerbose; - -import android.annotation.Nullable; -import android.annotation.UserIdInt; -import android.app.AppGlobals; -import android.content.ComponentName; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.os.ICancellationSignal; -import android.os.RemoteException; -import android.service.autofill.Dataset; -import android.service.autofill.FillResponse; -import android.service.autofill.IFillCallback; -import android.service.autofill.SaveInfo; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.util.Slog; -import android.view.autofill.AutofillId; -import android.view.autofill.IAutoFillManagerClient; -import android.view.inputmethod.InlineSuggestionsRequest; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.infra.AndroidFuture; - -import java.util.List; -import java.util.concurrent.CancellationException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicReference; - -/** - * Maintains a client suggestions session with the - * {@link android.view.autofill.AutofillRequestCallback} through the {@link IAutoFillManagerClient}. - * - */ -final class ClientSuggestionsSession { - - private static final String TAG = "ClientSuggestionsSession"; - private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 15 * DateUtils.SECOND_IN_MILLIS; - - private final int mSessionId; - private final IAutoFillManagerClient mClient; - private final Handler mHandler; - private final ComponentName mComponentName; - - private final RemoteFillService.FillServiceCallbacks mCallbacks; - - private final Object mLock = new Object(); - @GuardedBy("mLock") - private AndroidFuture<FillResponse> mPendingFillRequest; - @GuardedBy("mLock") - private int mPendingFillRequestId = INVALID_REQUEST_ID; - - ClientSuggestionsSession(int sessionId, IAutoFillManagerClient client, Handler handler, - ComponentName componentName, RemoteFillService.FillServiceCallbacks callbacks) { - mSessionId = sessionId; - mClient = client; - mHandler = handler; - mComponentName = componentName; - mCallbacks = callbacks; - } - - void onFillRequest(int requestId, InlineSuggestionsRequest inlineRequest, int flags) { - final AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>(); - final AtomicReference<AndroidFuture<FillResponse>> futureRef = new AtomicReference<>(); - final AndroidFuture<FillResponse> fillRequest = new AndroidFuture<>(); - - mHandler.post(() -> { - if (sVerbose) { - Slog.v(TAG, "calling onFillRequest() for id=" + requestId); - } - - try { - mClient.requestFillFromClient(requestId, inlineRequest, - new FillCallbackImpl(fillRequest, futureRef, cancellationSink)); - } catch (RemoteException e) { - fillRequest.completeExceptionally(e); - } - }); - - fillRequest.orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS); - futureRef.set(fillRequest); - - synchronized (mLock) { - mPendingFillRequest = fillRequest; - mPendingFillRequestId = requestId; - } - - fillRequest.whenComplete((res, err) -> mHandler.post(() -> { - synchronized (mLock) { - mPendingFillRequest = null; - mPendingFillRequestId = INVALID_REQUEST_ID; - } - if (err == null) { - processAutofillId(res); - mCallbacks.onFillRequestSuccess(requestId, res, - mComponentName.getPackageName(), flags); - } else { - Slog.e(TAG, "Error calling on client fill request", err); - if (err instanceof TimeoutException) { - dispatchCancellationSignal(cancellationSink.get()); - mCallbacks.onFillRequestTimeout(requestId); - } else if (err instanceof CancellationException) { - dispatchCancellationSignal(cancellationSink.get()); - } else { - mCallbacks.onFillRequestFailure(requestId, err.getMessage()); - } - } - })); - } - - /** - * Gets the application info for the component. - */ - @Nullable - static ApplicationInfo getAppInfo(ComponentName comp, @UserIdInt int userId) { - try { - ApplicationInfo si = AppGlobals.getPackageManager().getApplicationInfo( - comp.getPackageName(), - PackageManager.GET_META_DATA, - userId); - if (si != null) { - return si; - } - } catch (RemoteException e) { - } - return null; - } - - /** - * Gets the user-visible name of the application. - */ - @Nullable - @GuardedBy("mLock") - static CharSequence getAppLabelLocked(Context context, ApplicationInfo appInfo) { - return appInfo == null ? null : appInfo.loadSafeLabel( - context.getPackageManager(), 0 /* do not ellipsize */, - TextUtils.SAFE_STRING_FLAG_FIRST_LINE | TextUtils.SAFE_STRING_FLAG_TRIM); - } - - /** - * Gets the user-visible icon of the application. - */ - @Nullable - @GuardedBy("mLock") - static Drawable getAppIconLocked(Context context, ApplicationInfo appInfo) { - return appInfo == null ? null : appInfo.loadIcon(context.getPackageManager()); - } - - int cancelCurrentRequest() { - synchronized (mLock) { - return mPendingFillRequest != null && mPendingFillRequest.cancel(false) - ? mPendingFillRequestId - : INVALID_REQUEST_ID; - } - } - - /** - * The {@link AutofillId} which the client gets from its view is not contain the session id, - * but Autofill framework is using the {@link AutofillId} with a session id. So before using - * those ids in the Autofill framework, applies the current session id. - * - * @param res which response need to apply for a session id - */ - private void processAutofillId(FillResponse res) { - if (res == null) { - return; - } - - final List<Dataset> datasets = res.getDatasets(); - if (datasets != null && !datasets.isEmpty()) { - for (int i = 0; i < datasets.size(); i++) { - final Dataset dataset = datasets.get(i); - if (dataset != null) { - applySessionId(dataset.getFieldIds()); - } - } - } - - final SaveInfo saveInfo = res.getSaveInfo(); - if (saveInfo != null) { - applySessionId(saveInfo.getOptionalIds()); - applySessionId(saveInfo.getRequiredIds()); - applySessionId(saveInfo.getSanitizerValues()); - applySessionId(saveInfo.getTriggerId()); - } - } - - private void applySessionId(List<AutofillId> ids) { - if (ids == null || ids.isEmpty()) { - return; - } - - for (int i = 0; i < ids.size(); i++) { - applySessionId(ids.get(i)); - } - } - - private void applySessionId(AutofillId[][] ids) { - if (ids == null) { - return; - } - for (int i = 0; i < ids.length; i++) { - applySessionId(ids[i]); - } - } - - private void applySessionId(AutofillId[] ids) { - if (ids == null) { - return; - } - for (int i = 0; i < ids.length; i++) { - applySessionId(ids[i]); - } - } - - private void applySessionId(AutofillId id) { - if (id == null) { - return; - } - id.setSessionId(mSessionId); - } - - private void dispatchCancellationSignal(@Nullable ICancellationSignal signal) { - if (signal == null) { - return; - } - try { - signal.cancel(); - } catch (RemoteException e) { - Slog.e(TAG, "Error requesting a cancellation", e); - } - } - - private class FillCallbackImpl extends IFillCallback.Stub { - final AndroidFuture<FillResponse> mFillRequest; - final AtomicReference<AndroidFuture<FillResponse>> mFutureRef; - final AtomicReference<ICancellationSignal> mCancellationSink; - - FillCallbackImpl(AndroidFuture<FillResponse> fillRequest, - AtomicReference<AndroidFuture<FillResponse>> futureRef, - AtomicReference<ICancellationSignal> cancellationSink) { - mFillRequest = fillRequest; - mFutureRef = futureRef; - mCancellationSink = cancellationSink; - } - - @Override - public void onCancellable(ICancellationSignal cancellation) { - AndroidFuture<FillResponse> future = mFutureRef.get(); - if (future != null && future.isCancelled()) { - dispatchCancellationSignal(cancellation); - } else { - mCancellationSink.set(cancellation); - } - } - - @Override - public void onSuccess(FillResponse response) { - mFillRequest.complete(response); - } - - @Override - public void onFailure(int requestId, CharSequence message) { - String errorMessage = message == null ? "" : String.valueOf(message); - mFillRequest.completeExceptionally( - new RuntimeException(errorMessage)); - } - } -} diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index f83d7342b6e3..3736262e0673 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -36,7 +36,6 @@ import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED; import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED; import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED; import static android.view.autofill.AutofillManager.COMMIT_REASON_UNKNOWN; -import static android.view.autofill.AutofillManager.FLAG_ENABLED_CLIENT_SUGGESTIONS; import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM; import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString; @@ -446,9 +445,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ private final PccAssistDataReceiverImpl mPccAssistReceiver = new PccAssistDataReceiverImpl(); - @Nullable - private ClientSuggestionsSession mClientSuggestionsSession; - private final ClassificationState mClassificationState = new ClassificationState(); // TODO(b/216576510): Share one BroadcastReceiver between all Sessions instead of creating a @@ -590,9 +586,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** Whether the current {@link FillResponse} is expired. */ private boolean mExpiredResponse; - /** Whether the client is using {@link android.view.autofill.AutofillRequestCallback}. */ - private boolean mClientSuggestionsEnabled; - /** Whether the fill dialog UI is disabled. */ private boolean mFillDialogDisabled; } @@ -623,21 +616,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } mWaitForInlineRequest = inlineSuggestionsRequest != null; mPendingInlineSuggestionsRequest = inlineSuggestionsRequest; - mWaitForInlineRequest = inlineSuggestionsRequest != null; - maybeRequestFillFromServiceLocked(); + maybeRequestFillLocked(); viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST); } } : null; } - void newAutofillRequestLocked(@Nullable InlineSuggestionsRequest inlineRequest) { - mPendingFillRequest = null; - mWaitForInlineRequest = inlineRequest != null; - mPendingInlineSuggestionsRequest = inlineRequest; - } - @GuardedBy("mLock") - void maybeRequestFillFromServiceLocked() { + void maybeRequestFillLocked() { if (mPendingFillRequest == null) { return; } @@ -647,15 +633,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - if (mPendingInlineSuggestionsRequest.isServiceSupported()) { - mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(), - mPendingFillRequest.getFillContexts(), - mPendingFillRequest.getHints(), - mPendingFillRequest.getClientState(), - mPendingFillRequest.getFlags(), - mPendingInlineSuggestionsRequest, - mPendingFillRequest.getDelayedFillIntentSender()); - } + mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(), + mPendingFillRequest.getFillContexts(), + mPendingFillRequest.getHints(), + mPendingFillRequest.getClientState(), + mPendingFillRequest.getFlags(), + mPendingInlineSuggestionsRequest, + mPendingFillRequest.getDelayedFillIntentSender()); } mLastFillRequest = mPendingFillRequest; @@ -777,7 +761,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState : mDelayedFillPendingIntent.getIntentSender()); mPendingFillRequest = request; - maybeRequestFillFromServiceLocked(); + maybeRequestFillLocked(); } if (mActivityToken != null) { @@ -1099,39 +1083,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Cancels the last request sent to the {@link #mRemoteFillService} or the - * {@link #mClientSuggestionsSession}. + * Cancels the last request sent to the {@link #mRemoteFillService}. */ @GuardedBy("mLock") private void cancelCurrentRequestLocked() { - if (mRemoteFillService == null && mClientSuggestionsSession == null) { - wtf(null, "cancelCurrentRequestLocked() called without a remote service or a " - + "client suggestions session. mForAugmentedAutofillOnly: %s", - mSessionFlags.mAugmentedAutofillOnly); + if (mRemoteFillService == null) { + wtf(null, "cancelCurrentRequestLocked() called without a remote service. " + + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); return; } + final int canceledRequest = mRemoteFillService.cancelCurrentRequest(); - if (mRemoteFillService != null) { - final int canceledRequest = mRemoteFillService.cancelCurrentRequest(); - - // Remove the FillContext as there will never be a response for the service - if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) { - final int numContexts = mContexts.size(); + // Remove the FillContext as there will never be a response for the service + if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) { + final int numContexts = mContexts.size(); - // It is most likely the last context, hence search backwards - for (int i = numContexts - 1; i >= 0; i--) { - if (mContexts.get(i).getRequestId() == canceledRequest) { - if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest); - mContexts.remove(i); - break; - } + // It is most likely the last context, hence search backwards + for (int i = numContexts - 1; i >= 0; i--) { + if (mContexts.get(i).getRequestId() == canceledRequest) { + if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest); + mContexts.remove(i); + break; } } } - - if (mClientSuggestionsSession != null) { - mClientSuggestionsSession.cancelCurrentRequest(); - } } private boolean isViewFocusedLocked(int flags) { @@ -1225,30 +1200,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState requestAssistStructureForPccLocked(flags | FLAG_PCC_DETECTION); } - // Only ask IME to create inline suggestions request when - // 1. Autofill provider supports it or client enabled client suggestions. - // 2. The render service is available. - // 3. The view is focused. (The view may not be focused if the autofill is triggered - // manually.) + // Only ask IME to create inline suggestions request if Autofill provider supports it and + // the render service is available except the autofill is triggered manually and the view + // is also not focused. final RemoteInlineSuggestionRenderService remoteRenderService = mService.getRemoteInlineSuggestionRenderServiceLocked(); - if ((mSessionFlags.mInlineSupportedByService || mSessionFlags.mClientSuggestionsEnabled) - && remoteRenderService != null - && (isViewFocusedLocked(flags) || (isRequestSupportFillDialog(flags)))) { - final Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer; - if (mSessionFlags.mClientSuggestionsEnabled) { - final int finalRequestId = requestId; - inlineSuggestionsRequestConsumer = (inlineSuggestionsRequest) -> { - // Using client suggestions - synchronized (mLock) { - onClientFillRequestLocked(finalRequestId, inlineSuggestionsRequest); - } - viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST); - }; - } else { - inlineSuggestionsRequestConsumer = mAssistReceiver.newAutofillRequestLocked( - viewState, /* isInlineRequest= */ true); - } + if (mSessionFlags.mInlineSupportedByService + && remoteRenderService != null + && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) { + Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer = + mAssistReceiver.newAutofillRequestLocked(viewState, + /* isInlineRequest= */ true); if (inlineSuggestionsRequestConsumer != null) { final AutofillId focusedId = mCurrentViewId; final int requestIdCopy = requestId; @@ -1264,18 +1226,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState ); viewState.setState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST); } - } else if (mSessionFlags.mClientSuggestionsEnabled) { - // Request client suggestions for the dropdown mode - onClientFillRequestLocked(requestId, null); } else { mAssistReceiver.newAutofillRequestLocked(viewState, /* isInlineRequest= */ false); } - if (mSessionFlags.mClientSuggestionsEnabled) { - // Using client suggestions, unnecessary request AssistStructure - return; - } - // Now request the assist structure data. requestAssistStructureLocked(requestId, flags); } @@ -1380,11 +1334,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSessionFlags = new SessionFlags(); mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly; mSessionFlags.mInlineSupportedByService = mService.isInlineSuggestionsEnabledLocked(); - if (mContext.checkCallingPermission(PROVIDE_OWN_AUTOFILL_SUGGESTIONS) - == PackageManager.PERMISSION_GRANTED) { - mSessionFlags.mClientSuggestionsEnabled = - (mFlags & FLAG_ENABLED_CLIENT_SUGGESTIONS) != 0; - } setClientLocked(client); } @@ -1522,15 +1471,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (requestLog != null) { requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1); } - processNullResponseOrFallbackLocked(requestId, requestFlags); + processNullResponseLocked(requestId, requestFlags); return; } // TODO: Check if this is required. We can still present datasets to the user even if // traditional field classification is disabled. fieldClassificationIds = response.getFieldClassificationIds(); - if (!mSessionFlags.mClientSuggestionsEnabled && fieldClassificationIds != null - && !mService.isFieldClassificationEnabledLocked()) { + if (fieldClassificationIds != null && !mService.isFieldClassificationEnabledLocked()) { Slog.w(TAG, "Ignoring " + response + " because field detection is disabled"); processNullResponseLocked(requestId, requestFlags); return; @@ -1643,9 +1591,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState || (ArrayUtils.isEmpty(saveInfo.getOptionalIds()) && ArrayUtils.isEmpty(saveInfo.getRequiredIds()) && ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) == 0))) - && (ArrayUtils.isEmpty(response.getFieldClassificationIds()) - || (!mSessionFlags.mClientSuggestionsEnabled - && !mService.isFieldClassificationEnabledLocked()))); + && (ArrayUtils.isEmpty(response.getFieldClassificationIds()))); } } @@ -1975,40 +1921,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - @GuardedBy("mLock") - private void processNullResponseOrFallbackLocked(int requestId, int flags) { - if (!mSessionFlags.mClientSuggestionsEnabled) { - processNullResponseLocked(requestId, flags); - return; - } - - // fallback to the default platform password manager - mSessionFlags.mClientSuggestionsEnabled = false; - mLastFillDialogTriggerIds = null; - // Log the existing FillResponse event. - mFillResponseEventLogger.logAndEndEvent(); - - final InlineSuggestionsRequest inlineRequest = - (mLastInlineSuggestionsRequest != null - && mLastInlineSuggestionsRequest.first == requestId) - ? mLastInlineSuggestionsRequest.second : null; - - // Start a new FillRequest logger for client suggestion fallback. - mFillRequestEventLogger.startLogForNewRequest(); - mRequestCount++; - mFillRequestEventLogger.maybeSetAppPackageUid(uid); - mFillRequestEventLogger.maybeSetFlags( - flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS); - mFillRequestEventLogger.maybeSetRequestTriggerReason( - TRIGGER_REASON_NORMAL_TRIGGER); - mFillRequestEventLogger.maybeSetIsClientSuggestionFallback(true); - - mAssistReceiver.newAutofillRequestLocked(inlineRequest); - requestAssistStructureLocked(requestId, - flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS); - return; - } - // FillServiceCallbacks @Override @SuppressWarnings("GuardedBy") @@ -4205,22 +4117,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState filterText = value.getTextValue().toString(); } - final CharSequence targetLabel; - final Drawable targetIcon; - synchronized (mLock) { - if (mSessionFlags.mClientSuggestionsEnabled) { - final ApplicationInfo appInfo = ClientSuggestionsSession.getAppInfo(mComponentName, - mService.getUserId()); - targetLabel = ClientSuggestionsSession.getAppLabelLocked( - mService.getMaster().getContext(), appInfo); - targetIcon = ClientSuggestionsSession.getAppIconLocked( - mService.getMaster().getContext(), appInfo); - } else { - targetLabel = mService.getServiceLabelLocked(); - targetIcon = mService.getServiceIconLocked(); - } + final CharSequence serviceLabel; + final Drawable serviceIcon; + synchronized (this.mService.mLock) { + serviceLabel = mService.getServiceLabelLocked(); + serviceIcon = mService.getServiceIconLocked(); } - if (targetLabel == null || targetIcon == null) { + if (serviceLabel == null || serviceIcon == null) { wtf(null, "onFillReady(): no service label or icon"); return; } @@ -4281,7 +4184,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState getUiForShowing().showFillUi(filledId, response, filterText, mService.getServicePackageName(), mComponentName, - targetLabel, targetIcon, this, mContext, id, mCompatMode); + serviceLabel, serviceIcon, this, mContext, id, mCompatMode); synchronized (mLock) { mPresentationStatsEventLogger.maybeSetCountShown( @@ -4477,17 +4380,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return false; } - final InlineSuggestionsRequest request = inlineSuggestionsRequest.get(); - if (mSessionFlags.mClientSuggestionsEnabled && !request.isClientSupported() - || !mSessionFlags.mClientSuggestionsEnabled && !request.isServiceSupported()) { - if (sDebug) { - Slog.d(TAG, "Inline suggestions not supported for " - + (mSessionFlags.mClientSuggestionsEnabled ? "client" : "service") - + ". Falling back to dropdown."); - } - return false; - } - final RemoteInlineSuggestionRenderService remoteRenderService = mService.getRemoteInlineSuggestionRenderServiceLocked(); if (remoteRenderService == null) { @@ -4502,8 +4394,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } final InlineFillUi.InlineFillUiInfo inlineFillUiInfo = - new InlineFillUi.InlineFillUiInfo(request, focusedId, - filterText, remoteRenderService, userId, id); + new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId, + filterText, remoteRenderService, userId, id); InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response, new InlineFillUi.InlineSuggestionUiCallback() { @Override @@ -5154,26 +5046,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - @GuardedBy("mLock") - private void onClientFillRequestLocked(int requestId, - InlineSuggestionsRequest inlineSuggestionsRequest) { - if (mClientSuggestionsSession == null) { - mClientSuggestionsSession = new ClientSuggestionsSession(id, mClient, mHandler, - mComponentName, this); - } - - if (mContexts == null) { - mContexts = new ArrayList<>(1); - } - mContexts.add(new FillContext(requestId, new AssistStructure(), mCurrentViewId)); - - if (inlineSuggestionsRequest != null && !inlineSuggestionsRequest.isClientSupported()) { - inlineSuggestionsRequest = null; - } - - mClientSuggestionsSession.onFillRequest(requestId, inlineSuggestionsRequest, mFlags); - } - /** * The result of checking whether to show the save dialog, when session can be saved. * diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index a3a067481022..0172eaf03fa7 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -54,7 +54,6 @@ import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.NotificationManager; import android.app.PendingIntent; -import android.bluetooth.BluetoothDevice; import android.companion.AssociationInfo; import android.companion.AssociationRequest; import android.companion.DeviceNotAssociatedException; @@ -108,6 +107,9 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.companion.datatransfer.SystemDataTransferProcessor; import com.android.server.companion.datatransfer.SystemDataTransferRequestStore; +import com.android.server.companion.datatransfer.contextsync.CrossDeviceCall; +import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncController; +import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncControllerCallback; import com.android.server.companion.presence.CompanionDevicePresenceMonitor; import com.android.server.companion.transport.CompanionTransportManager; import com.android.server.pm.UserManagerInternal; @@ -117,6 +119,7 @@ import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -200,6 +203,8 @@ public class CompanionDeviceManagerService extends SystemService { private final RemoteCallbackList<IOnAssociationsChangedListener> mListeners = new RemoteCallbackList<>(); + private CrossDeviceSyncController mCrossDeviceSyncController; + public CompanionDeviceManagerService(Context context) { super(context); @@ -229,7 +234,7 @@ public class CompanionDeviceManagerService extends SystemService { loadAssociationsFromDisk(); mAssociationStore.registerListener(mAssociationStoreChangeListener); - mDevicePresenceMonitor = new CompanionDevicePresenceMonitor(mUserManager, + mDevicePresenceMonitor = new CompanionDevicePresenceMonitor( mAssociationStore, mDevicePresenceCallback); mAssociationRequestsProcessor = new AssociationRequestsProcessor( @@ -239,6 +244,8 @@ public class CompanionDeviceManagerService extends SystemService { mTransportManager = new CompanionTransportManager(context, mAssociationStore); mSystemDataTransferProcessor = new SystemDataTransferProcessor(this, mAssociationStore, mSystemDataTransferRequestStore, mTransportManager); + // TODO(b/279663946): move context sync to a dedicated system service + mCrossDeviceSyncController = new CrossDeviceSyncController(getContext(), mTransportManager); // Publish "binder" service. final CompanionDeviceManagerImpl impl = new CompanionDeviceManagerImpl(); @@ -315,21 +322,6 @@ public class CompanionDeviceManagerService extends SystemService { MINUTES.toMillis(10)); } - @Override - public void onUserUnlocked(@NonNull TargetUser user) { - // Notify and bind the app after the phone is unlocked. - final int userId = user.getUserIdentifier(); - final Set<BluetoothDevice> blueToothDevices = - mDevicePresenceMonitor.getPendingReportConnectedDevices().get(userId); - for (BluetoothDevice bluetoothDevice : blueToothDevices) { - for (AssociationInfo ai: - mAssociationStore.getAssociationsByAddress(bluetoothDevice.getAddress())) { - Slog.i(TAG, "onUserUnlocked, device id( " + ai.getId() + " ) is connected"); - mDevicePresenceMonitor.onBluetoothCompanionDeviceConnected(ai.getId()); - } - } - } - @NonNull AssociationInfo getAssociationWithCallerChecks( @UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress) { @@ -1369,6 +1361,39 @@ public class CompanionDeviceManagerService extends SystemService { public void removeInactiveSelfManagedAssociations() { CompanionDeviceManagerService.this.removeInactiveSelfManagedAssociations(); } + + @Override + public void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback) { + if (CompanionDeviceConfig.isEnabled( + CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) { + mCrossDeviceSyncController.registerCallMetadataSyncCallback(callback); + } + } + + @Override + public void crossDeviceSync(int userId, Collection<CrossDeviceCall> calls) { + if (CompanionDeviceConfig.isEnabled( + CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) { + mCrossDeviceSyncController.syncToAllDevicesForUserId(userId, calls); + } + } + + @Override + public void crossDeviceSync(AssociationInfo associationInfo, + Collection<CrossDeviceCall> calls) { + if (CompanionDeviceConfig.isEnabled( + CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) { + mCrossDeviceSyncController.syncToSingleDevice(associationInfo, calls); + } + } + + @Override + public void sendCrossDeviceSyncMessage(int associationId, byte[] message) { + if (CompanionDeviceConfig.isEnabled( + CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) { + mCrossDeviceSyncController.syncMessageToDevice(associationId, message); + } + } } /** diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java index 36492407d463..3b108e63e13d 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java @@ -16,12 +16,41 @@ package com.android.server.companion; +import android.companion.AssociationInfo; + +import com.android.server.companion.datatransfer.contextsync.CrossDeviceCall; +import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncControllerCallback; + +import java.util.Collection; + /** * Companion Device Manager Local System Service Interface. */ -interface CompanionDeviceManagerServiceInternal { +public interface CompanionDeviceManagerServiceInternal { /** * @see CompanionDeviceManagerService#removeInactiveSelfManagedAssociations */ void removeInactiveSelfManagedAssociations(); + + /** + * Registers a callback from an InCallService / ConnectionService to CDM to process sync + * requests and perform call control actions. + */ + void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback); + + /** + * Requests a sync from an InCallService / ConnectionService to CDM, for the given association + * and message. + */ + void sendCrossDeviceSyncMessage(int associationId, byte[] message); + + /** + * Requests a sync from an InCallService to CDM, for the given user and call metadata. + */ + void crossDeviceSync(int userId, Collection<CrossDeviceCall> calls); + + /** + * Requests a sync from an InCallService to CDM, for the given association and call metadata. + */ + void crossDeviceSync(AssociationInfo associationInfo, Collection<CrossDeviceCall> calls); } diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java index ae4766ac9fda..443a732eb6f1 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java @@ -17,15 +17,20 @@ package com.android.server.companion.datatransfer.contextsync; import android.annotation.Nullable; +import android.companion.AssociationInfo; import android.telecom.Call; import android.telecom.InCallService; import android.telecom.TelecomManager; +import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.LocalServices; import com.android.server.companion.CompanionDeviceConfig; +import com.android.server.companion.CompanionDeviceManagerServiceInternal; import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import java.util.stream.Collectors; @@ -35,90 +40,132 @@ import java.util.stream.Collectors; */ public class CallMetadataSyncInCallService extends InCallService { + private static final String TAG = "CallMetadataIcs"; private static final long NOT_VALID = -1L; + private CompanionDeviceManagerServiceInternal mCdmsi; + @VisibleForTesting final Map<Call, CrossDeviceCall> mCurrentCalls = new HashMap<>(); - @VisibleForTesting - boolean mShouldSync; + @VisibleForTesting int mNumberOfActiveSyncAssociations; final Call.Callback mTelecomCallback = new Call.Callback() { @Override public void onDetailsChanged(Call call, Call.Details details) { - mCurrentCalls.get(call).updateCallDetails(details); + if (mNumberOfActiveSyncAssociations > 0) { + final CrossDeviceCall crossDeviceCall = mCurrentCalls.get(call); + if (crossDeviceCall != null) { + crossDeviceCall.updateCallDetails(details); + sync(getUserId()); + } else { + Slog.w(TAG, "Could not update details for nonexistent call"); + } + } } }; - final CallMetadataSyncCallback mCallMetadataSyncCallback = new CallMetadataSyncCallback() { - @Override - void processCallControlAction(int crossDeviceCallId, int callControlAction) { - final CrossDeviceCall crossDeviceCall = getCallForId(crossDeviceCallId, - mCurrentCalls.values()); - switch (callControlAction) { - case android.companion.Telecom.Call.ACCEPT: - if (crossDeviceCall != null) { - crossDeviceCall.doAccept(); - } - break; - case android.companion.Telecom.Call.REJECT: - if (crossDeviceCall != null) { - crossDeviceCall.doReject(); + final CrossDeviceSyncControllerCallback + mCrossDeviceSyncControllerCallback = new CrossDeviceSyncControllerCallback() { + @Override + void processContextSyncMessage(int associationId, + CallMetadataSyncData callMetadataSyncData) { + final Iterator<CallMetadataSyncData.Call> iterator = + callMetadataSyncData.getRequests().iterator(); + while (iterator.hasNext()) { + final CallMetadataSyncData.Call call = iterator.next(); + if (call.getId() != 0) { + // The call is already assigned an id; treat as control invocations. + for (int control : call.getControls()) { + processCallControlAction(call.getId(), control); + } + } + iterator.remove(); } - break; - case android.companion.Telecom.Call.SILENCE: - doSilence(); - break; - case android.companion.Telecom.Call.MUTE: - doMute(); - break; - case android.companion.Telecom.Call.UNMUTE: - doUnmute(); - break; - case android.companion.Telecom.Call.END: - if (crossDeviceCall != null) { - crossDeviceCall.doEnd(); - } - break; - case android.companion.Telecom.Call.PUT_ON_HOLD: - if (crossDeviceCall != null) { - crossDeviceCall.doPutOnHold(); - } - break; - case android.companion.Telecom.Call.TAKE_OFF_HOLD: - if (crossDeviceCall != null) { - crossDeviceCall.doTakeOffHold(); + } + + private void processCallControlAction(long crossDeviceCallId, + int callControlAction) { + final CrossDeviceCall crossDeviceCall = getCallForId(crossDeviceCallId, + mCurrentCalls.values()); + switch (callControlAction) { + case android.companion.Telecom.Call.ACCEPT: + if (crossDeviceCall != null) { + crossDeviceCall.doAccept(); + } + break; + case android.companion.Telecom.Call.REJECT: + if (crossDeviceCall != null) { + crossDeviceCall.doReject(); + } + break; + case android.companion.Telecom.Call.SILENCE: + doSilence(); + break; + case android.companion.Telecom.Call.MUTE: + doMute(); + break; + case android.companion.Telecom.Call.UNMUTE: + doUnmute(); + break; + case android.companion.Telecom.Call.END: + if (crossDeviceCall != null) { + crossDeviceCall.doEnd(); + } + break; + case android.companion.Telecom.Call.PUT_ON_HOLD: + if (crossDeviceCall != null) { + crossDeviceCall.doPutOnHold(); + } + break; + case android.companion.Telecom.Call.TAKE_OFF_HOLD: + if (crossDeviceCall != null) { + crossDeviceCall.doTakeOffHold(); + } + break; + default: } - break; - default: - } - } + } - @Override - void requestCrossDeviceSync(int userId) { - } + @Override + void requestCrossDeviceSync(AssociationInfo associationInfo) { + if (associationInfo.getUserId() == getUserId()) { + sync(associationInfo); + } + } - @Override - void updateStatus(int userId, boolean shouldSyncCallMetadata) { - if (userId == getUserId()) { - mShouldSync = shouldSyncCallMetadata; - if (shouldSyncCallMetadata) { - initializeCalls(); - } else { - mCurrentCalls.clear(); + @Override + void updateNumberOfActiveSyncAssociations(int userId, boolean added) { + if (userId == getUserId()) { + final boolean wasActivelySyncing = mNumberOfActiveSyncAssociations > 0; + if (added) { + mNumberOfActiveSyncAssociations++; + } else { + mNumberOfActiveSyncAssociations--; + } + if (!wasActivelySyncing && mNumberOfActiveSyncAssociations > 0) { + initializeCalls(); + } else if (wasActivelySyncing && mNumberOfActiveSyncAssociations <= 0) { + mCurrentCalls.clear(); + } + } } - } - } }; @Override public void onCreate() { super.onCreate(); - initializeCalls(); + if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) { + mCdmsi = LocalServices.getService(CompanionDeviceManagerServiceInternal.class); + mCdmsi.registerCallMetadataSyncCallback(mCrossDeviceSyncControllerCallback); + } } private void initializeCalls() { if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM) - && mShouldSync) { + && mNumberOfActiveSyncAssociations > 0) { mCurrentCalls.putAll(getCalls().stream().collect(Collectors.toMap(call -> call, call -> new CrossDeviceCall(getPackageManager(), call, getCallAudioState())))); + mCurrentCalls.keySet().forEach(call -> call.registerCallback(mTelecomCallback, + getMainThreadHandler())); + sync(getUserId()); } } @@ -139,33 +186,39 @@ public class CallMetadataSyncInCallService extends InCallService { @Override public void onCallAdded(Call call) { if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM) - && mShouldSync) { + && mNumberOfActiveSyncAssociations > 0) { mCurrentCalls.put(call, new CrossDeviceCall(getPackageManager(), call, getCallAudioState())); + call.registerCallback(mTelecomCallback); + sync(getUserId()); } } @Override public void onCallRemoved(Call call) { if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM) - && mShouldSync) { + && mNumberOfActiveSyncAssociations > 0) { mCurrentCalls.remove(call); + call.unregisterCallback(mTelecomCallback); + sync(getUserId()); } } @Override public void onMuteStateChanged(boolean isMuted) { if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM) - && mShouldSync) { + && mNumberOfActiveSyncAssociations > 0) { mCurrentCalls.values().forEach(call -> call.updateMuted(isMuted)); + sync(getUserId()); } } @Override public void onSilenceRinger() { if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM) - && mShouldSync) { + && mNumberOfActiveSyncAssociations > 0) { mCurrentCalls.values().forEach(call -> call.updateSilencedIfRinging()); + sync(getUserId()); } } @@ -183,4 +236,12 @@ public class CallMetadataSyncInCallService extends InCallService { telecomManager.silenceRinger(); } } + + private void sync(int userId) { + mCdmsi.crossDeviceSync(userId, mCurrentCalls.values()); + } + + private void sync(AssociationInfo associationInfo) { + mCdmsi.crossDeviceSync(associationInfo, mCurrentCalls.values()); + } }
\ No newline at end of file diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java index 3d8fb7a8d5bf..adc5faf24f2c 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java @@ -16,27 +16,32 @@ package com.android.server.companion.datatransfer.contextsync; +import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_CONTEXT_SYNC; + import android.app.admin.DevicePolicyManager; import android.companion.AssociationInfo; import android.companion.ContextSyncMessage; +import android.companion.IOnMessageReceivedListener; +import android.companion.IOnTransportsChangedListener; import android.companion.Telecom; -import android.companion.Telecom.Call; import android.content.Context; +import android.os.Binder; import android.os.UserHandle; -import android.util.Pair; import android.util.Slog; +import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; +import android.util.proto.ProtoParseException; +import android.util.proto.ProtoUtils; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.companion.CompanionDeviceConfig; +import com.android.server.companion.transport.CompanionTransportManager; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; /** @@ -45,149 +50,308 @@ import java.util.Set; public class CrossDeviceSyncController { private static final String TAG = "CrossDeviceSyncController"; - private static final int BYTE_ARRAY_SIZE = 64; + + private static final int VERSION_1 = 1; + private static final int CURRENT_VERSION = VERSION_1; private final Context mContext; - private final Callback mCdmCallback; - private final Map<Integer, List<AssociationInfo>> mUserIdToAssociationInfo = new HashMap<>(); - private final Map<Integer, Pair<InputStream, OutputStream>> mAssociationIdToStreams = - new HashMap<>(); + private final CompanionTransportManager mCompanionTransportManager; + private final List<AssociationInfo> mConnectedAssociations = new ArrayList<>(); private final Set<Integer> mBlocklist = new HashSet<>(); - private CallMetadataSyncCallback mInCallServiceCallMetadataSyncCallback; + private CrossDeviceSyncControllerCallback mCrossDeviceSyncControllerCallback; - public CrossDeviceSyncController(Context context, Callback callback) { + public CrossDeviceSyncController(Context context, + CompanionTransportManager companionTransportManager) { mContext = context; - mCdmCallback = callback; + mCompanionTransportManager = companionTransportManager; + mCompanionTransportManager.addListener(new IOnTransportsChangedListener.Stub() { + @Override + public void onTransportsChanged(List<AssociationInfo> newAssociations) { + final long token = Binder.clearCallingIdentity(); + try { + if (!CompanionDeviceConfig.isEnabled( + CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) { + return; + } + } finally { + Binder.restoreCallingIdentity(token); + } + final List<AssociationInfo> existingAssociations = new ArrayList<>( + mConnectedAssociations); + mConnectedAssociations.clear(); + mConnectedAssociations.addAll(newAssociations); + + if (mCrossDeviceSyncControllerCallback == null) { + Slog.w(TAG, "No callback to report transports changed"); + return; + } + for (AssociationInfo associationInfo : newAssociations) { + if (!existingAssociations.contains(associationInfo) + && !isAssociationBlocked(associationInfo.getId())) { + mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations( + associationInfo.getUserId(), /* added= */ true); + mCrossDeviceSyncControllerCallback.requestCrossDeviceSync(associationInfo); + } + } + for (AssociationInfo associationInfo : existingAssociations) { + if (!newAssociations.contains(associationInfo)) { + if (isAssociationBlocked(associationInfo.getId())) { + mBlocklist.remove(associationInfo.getId()); + } else { + mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations( + associationInfo.getUserId(), /* added= */ false); + } + } + } + } + }); + mCompanionTransportManager.addListener(MESSAGE_REQUEST_CONTEXT_SYNC, + new IOnMessageReceivedListener.Stub() { + @Override + public void onMessageReceived(int associationId, byte[] data) { + if (mCrossDeviceSyncControllerCallback == null) { + Slog.w(TAG, "No callback to process context sync message"); + return; + } + mCrossDeviceSyncControllerCallback.processContextSyncMessage(associationId, + processTelecomDataFromSync(data)); + } + }); + } + + private boolean isAssociationBlocked(int associationId) { + return mBlocklist.contains(associationId); } /** Registers the call metadata callback. */ - public void registerCallMetadataSyncCallback(CallMetadataSyncCallback callback) { - mInCallServiceCallMetadataSyncCallback = callback; + public void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback) { + mCrossDeviceSyncControllerCallback = callback; + for (AssociationInfo associationInfo : mConnectedAssociations) { + if (!isAssociationBlocked(associationInfo.getId())) { + mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations( + associationInfo.getUserId(), /* added= */ true); + mCrossDeviceSyncControllerCallback.requestCrossDeviceSync(associationInfo); + } + } } /** Allow specific associated devices to enable / disable syncing. */ public void setSyncEnabled(AssociationInfo associationInfo, boolean enabled) { if (enabled) { - if (mBlocklist.contains(associationInfo.getId())) { + if (isAssociationBlocked(associationInfo.getId())) { mBlocklist.remove(associationInfo.getId()); - openChannel(associationInfo); + mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations( + associationInfo.getUserId(), /* added= */ true); + mCrossDeviceSyncControllerCallback.requestCrossDeviceSync(associationInfo); } } else { - if (!mBlocklist.contains(associationInfo.getId())) { + if (!isAssociationBlocked(associationInfo.getId())) { mBlocklist.add(associationInfo.getId()); - closeChannel(associationInfo); + mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations( + associationInfo.getUserId(), /* added= */ false); + // Send empty message to device to clear its data (otherwise it will get stale) + syncMessageToDevice(associationInfo.getId(), createEmptyMessage()); } } } + private boolean isAdminBlocked(int userId) { + return mContext.getSystemService(DevicePolicyManager.class) + .getBluetoothContactSharingDisabled(UserHandle.of(userId)); + } + /** - * Opens channels to newly associated devices, and closes channels to newly disassociated - * devices. + * Sync data to associated devices. * - * TODO(b/265466098): this needs to be limited to just connected devices + * @param userId The user whose data should be synced. + * @param calls The full list of current calls for all users. */ - public void onAssociationsChanged(int userId, List<AssociationInfo> newAssociationInfoList) { - final List<AssociationInfo> existingAssociationInfoList = mUserIdToAssociationInfo.get( - userId); - // Close channels to newly-disconnected devices. - for (AssociationInfo existingAssociationInfo : existingAssociationInfoList) { - if (!newAssociationInfoList.contains(existingAssociationInfo) && !mBlocklist.contains( - existingAssociationInfo.getId())) { - closeChannel(existingAssociationInfo); + public void syncToAllDevicesForUserId(int userId, Collection<CrossDeviceCall> calls) { + final Set<Integer> associationIds = new HashSet<>(); + for (AssociationInfo associationInfo : mConnectedAssociations) { + if (associationInfo.getUserId() == userId && !isAssociationBlocked( + associationInfo.getId())) { + associationIds.add(associationInfo.getId()); } } - // Open channels to newly-connected devices. - for (AssociationInfo newAssociationInfo : newAssociationInfoList) { - if (!existingAssociationInfoList.contains(newAssociationInfo) && !mBlocklist.contains( - newAssociationInfo.getId())) { - openChannel(newAssociationInfo); - } + if (associationIds.isEmpty()) { + Slog.w(TAG, "No eligible devices to sync to"); + return; } - mUserIdToAssociationInfo.put(userId, newAssociationInfoList); - } - private boolean isAdminBlocked(int userId) { - return mContext.getSystemService(DevicePolicyManager.class) - .getBluetoothContactSharingDisabled(UserHandle.of(userId)); + mCompanionTransportManager.sendMessage(MESSAGE_REQUEST_CONTEXT_SYNC, + createCallUpdateMessage(calls, userId), + associationIds.stream().mapToInt(Integer::intValue).toArray()); } - /** Stop reading, close streams, and close secure channel. */ - private void closeChannel(AssociationInfo associationInfo) { - // TODO(b/265466098): stop reading from secure channel - final Pair<InputStream, OutputStream> streams = mAssociationIdToStreams.get( - associationInfo.getId()); - if (streams != null) { - try { - if (streams.first != null) { - streams.first.close(); - } - if (streams.second != null) { - streams.second.close(); - } - } catch (IOException e) { - Slog.e(TAG, "Could not close streams for association " + associationInfo.getId(), - e); - } + /** + * Sync data to associated devices. + * + * @param associationInfo The association whose data should be synced. + * @param calls The full list of current calls for all users. + */ + public void syncToSingleDevice(AssociationInfo associationInfo, + Collection<CrossDeviceCall> calls) { + if (isAssociationBlocked(associationInfo.getId())) { + Slog.e(TAG, "Cannot sync to requested device; connection is blocked"); + return; } - mCdmCallback.closeSecureChannel(associationInfo.getId()); - } - /** Sync initial snapshot and start reading. */ - private void openChannel(AssociationInfo associationInfo) { - final InputStream is = new ByteArrayInputStream(new byte[BYTE_ARRAY_SIZE]); - final OutputStream os = new ByteArrayOutputStream(BYTE_ARRAY_SIZE); - mAssociationIdToStreams.put(associationInfo.getId(), new Pair<>(is, os)); - mCdmCallback.createSecureChannel(associationInfo.getId(), is, os); - // TODO(b/265466098): only requestSync for this specific association / connection? - mInCallServiceCallMetadataSyncCallback.requestCrossDeviceSync(associationInfo.getUserId()); - // TODO(b/265466098): start reading from secure channel + mCompanionTransportManager.sendMessage(MESSAGE_REQUEST_CONTEXT_SYNC, + createCallUpdateMessage(calls, associationInfo.getUserId()), + new int[]{associationInfo.getId()}); } /** * Sync data to associated devices. * - * @param userId The user whose data should be synced. - * @param calls The full list of current calls for all users. + * @param associationId The association whose data should be synced. + * @param message The message to sync. */ - public void crossDeviceSync(int userId, Collection<CrossDeviceCall> calls) { - final boolean isAdminBlocked = isAdminBlocked(userId); - for (AssociationInfo associationInfo : mUserIdToAssociationInfo.get(userId)) { - final Pair<InputStream, OutputStream> streams = mAssociationIdToStreams.get( - associationInfo.getId()); - final ProtoOutputStream pos = new ProtoOutputStream(streams.second); - final long telecomToken = pos.start(ContextSyncMessage.TELECOM); - for (CrossDeviceCall call : calls) { - final long callsToken = pos.start(Telecom.CALLS); - pos.write(Call.ID, call.getId()); - final long originToken = pos.start(Call.ORIGIN); - pos.write(Call.Origin.CALLER_ID, call.getReadableCallerId(isAdminBlocked)); - pos.write(Call.Origin.APP_ICON, call.getCallingAppIcon()); - pos.write(Call.Origin.APP_NAME, call.getCallingAppName()); - pos.end(originToken); - pos.write(Call.STATUS, call.getStatus()); - for (int control : call.getControls()) { - pos.write(Call.CONTROLS_AVAILABLE, control); + public void syncMessageToDevice(int associationId, byte[] message) { + if (isAssociationBlocked(associationId)) { + Slog.e(TAG, "Cannot sync to requested device; connection is blocked"); + return; + } + + mCompanionTransportManager.sendMessage(MESSAGE_REQUEST_CONTEXT_SYNC, message, + new int[]{associationId}); + } + + @VisibleForTesting + CallMetadataSyncData processTelecomDataFromSync(byte[] data) { + final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); + final ProtoInputStream pis = new ProtoInputStream(data); + try { + int version = -1; + while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pis.getFieldNumber()) { + case (int) ContextSyncMessage.VERSION: + version = pis.readInt(ContextSyncMessage.VERSION); + Slog.e(TAG, "Processing context sync message version " + version); + break; + case (int) ContextSyncMessage.TELECOM: + if (version == VERSION_1) { + final long telecomToken = pis.start(ContextSyncMessage.TELECOM); + while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + if (pis.getFieldNumber() == (int) Telecom.CALLS) { + final long callsToken = pis.start(Telecom.CALLS); + callMetadataSyncData.addCall(processCallDataFromSync(pis)); + pis.end(callsToken); + } else if (pis.getFieldNumber() == (int) Telecom.REQUESTS) { + final long requestsToken = pis.start(Telecom.REQUESTS); + callMetadataSyncData.addRequest(processCallDataFromSync(pis)); + pis.end(requestsToken); + } else { + Slog.e(TAG, "Unhandled field in Telecom:" + + ProtoUtils.currentFieldToString(pis)); + } + } + pis.end(telecomToken); + } else { + Slog.e(TAG, "Cannot process unsupported version " + version); + } + break; + default: + Slog.e(TAG, "Unhandled field in ContextSyncMessage:" + + ProtoUtils.currentFieldToString(pis)); } - pos.end(callsToken); } - pos.end(telecomToken); - pos.flush(); + } catch (IOException | ProtoParseException e) { + throw new RuntimeException(e); } + return callMetadataSyncData; } - /** - * Callback to be implemented by CompanionDeviceManagerService. - */ - public interface Callback { - /** - * Create a secure channel to send messages. - */ - void createSecureChannel(int associationId, InputStream input, OutputStream output); - - /** - * Close the secure channel created previously. - */ - void closeSecureChannel(int associationId); + @VisibleForTesting + CallMetadataSyncData.Call processCallDataFromSync(ProtoInputStream pis) throws IOException { + final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); + while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pis.getFieldNumber()) { + case (int) Telecom.Call.ID: + call.setId(pis.readLong(Telecom.Call.ID)); + break; + case (int) Telecom.Call.ORIGIN: + final long originToken = pis.start(Telecom.Call.ORIGIN); + while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pis.getFieldNumber()) { + case (int) Telecom.Call.Origin.APP_ICON: + call.setAppIcon(pis.readBytes(Telecom.Call.Origin.APP_ICON)); + break; + case (int) Telecom.Call.Origin.APP_NAME: + call.setAppName(pis.readString(Telecom.Call.Origin.APP_NAME)); + break; + case (int) Telecom.Call.Origin.CALLER_ID: + call.setCallerId(pis.readString(Telecom.Call.Origin.CALLER_ID)); + break; + case (int) Telecom.Call.Origin.APP_IDENTIFIER: + call.setAppIdentifier( + pis.readString(Telecom.Call.Origin.APP_IDENTIFIER)); + break; + default: + Slog.e(TAG, "Unhandled field in Origin:" + + ProtoUtils.currentFieldToString(pis)); + } + } + pis.end(originToken); + break; + case (int) Telecom.Call.STATUS: + call.setStatus(pis.readInt(Telecom.Call.STATUS)); + break; + case (int) Telecom.Call.CONTROLS: + call.addControl(pis.readInt(Telecom.Call.CONTROLS)); + break; + default: + Slog.e(TAG, + "Unhandled field in Telecom:" + ProtoUtils.currentFieldToString(pis)); + } + } + return call; + } + + @VisibleForTesting + byte[] createCallUpdateMessage(Collection<CrossDeviceCall> calls, int userId) { + final ProtoOutputStream pos = new ProtoOutputStream(); + pos.write(ContextSyncMessage.VERSION, CURRENT_VERSION); + final long telecomToken = pos.start(ContextSyncMessage.TELECOM); + for (CrossDeviceCall call : calls) { + final long callsToken = pos.start(Telecom.CALLS); + pos.write(Telecom.Call.ID, call.getId()); + final long originToken = pos.start(Telecom.Call.ORIGIN); + pos.write(Telecom.Call.Origin.CALLER_ID, + call.getReadableCallerId(isAdminBlocked(userId))); + pos.write(Telecom.Call.Origin.APP_ICON, call.getCallingAppIcon()); + pos.write(Telecom.Call.Origin.APP_NAME, call.getCallingAppName()); + pos.write(Telecom.Call.Origin.APP_IDENTIFIER, call.getCallingAppPackageName()); + pos.end(originToken); + pos.write(Telecom.Call.STATUS, call.getStatus()); + for (int control : call.getControls()) { + pos.write(Telecom.Call.CONTROLS, control); + } + pos.end(callsToken); + } + pos.end(telecomToken); + return pos.getBytes(); + } + + /** Create a call control message. */ + public static byte[] createCallControlMessage(long callId, int control) { + final ProtoOutputStream pos = new ProtoOutputStream(); + pos.write(ContextSyncMessage.VERSION, CURRENT_VERSION); + final long telecomToken = pos.start(ContextSyncMessage.TELECOM); + final long requestsToken = pos.start(Telecom.REQUESTS); + pos.write(Telecom.Call.ID, callId); + pos.write(Telecom.Call.CONTROLS, control); + pos.end(requestsToken); + pos.end(telecomToken); + return pos.getBytes(); + } + + /** Create an empty context sync message, used to clear state. */ + public static byte[] createEmptyMessage() { + final ProtoOutputStream pos = new ProtoOutputStream(); + pos.write(ContextSyncMessage.VERSION, CURRENT_VERSION); + return pos.getBytes(); } } diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerCallback.java index 7c339d213483..31e10a814568 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerCallback.java @@ -16,12 +16,14 @@ package com.android.server.companion.datatransfer.contextsync; +import android.companion.AssociationInfo; + /** Callback for call metadata syncing. */ -public abstract class CallMetadataSyncCallback { +public abstract class CrossDeviceSyncControllerCallback { - abstract void processCallControlAction(int crossDeviceCallId, int callControlAction); + void processContextSyncMessage(int associationId, CallMetadataSyncData callMetadataSyncData) {} - abstract void requestCrossDeviceSync(int userId); + void requestCrossDeviceSync(AssociationInfo associationInfo) {} - abstract void updateStatus(int userId, boolean shouldSyncCallMetadata); + void updateNumberOfActiveSyncAssociations(int userId, boolean added) {} } diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java index 82d0325cd749..f6b99b551ecb 100644 --- a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java +++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java @@ -27,23 +27,17 @@ import android.companion.AssociationInfo; import android.net.MacAddress; import android.os.Handler; import android.os.HandlerExecutor; -import android.os.UserHandle; -import android.os.UserManager; import android.util.Log; -import android.util.SparseArray; -import com.android.internal.annotations.GuardedBy; import com.android.server.companion.AssociationStore; import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; @SuppressLint("LongLogTag") -public class BluetoothCompanionDeviceConnectionListener +class BluetoothCompanionDeviceConnectionListener extends BluetoothAdapter.BluetoothConnectionCallback implements AssociationStore.OnChangeListener { private static final String TAG = "CDM_BluetoothCompanionDeviceConnectionListener"; @@ -54,23 +48,15 @@ public class BluetoothCompanionDeviceConnectionListener void onBluetoothCompanionDeviceDisconnected(int associationId); } - private final UserManager mUserManager; private final @NonNull AssociationStore mAssociationStore; private final @NonNull Callback mCallback; /** A set of ALL connected BT device (not only companion.) */ private final @NonNull Map<MacAddress, BluetoothDevice> mAllConnectedDevices = new HashMap<>(); - - @GuardedBy("mPendingReportConnectedDevices") - @NonNull - final SparseArray<Set<BluetoothDevice>> mPendingReportConnectedDevices = - new SparseArray<>(); - - BluetoothCompanionDeviceConnectionListener(UserManager userManager, - @NonNull AssociationStore associationStore, @NonNull Callback callback) { + BluetoothCompanionDeviceConnectionListener(@NonNull AssociationStore associationStore, + @NonNull Callback callback) { mAssociationStore = associationStore; mCallback = callback; - mUserManager = userManager; } public void init(@NonNull BluetoothAdapter btAdapter) { @@ -90,32 +76,12 @@ public class BluetoothCompanionDeviceConnectionListener if (DEBUG) Log.i(TAG, "onDevice_Connected() " + btDeviceToString(device)); final MacAddress macAddress = MacAddress.fromString(device.getAddress()); - final int userId = UserHandle.myUserId(); - if (mAllConnectedDevices.put(macAddress, device) != null) { if (DEBUG) Log.w(TAG, "Device " + btDeviceToString(device) + " is already connected."); return; } - // Try to bind and notify the app after the phone is unlocked. - if (!mUserManager.isUserUnlockingOrUnlocked(UserHandle.myUserId())) { - if (DEBUG) { - Log.i(TAG, "Current user is not in unlocking or unlocked stage yet. Notify " - + "the application when the phone is unlocked"); - } - synchronized (mPendingReportConnectedDevices) { - Set<BluetoothDevice> bluetoothDevices = mPendingReportConnectedDevices.get(userId); - - if (bluetoothDevices == null) { - bluetoothDevices = new HashSet<>(); - mPendingReportConnectedDevices.put(userId, bluetoothDevices); - } - - bluetoothDevices.add(device); - } - } else { - onDeviceConnectivityChanged(device, true); - } + onDeviceConnectivityChanged(device, true); } /** @@ -132,8 +98,6 @@ public class BluetoothCompanionDeviceConnectionListener } final MacAddress macAddress = MacAddress.fromString(device.getAddress()); - final int userId = UserHandle.myUserId(); - if (mAllConnectedDevices.remove(macAddress) == null) { if (DEBUG) { Log.w(TAG, "The device wasn't tracked as connected " + btDeviceToString(device)); @@ -141,19 +105,6 @@ public class BluetoothCompanionDeviceConnectionListener return; } - // Do not need to report the connectivity since the user is not unlock the phone so - // that cdm is not bind with the app yet. - if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { - synchronized (mPendingReportConnectedDevices) { - Set<BluetoothDevice> bluetoothDevices = mPendingReportConnectedDevices.get(userId); - if (bluetoothDevices != null) { - bluetoothDevices.remove(device); - } - } - - return; - } - onDeviceConnectivityChanged(device, false); } diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java index 6c565004d948..4010be922b2c 100644 --- a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java +++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java @@ -23,16 +23,13 @@ import android.annotation.NonNull; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; import android.companion.AssociationInfo; import android.content.Context; import android.os.Binder; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.os.UserManager; import android.util.Log; -import android.util.SparseArray; import com.android.server.companion.AssociationStore; @@ -89,12 +86,13 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange private final SimulatedDevicePresenceSchedulerHelper mSchedulerHelper = new SimulatedDevicePresenceSchedulerHelper(); - public CompanionDevicePresenceMonitor(UserManager userManager, - @NonNull AssociationStore associationStore, @NonNull Callback callback) { + public CompanionDevicePresenceMonitor(@NonNull AssociationStore associationStore, + @NonNull Callback callback) { mAssociationStore = associationStore; mCallback = callback; - mBtConnectionListener = new BluetoothCompanionDeviceConnectionListener(userManager, - associationStore, /* BluetoothCompanionDeviceConnectionListener.Callback */ this); + + mBtConnectionListener = new BluetoothCompanionDeviceConnectionListener(associationStore, + /* BluetoothCompanionDeviceConnectionListener.Callback */ this); mBleScanner = new BleCompanionDeviceScanner(associationStore, /* BleCompanionDeviceScanner.Callback */ this); } @@ -300,15 +298,6 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange // what's needed. } - /** - * Return a set of devices that pending to report connectivity - */ - public SparseArray<Set<BluetoothDevice>> getPendingReportConnectedDevices() { - synchronized (mBtConnectionListener.mPendingReportConnectedDevices) { - return mBtConnectionListener.mPendingReportConnectedDevices; - } - } - private static void enforceCallerShellOrRoot() { final int callingUid = Binder.getCallingUid(); if (callingUid == SHELL_UID || callingUid == ROOT_UID) return; diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 678d58232f15..cf09fde9ec2c 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -762,6 +762,15 @@ public final class ActiveServices { } } + private static void traceInstant(@NonNull String message, @NonNull ServiceRecord service) { + if (!Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + return; + } + final String serviceName = (service.getComponentName() != null) + ? service.getComponentName().toShortString() : "(?)"; + Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, message + serviceName); + } + ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, int callingPid, int callingUid, boolean fgRequired, String callingPackage, @Nullable String callingFeatureId, final int userId, boolean isSdkSandboxService, @@ -818,6 +827,9 @@ public final class ActiveServices { } ServiceRecord r = res.record; + + traceInstant("startService(): ", r); + // Note, when startService() or startForegroundService() is called on an already // running SHORT_SERVICE FGS, the call will succeed (i.e. we won't throw // ForegroundServiceStartNotAllowedException), even when the service is already timed @@ -1407,6 +1419,7 @@ public final class ActiveServices { } private void stopServiceLocked(ServiceRecord service, boolean enqueueOomAdj) { + traceInstant("stopService(): ", service); try { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "stopServiceLocked()"); if (service.delayed) { @@ -1946,6 +1959,7 @@ public final class ActiveServices { if (notification == null) { throw new IllegalArgumentException("null notification"); } + traceInstant("startForeground(): ", r); final int foregroundServiceStartType = foregroundServiceType; // Instant apps need permission to create foreground services. if (r.appInfo.isInstantApp()) { @@ -2484,6 +2498,7 @@ public final class ActiveServices { } } else { if (r.isForeground) { + traceInstant("stopForeground(): ", r); final ServiceMap smap = getServiceMapLocked(r.userId); if (smap != null) { decActiveForegroundAppLocked(smap, r); @@ -3311,6 +3326,7 @@ public final class ActiveServices { Slog.i(TAG_SERVICE, "Short FGS started: " + sr); } } + traceInstant("short FGS start/extend: ", sr); sr.setShortFgsInfo(SystemClock.uptimeMillis()); // We'll restart the timeout. @@ -3356,10 +3372,11 @@ public final class ActiveServices { return; } Slog.e(TAG_SERVICE, "Short FGS timed out: " + sr); - final long now = SystemClock.uptimeMillis(); + traceInstant("short FGS timeout: ", sr); + logFGSStateChangeLocked(sr, FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT, - now > sr.mFgsEnterTime ? (int) (now - sr.mFgsEnterTime) : 0, + nowUptime > sr.mFgsEnterTime ? (int) (nowUptime - sr.mFgsEnterTime) : 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN); try { @@ -3410,6 +3427,7 @@ public final class ActiveServices { } Slog.e(TAG_SERVICE, "Short FGS procstate demoted: " + sr); + traceInstant("short FGS demote: ", sr); mAm.updateOomAdjLocked(sr.app, OOM_ADJ_REASON_SHORT_FGS_TIMEOUT); } @@ -3440,6 +3458,9 @@ public final class ActiveServices { } else { Slog.e(TAG_SERVICE, message); } + + traceInstant("short FGS ANR: ", sr); + mAm.appNotResponding(sr.app, tr); // TODO: Can we close the ANR dialog here, if it's still shown? Currently, the ANR diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index a54e8e95b155..1a5d4253b5ba 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -5190,7 +5190,10 @@ public class ActivityManagerService extends IActivityManager.Stub throw new IllegalArgumentException( "Can't use FLAG_RECEIVER_BOOT_UPGRADE here"); } - if (PendingIntent.isNewMutableDisallowedImplicitPendingIntent(flags, intent)) { + boolean isActivityResultType = + type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT; + if (PendingIntent.isNewMutableDisallowedImplicitPendingIntent(flags, intent, + isActivityResultType)) { boolean isChangeEnabled = CompatChanges.isChangeEnabled( PendingIntent.BLOCK_MUTABLE_IMPLICIT_PENDING_INTENT, owningUid); @@ -6915,7 +6918,7 @@ public class ActivityManagerService extends IActivityManager.Stub mActivityTaskManager.unhandledBack(); } - // TODO: Move to ContentProviderHelper? + // TODO: Replace this method with one that returns a bound IContentProvider. public ParcelFileDescriptor openContentUri(String uriString) throws RemoteException { enforceNotIsolatedCaller("openContentUri"); final int userId = UserHandle.getCallingUserId(); @@ -6944,6 +6947,16 @@ public class ActivityManagerService extends IActivityManager.Stub Log.e(TAG, "Cannot find package for uid: " + uid); return null; } + + final ApplicationInfo appInfo = mPackageManagerInt.getApplicationInfo( + androidPackage.getPackageName(), /*flags*/0, Process.SYSTEM_UID, + UserHandle.USER_SYSTEM); + if (!appInfo.isVendor() && !appInfo.isSystemApp() && !appInfo.isSystemExt() + && !appInfo.isProduct()) { + Log.e(TAG, "openContentUri may only be used by vendor/system/product."); + return null; + } + final AttributionSource attributionSource = new AttributionSource( Binder.getCallingUid(), androidPackage.getPackageName(), null); pfd = cph.provider.openFile(attributionSource, uri, "r", null); diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java index f29a2e1ee7da..c687184a5a71 100644 --- a/services/core/java/com/android/server/am/AppProfiler.java +++ b/services/core/java/com/android/server/am/AppProfiler.java @@ -1199,6 +1199,9 @@ public class AppProfiler { .sendToTarget(); } + mCachedAppsWatermarkData.updateCachedAppsHighWatermarkIfNecessaryLocked( + numCached + numEmpty, now); + if (mService.mConstants.USE_MODERN_TRIM) { // Modern trim is not sent based on lowmem state // Dispatch UI_HIDDEN to processes that need it @@ -1316,8 +1319,6 @@ public class AppProfiler { profile.setTrimMemoryLevel(0); }); } - mCachedAppsWatermarkData.updateCachedAppsHighWatermarkIfNecessaryLocked( - numCached + numEmpty, now); return allChanged; } diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 1426cfd65286..3bc5de91b2bd 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -2100,9 +2100,12 @@ public final class CachedAppOptimizer { final boolean frozen; final ProcessCachedOptimizerRecord opt = proc.mOptRecord; - opt.setPendingFreeze(false); - synchronized (mProcLock) { + // someone has canceled this freeze + if (!opt.isPendingFreeze()) { + return; + } + opt.setPendingFreeze(false); pid = proc.getPid(); if (mFreezerOverride) { @@ -2148,7 +2151,6 @@ public final class CachedAppOptimizer { try { traceAppFreeze(proc.processName, pid, -1); Process.setProcessFrozen(pid, proc.uid, true); - opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis()); opt.setFrozen(true); opt.setHasCollectedFrozenPSS(false); diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 78aafeba3b22..6551db9ad783 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -643,6 +643,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN pw.print(prefix); pw.print("lastUntrustedSetFgsRestrictionAllowedTime="); TimeUtils.formatDuration(mLastUntrustedSetFgsRestrictionAllowedTime, now, pw); + pw.println(); if (delayed) { pw.print(prefix); pw.print("delayed="); pw.println(delayed); diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index a181402e2d9f..b2fdee7a6f89 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -3747,7 +3747,8 @@ class UserController implements Handler.Callback { synchronized (mUserSwitchingDialogLock) { dismissUserSwitchingDialog(null); mUserSwitchingDialog = new UserSwitchingDialog(mService.mContext, fromUser, toUser, - switchingFromSystemUserMessage, switchingToSystemUserMessage); + switchingFromSystemUserMessage, switchingToSystemUserMessage, + getWindowManager()); mUserSwitchingDialog.show(onShown); } } diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java index 649305f7ee01..412fbe797758 100644 --- a/services/core/java/com/android/server/am/UserSwitchingDialog.java +++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java @@ -19,7 +19,6 @@ package com.android.server.am; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.app.ActivityManager; import android.app.Dialog; import android.content.Context; import android.content.pm.UserInfo; @@ -38,6 +37,7 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; +import android.provider.Settings; import android.util.Slog; import android.util.TypedValue; import android.view.View; @@ -51,6 +51,7 @@ import android.widget.TextView; import com.android.internal.R; import com.android.internal.util.ObjectUtils; import com.android.internal.util.UserIcons; +import com.android.server.wm.WindowManagerService; /** * Dialog to show during the user switch. This dialog shows target user's name and their profile @@ -70,11 +71,14 @@ class UserSwitchingDialog extends Dialog { protected final UserInfo mNewUser; private final String mSwitchingFromSystemUserMessage; private final String mSwitchingToSystemUserMessage; + private final WindowManagerService mWindowManager; protected final Context mContext; private final int mTraceCookie; + private final boolean mNeedToFreezeScreen; UserSwitchingDialog(Context context, UserInfo oldUser, UserInfo newUser, - String switchingFromSystemUserMessage, String switchingToSystemUserMessage) { + String switchingFromSystemUserMessage, String switchingToSystemUserMessage, + WindowManagerService windowManager) { // TODO(b/278857848): Make full screen user switcher cover top part of the screen as well. // This problem is seen only on phones, it works fine on tablets. super(context, R.style.Theme_Material_NoActionBar_Fullscreen); @@ -84,8 +88,10 @@ class UserSwitchingDialog extends Dialog { mNewUser = newUser; mSwitchingFromSystemUserMessage = switchingFromSystemUserMessage; mSwitchingToSystemUserMessage = switchingToSystemUserMessage; - mDisableAnimations = ActivityManager.isLowRamDeviceStatic() || SystemProperties.getBoolean( + mDisableAnimations = SystemProperties.getBoolean( "debug.usercontroller.disable_user_switching_dialog_animations", false); + mWindowManager = windowManager; + mNeedToFreezeScreen = !mDisableAnimations && !isUserSetupComplete(newUser); mTraceCookie = UserHandle.MAX_SECONDARY_USER_ID * oldUser.id + newUser.id; inflateContent(); @@ -167,6 +173,11 @@ class UserSwitchingDialog extends Dialog { : res.getString(R.string.user_switching_message, mNewUser.name); } + private boolean isUserSetupComplete(UserInfo user) { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.USER_SETUP_COMPLETE, /* default= */ 0, user.id) == 1; + } + @Override public void show() { asyncTraceBegin("", 0); @@ -176,29 +187,24 @@ class UserSwitchingDialog extends Dialog { @Override public void dismiss() { super.dismiss(); + stopFreezingScreen(); asyncTraceEnd("", 0); } public void show(@NonNull Runnable onShown) { if (DEBUG) Slog.d(TAG, "show called"); show(); - - if (mDisableAnimations) { + startShowAnimation(() -> { + startFreezingScreen(); onShown.run(); - } else { - startShowAnimation(onShown); - } + }); } public void dismiss(@Nullable Runnable onDismissed) { if (DEBUG) Slog.d(TAG, "dismiss called"); - if (onDismissed == null) { // no animation needed dismiss(); - } else if (mDisableAnimations) { - dismiss(); - onDismissed.run(); } else { startDismissAnimation(() -> { dismiss(); @@ -207,7 +213,31 @@ class UserSwitchingDialog extends Dialog { } } + private void startFreezingScreen() { + if (!mNeedToFreezeScreen) { + return; + } + if (DEBUG) Slog.d(TAG, "startFreezingScreen"); + Trace.traceBegin(TRACE_TAG, "startFreezingScreen"); + mWindowManager.startFreezingScreen(0, 0); + Trace.traceEnd(TRACE_TAG); + } + + private void stopFreezingScreen() { + if (!mNeedToFreezeScreen) { + return; + } + if (DEBUG) Slog.d(TAG, "stopFreezingScreen"); + Trace.traceBegin(TRACE_TAG, "stopFreezingScreen"); + mWindowManager.stopFreezingScreen(); + Trace.traceEnd(TRACE_TAG); + } + private void startShowAnimation(Runnable onAnimationEnd) { + if (mDisableAnimations) { + onAnimationEnd.run(); + return; + } asyncTraceBegin("-showAnimation", 1); startDialogAnimation(new AlphaAnimation(0, 1), () -> { asyncTraceEnd("-showAnimation", 1); @@ -222,6 +252,11 @@ class UserSwitchingDialog extends Dialog { } private void startDismissAnimation(Runnable onAnimationEnd) { + if (mDisableAnimations || mNeedToFreezeScreen) { + // animations are disabled or screen is frozen, no need to play an animation + onAnimationEnd.run(); + return; + } asyncTraceBegin("-dismissAnimation", 3); startDialogAnimation(new AlphaAnimation(1, 0), () -> { asyncTraceEnd("-dismissAnimation", 3); @@ -231,8 +266,11 @@ class UserSwitchingDialog extends Dialog { } private void startProgressAnimation(Runnable onAnimationEnd) { - final ImageView progressCircular = findViewById(R.id.progress_circular); - final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) progressCircular.getDrawable(); + final AnimatedVectorDrawable avd = getSpinnerAVD(); + if (mDisableAnimations || avd == null) { + onAnimationEnd.run(); + return; + } avd.registerAnimationCallback(new Animatable2.AnimationCallback() { @Override public void onAnimationEnd(Drawable drawable) { @@ -242,7 +280,23 @@ class UserSwitchingDialog extends Dialog { avd.start(); } + private AnimatedVectorDrawable getSpinnerAVD() { + final ImageView view = findViewById(R.id.progress_circular); + if (view != null) { + final Drawable drawable = view.getDrawable(); + if (drawable instanceof AnimatedVectorDrawable) { + return (AnimatedVectorDrawable) drawable; + } + } + return null; + } + private void startDialogAnimation(Animation animation, Runnable onAnimationEnd) { + final View view = findViewById(R.id.content); + if (mDisableAnimations || view == null) { + onAnimationEnd.run(); + return; + } animation.setDuration(DIALOG_SHOW_HIDE_ANIMATION_DURATION_MS); animation.setAnimationListener(new Animation.AnimationListener() { @Override @@ -260,7 +314,7 @@ class UserSwitchingDialog extends Dialog { } }); - findViewById(R.id.content).startAnimation(animation); + view.startAnimation(animation); } private void asyncTraceBegin(String subTag, int subCookie) { diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java index 7cdea8d08c58..9429b4c129f5 100644 --- a/services/core/java/com/android/server/audio/SoundDoseHelper.java +++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java @@ -57,6 +57,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -168,7 +169,7 @@ public class SoundDoseHelper { @NonNull private final AudioHandler mAudioHandler; @NonNull private final ISafeHearingVolumeController mVolumeController; - private final boolean mEnableCsd; + private final AtomicBoolean mEnableCsd = new AtomicBoolean(false); private final Object mCsdStateLock = new Object(); @@ -195,7 +196,7 @@ public class SoundDoseHelper { private final ISoundDoseCallback.Stub mSoundDoseCallback = new ISoundDoseCallback.Stub() { public void onMomentaryExposure(float currentMel, int deviceId) { - if (!mEnableCsd) { + if (!mEnableCsd.get()) { Log.w(TAG, "onMomentaryExposure: csd not supported, ignoring callback"); return; } @@ -222,7 +223,7 @@ public class SoundDoseHelper { } public void onNewCsdValue(float currentCsd, SoundDoseRecord[] records) { - if (!mEnableCsd) { + if (!mEnableCsd.get()) { Log.w(TAG, "onNewCsdValue: csd not supported, ignoring value"); return; } @@ -272,8 +273,6 @@ public class SoundDoseHelper { mContext = context; - mEnableCsd = mContext.getResources().getBoolean(R.bool.config_audio_csd_enabled_default); - initCsd(); initSafeVolumes(); mSafeMediaVolumeState = mSettings.getGlobalInt(audioService.getContentResolver(), @@ -285,6 +284,10 @@ public class SoundDoseHelper { mSafeMediaVolumeIndex = mContext.getResources().getInteger( R.integer.config_safe_media_volume_index) * 10; + mSoundDose.set(AudioSystem.getSoundDoseInterface(mSoundDoseCallback)); + // Csd will be initially disabled until the mcc is read in onConfigureSafeMedia() + initCsd(); + mAlarmManager = (AlarmManager) mContext.getSystemService( Context.ALARM_SERVICE); } @@ -310,7 +313,7 @@ public class SoundDoseHelper { } float getOutputRs2UpperBound() { - if (!mEnableCsd) { + if (!mEnableCsd.get()) { return 0.f; } @@ -329,7 +332,7 @@ public class SoundDoseHelper { } void setOutputRs2UpperBound(float rs2Value) { - if (!mEnableCsd) { + if (!mEnableCsd.get()) { return; } @@ -347,7 +350,7 @@ public class SoundDoseHelper { } float getCsd() { - if (!mEnableCsd) { + if (!mEnableCsd.get()) { return -1.f; } @@ -366,7 +369,7 @@ public class SoundDoseHelper { } void setCsd(float csd) { - if (!mEnableCsd) { + if (!mEnableCsd.get()) { return; } @@ -400,7 +403,7 @@ public class SoundDoseHelper { } void resetCsdTimeouts() { - if (!mEnableCsd) { + if (!mEnableCsd.get()) { return; } @@ -416,7 +419,7 @@ public class SoundDoseHelper { } void forceUseFrameworkMel(boolean useFrameworkMel) { - if (!mEnableCsd) { + if (!mEnableCsd.get()) { return; } @@ -434,7 +437,7 @@ public class SoundDoseHelper { } void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices) { - if (!mEnableCsd) { + if (!mEnableCsd.get()) { return; } @@ -454,7 +457,7 @@ public class SoundDoseHelper { } boolean isCsdEnabled() { - if (!mEnableCsd) { + if (!mEnableCsd.get()) { return false; } @@ -697,8 +700,8 @@ public class SoundDoseHelper { } /*package*/ void dump(PrintWriter pw) { - pw.print(" mEnableCsd="); pw.println(mEnableCsd); - if (mEnableCsd) { + pw.print(" mEnableCsd="); pw.println(mEnableCsd.get()); + if (mEnableCsd.get()) { synchronized (mCsdStateLock) { pw.print(" mCurrentCsd="); pw.println(mCurrentCsd); } @@ -719,9 +722,11 @@ public class SoundDoseHelper { pw.println(); } - /*package*/void reset() { + /*package*/void reset() { Log.d(TAG, "Reset the sound dose helper"); - mSoundDose.set(AudioSystem.getSoundDoseInterface(mSoundDoseCallback)); + + mSoundDose.compareAndExchange(/*expectedValue=*/null, + AudioSystem.getSoundDoseInterface(mSoundDoseCallback)); synchronized (mCsdStateLock) { try { @@ -743,7 +748,7 @@ public class SoundDoseHelper { private void updateDoseAttenuation(int newIndex, int device, int streamType, boolean isAbsoluteVolume) { - if (!mEnableCsd) { + if (!mEnableCsd.get()) { return; } @@ -775,17 +780,19 @@ public class SoundDoseHelper { } private void initCsd() { - if (!mEnableCsd) { - final ISoundDose soundDose = AudioSystem.getSoundDoseInterface(mSoundDoseCallback); - if (soundDose == null) { - Log.w(TAG, "ISoundDose instance is null."); - return; - } - try { - soundDose.disableCsd(); - } catch (RemoteException e) { - Log.e(TAG, "Cannot disable CSD", e); - } + ISoundDose soundDose = mSoundDose.get(); + if (soundDose == null) { + Log.w(TAG, "ISoundDose instance is null."); + return; + } + + try { + soundDose.setCsdEnabled(mEnableCsd.get()); + } catch (RemoteException e) { + Log.e(TAG, "Cannot disable CSD", e); + } + + if (!mEnableCsd.get()) { return; } @@ -829,7 +836,6 @@ public class SoundDoseHelper { SystemProperties.getBoolean("audio.safemedia.force", false) || mContext.getResources().getBoolean( com.android.internal.R.bool.config_safe_media_volume_enabled); - boolean safeMediaVolumeBypass = SystemProperties.getBoolean("audio.safemedia.bypass", false); @@ -860,6 +866,13 @@ public class SoundDoseHelper { mAudioHandler.obtainMessage(MSG_PERSIST_SAFE_VOLUME_STATE, persistedState, /*arg2=*/0, /*obj=*/null), /*delay=*/0); + + boolean newEnableCsd = SystemProperties.getBoolean("audio.safemedia.force", false) + || mContext.getResources().getBoolean( + R.bool.config_safe_sound_dosage_enabled); + if (mEnableCsd.compareAndSet(!newEnableCsd, newEnableCsd)) { + initCsd(); + } } } } @@ -913,7 +926,7 @@ public class SoundDoseHelper { // legacy implementation uses mSafeMediaVolumeIndex for wired HS/HP // instead of computing it from the volume curves if ((deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE - || deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADSET) && !mEnableCsd) { + || deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADSET) && !mEnableCsd.get()) { return mSafeMediaVolumeIndex; } diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java index aab815c93e1a..adea13fcc13c 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java @@ -102,6 +102,8 @@ final class ConversionUtils { return new UnsupportedOperationException(action + ": NOT_SUPPORTED"); case Result.TIMEOUT: return new ParcelableException(new RuntimeException(action + ": TIMEOUT")); + case Result.CANCELED: + return new IllegalStateException(action + ": CANCELED"); default: return new ParcelableException(new RuntimeException( action + ": unknown error (" + result + ")")); @@ -123,6 +125,8 @@ final class ConversionUtils { return RadioTuner.TUNER_RESULT_NOT_SUPPORTED; case Result.TIMEOUT: return RadioTuner.TUNER_RESULT_TIMEOUT; + case Result.CANCELED: + return RadioTuner.TUNER_RESULT_CANCELED; case Result.UNKNOWN_ERROR: default: return RadioTuner.TUNER_RESULT_UNKNOWN_ERROR; diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java index 1f82961efd22..6d4306198aa2 100644 --- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java +++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java @@ -41,6 +41,7 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.BitUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.RingBuffer; @@ -278,6 +279,11 @@ public class NetdEventListenerService extends BaseNetdEventListener { } } + private boolean hasWifiTransport(Network network) { + final NetworkCapabilities nc = mCm.getNetworkCapabilities(network); + return nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI); + } + @Override public synchronized void onWakeupEvent(String prefix, int uid, int ethertype, int ipNextHeader, byte[] dstHw, String srcIp, String dstIp, int srcPort, int dstPort, long timestampNs) { @@ -286,12 +292,21 @@ public class NetdEventListenerService extends BaseNetdEventListener { throw new IllegalArgumentException("Prefix " + prefix + " required in format <nethandle>:<interface>"); } + final long netHandle = Long.parseLong(prefixParts[0]); + final Network network = Network.fromNetworkHandle(netHandle); final WakeupEvent event = new WakeupEvent(); event.iface = prefixParts[1]; event.uid = uid; event.ethertype = ethertype; - event.dstHwAddr = MacAddress.fromBytes(dstHw); + if (ArrayUtils.isEmpty(dstHw)) { + if (hasWifiTransport(network)) { + Log.e(TAG, "Empty mac address on WiFi transport, network: " + network); + } + event.dstHwAddr = null; + } else { + event.dstHwAddr = MacAddress.fromBytes(dstHw); + } event.srcIp = srcIp; event.dstIp = dstIp; event.ipNextHeader = ipNextHeader; @@ -306,14 +321,12 @@ public class NetdEventListenerService extends BaseNetdEventListener { final BatteryStatsInternal bsi = LocalServices.getService(BatteryStatsInternal.class); if (bsi != null) { - final long netHandle = Long.parseLong(prefixParts[0]); final long elapsedMs = SystemClock.elapsedRealtime() + event.timestampMs - System.currentTimeMillis(); - bsi.noteCpuWakingNetworkPacket(Network.fromNetworkHandle(netHandle), elapsedMs, - event.uid); + bsi.noteCpuWakingNetworkPacket(network, elapsedMs, event.uid); } - final String dstMac = event.dstHwAddr.toString(); + final String dstMac = String.valueOf(event.dstHwAddr); FrameworkStatsLog.write(FrameworkStatsLog.PACKET_WAKEUP_OCCURRED, uid, event.iface, ethertype, dstMac, srcIp, dstIp, ipNextHeader, srcPort, dstPort); } diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 378363c870c7..774087c63f0f 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -366,7 +366,12 @@ public class AutomaticBrightnessController { return getAutomaticScreenBrightness(null); } - float getAutomaticScreenBrightness(BrightnessEvent brightnessEvent) { + /** + * @return The current brightness recommendation calculated from the current conditions. + * @param brightnessEvent Event object to populate with details about why the specific + * brightness was chosen. + */ + public float getAutomaticScreenBrightness(BrightnessEvent brightnessEvent) { if (brightnessEvent != null) { brightnessEvent.setLux( mAmbientLuxValid ? mAmbientLux : PowerManager.BRIGHTNESS_INVALID_FLOAT); diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index a4bd6a699ceb..46cd496bdcd3 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -191,7 +191,7 @@ final class ColorFade { } if (!(createEglContext(isProtected) && createEglSurface(isProtected, isWideColor) - && setScreenshotTextureAndSetViewport(hardwareBuffer))) { + && setScreenshotTextureAndSetViewport(hardwareBuffer, displayInfo.rotation))) { dismiss(); return false; } @@ -500,7 +500,8 @@ final class ColorFade { } private boolean setScreenshotTextureAndSetViewport( - ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer) { + ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer, + @Surface.Rotation int rotation) { if (!attachEglContext()) { return false; } @@ -525,14 +526,22 @@ final class ColorFade { s.release(); st.release(); } + // if screen is rotated, map texture starting different corner + int indexDelta = (rotation == Surface.ROTATION_90) ? 2 + : (rotation == Surface.ROTATION_180) ? 4 + : (rotation == Surface.ROTATION_270) ? 6 : 0; // Set up texture coordinates for a quad. // We might need to change this if the texture ends up being // a different size from the display for some reason. - mTexCoordBuffer.put(0, 0f); mTexCoordBuffer.put(1, 0f); - mTexCoordBuffer.put(2, 0f); mTexCoordBuffer.put(3, 1f); - mTexCoordBuffer.put(4, 1f); mTexCoordBuffer.put(5, 1f); - mTexCoordBuffer.put(6, 1f); mTexCoordBuffer.put(7, 0f); + mTexCoordBuffer.put(indexDelta, 0f); + mTexCoordBuffer.put(indexDelta + 1, 0f); + mTexCoordBuffer.put((indexDelta + 2) % 8, 0f); + mTexCoordBuffer.put((indexDelta + 3) % 8, 1f); + mTexCoordBuffer.put((indexDelta + 4) % 8, 1f); + mTexCoordBuffer.put((indexDelta + 5) % 8, 1f); + mTexCoordBuffer.put((indexDelta + 6) % 8, 1f); + mTexCoordBuffer.put((indexDelta + 7) % 8, 0f); // Set up our viewport. GLES20.glViewport(0, 0, mDisplayWidth, mDisplayHeight); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 26b6cb0ebe9b..5771a04b9607 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -2548,7 +2548,6 @@ public final class DisplayManagerService extends SystemService { final DisplayInfo displayInfo = logicalDisplay.getDisplayInfoLocked(); captureArgs = new ScreenCapture.DisplayCaptureArgs.Builder(token) .setSize(displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight()) - .setUseIdentityTransform(true) .setCaptureSecureLayers(true) .setAllowProtected(true) .build(); diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 0d89ba88c1cf..0861cb59a944 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -83,6 +83,7 @@ import com.android.server.display.whitebalance.DisplayWhiteBalanceSettings; import com.android.server.policy.WindowManagerPolicy; import java.io.PrintWriter; +import java.util.Objects; /** * Controls the power state of the display. @@ -605,7 +606,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mLastBrightnessEvent = new BrightnessEvent(mDisplayId); mTempBrightnessEvent = new BrightnessEvent(mDisplayId); mThermalBrightnessThrottlingDataId = - logicalDisplay.getThermalBrightnessThrottlingDataIdLocked(); + logicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId; if (mDisplayId == Display.DEFAULT_DISPLAY) { mBatteryStats = BatteryStatsService.getService(); @@ -955,7 +956,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call && mLogicalDisplay.getPrimaryDisplayDeviceLocked() .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL; final String thermalBrightnessThrottlingDataId = - mLogicalDisplay.getThermalBrightnessThrottlingDataIdLocked(); + mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId; mHandler.postAtTime(() -> { boolean changed = false; if (mDisplayDevice != device) { @@ -972,8 +973,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // last command that was sent to change it's state. Let's assume it is unknown so // that we trigger a change immediately. mPowerState.resetScreenState(); - } else if ( - !mThermalBrightnessThrottlingDataId.equals(thermalBrightnessThrottlingDataId)) { + } else if (!Objects.equals(mThermalBrightnessThrottlingDataId, + thermalBrightnessThrottlingDataId)) { changed = true; mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId; mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig( @@ -2189,7 +2190,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call () -> { sendUpdatePowerState(); postBrightnessChangeRunnable(); - }, mUniqueDisplayId, mLogicalDisplay.getThermalBrightnessThrottlingDataIdLocked(), + }, mUniqueDisplayId, + mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId, ddConfig.getThermalBrightnessThrottlingDataMapByThrottlingId()); } diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index 9e8c47f64926..3b3d5da8396c 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -85,6 +85,7 @@ import com.android.server.display.whitebalance.DisplayWhiteBalanceSettings; import com.android.server.policy.WindowManagerPolicy; import java.io.PrintWriter; +import java.util.Objects; /** * Controls the power state of the display. @@ -488,7 +489,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(context, mDisplayId); mTag = "DisplayPowerController2[" + mDisplayId + "]"; mThermalBrightnessThrottlingDataId = - logicalDisplay.getThermalBrightnessThrottlingDataIdLocked(); + logicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId; mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); @@ -760,7 +761,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal && mLogicalDisplay.getPrimaryDisplayDeviceLocked() .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL; final String thermalBrightnessThrottlingDataId = - mLogicalDisplay.getThermalBrightnessThrottlingDataIdLocked(); + mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId; mHandler.postAtTime(() -> { boolean changed = false; @@ -778,8 +779,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal // last command that was sent to change it's state. Let's assume it is unknown so // that we trigger a change immediately. mPowerState.resetScreenState(); - } else if ( - !mThermalBrightnessThrottlingDataId.equals(thermalBrightnessThrottlingDataId)) { + } else if (!Objects.equals(mThermalBrightnessThrottlingDataId, + thermalBrightnessThrottlingDataId)) { changed = true; mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId; mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig( @@ -1301,7 +1302,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal int brightnessAdjustmentFlags = 0; if (Float.isNaN(brightnessState)) { if (mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()) { - brightnessState = mAutomaticBrightnessStrategy.getAutomaticScreenBrightness(); + brightnessState = mAutomaticBrightnessStrategy.getAutomaticScreenBrightness( + mTempBrightnessEvent); if (BrightnessUtils.isValidBrightnessValue(brightnessState) || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) { rawBrightnessState = mAutomaticBrightnessController @@ -1820,7 +1822,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal () -> { sendUpdatePowerState(); postBrightnessChangeRunnable(); - }, mUniqueDisplayId, mLogicalDisplay.getThermalBrightnessThrottlingDataIdLocked(), + }, mUniqueDisplayId, + mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId, ddConfig.getThermalBrightnessThrottlingDataMapByThrottlingId()); } diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 0b6d1c851dc4..e5c50e61bf25 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -203,6 +203,7 @@ final class LogicalDisplay { mIsEnabled = true; mIsInTransition = false; mThermalBrightnessThrottlingDataId = DisplayDeviceConfig.DEFAULT_ID; + mBaseDisplayInfo.thermalBrightnessThrottlingDataId = mThermalBrightnessThrottlingDataId; } public void setDevicePositionLocked(int position) { @@ -514,6 +515,7 @@ final class LogicalDisplay { mBaseDisplayInfo.layoutLimitedRefreshRate = mLayoutLimitedRefreshRate; mBaseDisplayInfo.thermalRefreshRateThrottling = mThermalRefreshRateThrottling; + mBaseDisplayInfo.thermalBrightnessThrottlingDataId = mThermalBrightnessThrottlingDataId; mPrimaryDisplayDeviceInfo = deviceInfo; mInfo.set(null); @@ -886,19 +888,14 @@ final class LogicalDisplay { } /** - * @return The ID of the brightness throttling data that this display should use. - */ - public String getThermalBrightnessThrottlingDataIdLocked() { - return mThermalBrightnessThrottlingDataId; - } - - /** * @param brightnessThrottlingDataId The ID of the brightness throttling data that this * display should use. */ public void setThermalBrightnessThrottlingDataIdLocked(String brightnessThrottlingDataId) { - mThermalBrightnessThrottlingDataId = - brightnessThrottlingDataId; + if (!Objects.equals(brightnessThrottlingDataId, mThermalBrightnessThrottlingDataId)) { + mThermalBrightnessThrottlingDataId = brightnessThrottlingDataId; + mDirty = true; + } } /** diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java index f6cf866dfa2f..95cbf9835e0f 100644 --- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java +++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java @@ -25,6 +25,7 @@ import android.view.Display; import com.android.internal.annotations.VisibleForTesting; import com.android.server.display.AutomaticBrightnessController; +import com.android.server.display.brightness.BrightnessEvent; import com.android.server.display.brightness.BrightnessReason; import com.android.server.display.brightness.BrightnessUtils; @@ -252,10 +253,12 @@ public class AutomaticBrightnessStrategy { /** * Evaluates the target automatic brightness of the associated display. + * @param brightnessEvent Event object to populate with details about why the specific + * brightness was chosen. */ - public float getAutomaticScreenBrightness() { + public float getAutomaticScreenBrightness(BrightnessEvent brightnessEvent) { float brightness = (mAutomaticBrightnessController != null) - ? mAutomaticBrightnessController.getAutomaticScreenBrightness() + ? mAutomaticBrightnessController.getAutomaticScreenBrightness(brightnessEvent) : PowerManager.BRIGHTNESS_INVALID_FLOAT; adjustAutomaticBrightnessStateIfValid(brightness); return brightness; diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java index fd94be9a2921..17f928af1c09 100644 --- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -65,7 +65,6 @@ import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.display.BrightnessSynchronizer; -import com.android.internal.display.RefreshRateSettingsUtils; import com.android.internal.os.BackgroundThread; import com.android.server.LocalServices; import com.android.server.display.DisplayDeviceConfig; @@ -1067,10 +1066,10 @@ public class DisplayModeDirector { @VisibleForTesting final class SettingsObserver extends ContentObserver { - private final Uri mSmoothDisplaySetting = - Settings.System.getUriFor(Settings.System.SMOOTH_DISPLAY); - private final Uri mForcePeakRefreshRateSetting = - Settings.System.getUriFor(Settings.System.FORCE_PEAK_REFRESH_RATE); + private final Uri mPeakRefreshRateSetting = + Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE); + private final Uri mMinRefreshRateSetting = + Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE); private final Uri mLowPowerModeSetting = Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE); private final Uri mMatchContentFrameRateSetting = @@ -1106,8 +1105,9 @@ public class DisplayModeDirector { public void observe() { final ContentResolver cr = mContext.getContentResolver(); - mInjector.registerSmoothDisplayObserver(cr, this); - mInjector.registerForcePeakRefreshRateObserver(cr, this); + mInjector.registerPeakRefreshRateObserver(cr, this); + cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this, + UserHandle.USER_SYSTEM); cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM); cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/, @@ -1149,8 +1149,8 @@ public class DisplayModeDirector { @Override public void onChange(boolean selfChange, Uri uri, int userId) { synchronized (mLock) { - if (mSmoothDisplaySetting.equals(uri) - || mForcePeakRefreshRateSetting.equals(uri)) { + if (mPeakRefreshRateSetting.equals(uri) + || mMinRefreshRateSetting.equals(uri)) { updateRefreshRateSettingLocked(); } else if (mLowPowerModeSetting.equals(uri)) { updateLowPowerModeSettingLocked(); @@ -1205,9 +1205,12 @@ public class DisplayModeDirector { } private void updateRefreshRateSettingLocked() { - updateRefreshRateSettingLocked(RefreshRateSettingsUtils.getMinRefreshRate(mContext), - RefreshRateSettingsUtils.getPeakRefreshRate(mContext, mDefaultPeakRefreshRate), - mDefaultRefreshRate); + final ContentResolver cr = mContext.getContentResolver(); + float minRefreshRate = Settings.System.getFloatForUser(cr, + Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId()); + float peakRefreshRate = Settings.System.getFloatForUser(cr, + Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate, cr.getUserId()); + updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate); } private void updateRefreshRateSettingLocked( @@ -2840,17 +2843,12 @@ public class DisplayModeDirector { } interface Injector { - Uri SMOOTH_DISPLAY_URI = Settings.System.getUriFor(Settings.System.SMOOTH_DISPLAY); - Uri FORCE_PEAK_REFRESH_RATE_URI = - Settings.System.getUriFor(Settings.System.FORCE_PEAK_REFRESH_RATE); + Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE); @NonNull DeviceConfigInterface getDeviceConfig(); - void registerSmoothDisplayObserver(@NonNull ContentResolver cr, - @NonNull ContentObserver observer); - - void registerForcePeakRefreshRateObserver(@NonNull ContentResolver cr, + void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, @NonNull ContentObserver observer); void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener, @@ -2890,16 +2888,9 @@ public class DisplayModeDirector { } @Override - public void registerSmoothDisplayObserver(@NonNull ContentResolver cr, - @NonNull ContentObserver observer) { - cr.registerContentObserver(SMOOTH_DISPLAY_URI, false /*notifyDescendants*/, - observer, UserHandle.USER_SYSTEM); - } - - @Override - public void registerForcePeakRefreshRateObserver(@NonNull ContentResolver cr, + public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, @NonNull ContentObserver observer) { - cr.registerContentObserver(FORCE_PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/, + cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/, observer, UserHandle.USER_SYSTEM); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java index 220a438d55ee..b82129bdf82f 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java @@ -35,6 +35,7 @@ public class HdmiCecMessageValidator { ERROR_DESTINATION, ERROR_PARAMETER, ERROR_PARAMETER_SHORT, + ERROR_PARAMETER_LONG, }) public @interface ValidationResult {}; @@ -43,6 +44,7 @@ public class HdmiCecMessageValidator { static final int ERROR_DESTINATION = 2; static final int ERROR_PARAMETER = 3; static final int ERROR_PARAMETER_SHORT = 4; + static final int ERROR_PARAMETER_LONG = 5; interface ParameterValidator { /** @@ -159,11 +161,13 @@ public class HdmiCecMessageValidator { addValidationInfo(Constants.MESSAGE_SET_MENU_LANGUAGE, new AsciiValidator(3), DEST_BROADCAST); - ParameterValidator statusRequestValidator = new OneByteRangeValidator(0x01, 0x03); + ParameterValidator statusRequestValidator = new MinimumOneByteRangeValidator(0x01, 0x03); addValidationInfo( - Constants.MESSAGE_DECK_CONTROL, new OneByteRangeValidator(0x01, 0x04), DEST_DIRECT); + Constants.MESSAGE_DECK_CONTROL, + new MinimumOneByteRangeValidator(0x01, 0x04), DEST_DIRECT); addValidationInfo( - Constants.MESSAGE_DECK_STATUS, new OneByteRangeValidator(0x11, 0x1F), DEST_DIRECT); + Constants.MESSAGE_DECK_STATUS, + new MinimumOneByteRangeValidator(0x11, 0x1F), DEST_DIRECT); addValidationInfo(Constants.MESSAGE_GIVE_DECK_STATUS, statusRequestValidator, DEST_DIRECT); addValidationInfo(Constants.MESSAGE_PLAY, new PlayModeValidator(), DEST_DIRECT); @@ -201,9 +205,11 @@ public class HdmiCecMessageValidator { // Messages for the Device Menu Control. addValidationInfo( - Constants.MESSAGE_MENU_REQUEST, new OneByteRangeValidator(0x00, 0x02), DEST_DIRECT); + Constants.MESSAGE_MENU_REQUEST, + new MinimumOneByteRangeValidator(0x00, 0x02), DEST_DIRECT); addValidationInfo( - Constants.MESSAGE_MENU_STATUS, new OneByteRangeValidator(0x00, 0x01), DEST_DIRECT); + Constants.MESSAGE_MENU_STATUS, + new MinimumOneByteRangeValidator(0x00, 0x01), DEST_DIRECT); // Messages for the Remote Control Passthrough. addValidationInfo( @@ -214,7 +220,7 @@ public class HdmiCecMessageValidator { // Messages for the Power Status. addValidationInfo( Constants.MESSAGE_REPORT_POWER_STATUS, - new OneByteRangeValidator(0x00, 0x03), + new MinimumOneByteRangeValidator(0x00, 0x03), DEST_DIRECT | DEST_BROADCAST); // Messages for the General Protocol. @@ -229,17 +235,17 @@ public class HdmiCecMessageValidator { oneByteValidator, DEST_DIRECT); addValidationInfo( Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE, - new OneByteRangeValidator(0x00, 0x01), + new MinimumOneByteRangeValidator(0x00, 0x01), DEST_ALL); addValidationInfo( Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS, - new OneByteRangeValidator(0x00, 0x01), + new SingleByteRangeValidator(0x00, 0x01), DEST_DIRECT); // Messages for the Audio Rate Control. addValidationInfo( Constants.MESSAGE_SET_AUDIO_RATE, - new OneByteRangeValidator(0x00, 0x06), + new MinimumOneByteRangeValidator(0x00, 0x06), DEST_DIRECT); // Messages for Feature Discovery. @@ -900,11 +906,32 @@ public class HdmiCecMessageValidator { } } - /** Check if the given parameters are one byte parameters and within range. */ - private static class OneByteRangeValidator implements ParameterValidator { + /** + * Check if the given parameters are at least one byte parameters + * and the first byte is within range. + */ + private static class MinimumOneByteRangeValidator implements ParameterValidator { + private final int mMinValue, mMaxValue; + + MinimumOneByteRangeValidator(int minValue, int maxValue) { + mMinValue = minValue; + mMaxValue = maxValue; + } + + @Override + public int isValid(byte[] params) { + if (params.length < 1) { + return ERROR_PARAMETER_SHORT; + } + return toErrorCode(isWithinRange(params[0], mMinValue, mMaxValue)); + } + } + + /** Check if the given parameters are exactly one byte parameters and within range. */ + private static class SingleByteRangeValidator implements ParameterValidator { private final int mMinValue, mMaxValue; - OneByteRangeValidator(int minValue, int maxValue) { + SingleByteRangeValidator(int minValue, int maxValue) { mMinValue = minValue; mMaxValue = maxValue; } @@ -913,6 +940,8 @@ public class HdmiCecMessageValidator { public int isValid(byte[] params) { if (params.length < 1) { return ERROR_PARAMETER_SHORT; + } else if (params.length > 1) { + return ERROR_PARAMETER_LONG; } return toErrorCode(isWithinRange(params[0], mMinValue, mMaxValue)); } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 75fe63a66206..9cd5272b356b 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -1611,6 +1611,7 @@ public class HdmiControlService extends SystemService { @HdmiCecMessageValidator.ValidationResult int validationResult = message.getValidationResult(); if (validationResult == HdmiCecMessageValidator.ERROR_PARAMETER + || validationResult == HdmiCecMessageValidator.ERROR_PARAMETER_LONG || !verifyPhysicalAddresses(message)) { return Constants.ABORT_INVALID_OPERAND; } else if (validationResult != HdmiCecMessageValidator.OK diff --git a/services/core/java/com/android/server/infra/ServiceNameBaseResolver.java b/services/core/java/com/android/server/infra/ServiceNameBaseResolver.java index 76ea05e36141..66ce5c79663e 100644 --- a/services/core/java/com/android/server/infra/ServiceNameBaseResolver.java +++ b/services/core/java/com/android/server/infra/ServiceNameBaseResolver.java @@ -268,7 +268,7 @@ public abstract class ServiceNameBaseResolver implements ServiceNameResolver { } if (enabled) { Slog.i(TAG, "disabling default service for user " + userId); - mDefaultServicesDisabled.removeAt(userId); + mDefaultServicesDisabled.delete(userId); } else { Slog.i(TAG, "enabling default service for user " + userId); mDefaultServicesDisabled.put(userId, true); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index c70d55510493..57f8d1478905 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -856,13 +856,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("ImfLock.class") private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>(); - private static final class SoftInputShowHideHistory { + @VisibleForTesting + static final class SoftInputShowHideHistory { private final Entry[] mEntries = new Entry[16]; private int mNextIndex = 0; private static final AtomicInteger sSequenceNumber = new AtomicInteger(0); - private static final class Entry { + static final class Entry { final int mSequenceNumber = sSequenceNumber.getAndIncrement(); + @Nullable final ClientState mClientState; @SoftInputModeFlags final int mFocusedWindowSoftInputMode; @@ -874,7 +876,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final boolean mInFullscreenMode; @NonNull final String mFocusedWindowName; - @NonNull + @Nullable final EditorInfo mEditorInfo; @NonNull final String mRequestWindowName; @@ -953,9 +955,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub pw.print(prefix); pw.print(" editorInfo: "); - pw.print(" inputType=" + entry.mEditorInfo.inputType); - pw.print(" privateImeOptions=" + entry.mEditorInfo.privateImeOptions); - pw.println(" fieldId (viewId)=" + entry.mEditorInfo.fieldId); + if (entry.mEditorInfo != null) { + pw.print(" inputType=" + entry.mEditorInfo.inputType); + pw.print(" privateImeOptions=" + entry.mEditorInfo.privateImeOptions); + pw.println(" fieldId (viewId)=" + entry.mEditorInfo.fieldId); + } else { + pw.println("null"); + } pw.print(prefix); pw.println(" focusedWindowSoftInputMode=" + InputMethodDebug.softInputModeToString( diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 33e6a8f15df2..f0ab815db2c1 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -316,7 +316,6 @@ import com.android.server.notification.toast.TextToastRecord; import com.android.server.notification.toast.ToastRecord; import com.android.server.pm.PackageManagerService; import com.android.server.pm.UserManagerInternal; -import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.policy.PermissionPolicyInternal; import com.android.server.powerstats.StatsPullAtomCallbackImpl; import com.android.server.statusbar.StatusBarManagerInternal; @@ -2559,8 +2558,8 @@ public class NotificationManagerService extends SystemService { Context.STATS_MANAGER), getContext().getSystemService(TelephonyManager.class), LocalServices.getService(ActivityManagerInternal.class), - createToastRateLimiter(), new PermissionHelper(LocalServices.getService( - PermissionManagerServiceInternal.class), AppGlobals.getPackageManager(), + createToastRateLimiter(), new PermissionHelper(getContext(), + AppGlobals.getPackageManager(), AppGlobals.getPermissionManager()), LocalServices.getService(UsageStatsManagerInternal.class), getContext().getSystemService(TelecomManager.class), @@ -11599,6 +11598,8 @@ public class NotificationManagerService extends SystemService { StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); try { listener.onNotificationPosted(sbnHolder, rankingUpdate); + } catch (android.os.DeadObjectException ex) { + Slog.wtf(TAG, "unable to notify listener (posted): " + info, ex); } catch (RemoteException ex) { Slog.e(TAG, "unable to notify listener (posted): " + info, ex); } @@ -11620,6 +11621,8 @@ public class NotificationManagerService extends SystemService { reason = REASON_LISTENER_CANCEL; } listener.onNotificationRemoved(sbnHolder, rankingUpdate, stats, reason); + } catch (android.os.DeadObjectException ex) { + Slog.wtf(TAG, "unable to notify listener (removed): " + info, ex); } catch (RemoteException ex) { Slog.e(TAG, "unable to notify listener (removed): " + info, ex); } @@ -11630,6 +11633,8 @@ public class NotificationManagerService extends SystemService { final INotificationListener listener = (INotificationListener) info.service; try { listener.onNotificationRankingUpdate(rankingUpdate); + } catch (android.os.DeadObjectException ex) { + Slog.wtf(TAG, "unable to notify listener (ranking update): " + info, ex); } catch (RemoteException ex) { Slog.e(TAG, "unable to notify listener (ranking update): " + info, ex); } diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java index b6fd822b7687..93c83e181ec1 100644 --- a/services/core/java/com/android/server/notification/PermissionHelper.java +++ b/services/core/java/com/android/server/notification/PermissionHelper.java @@ -25,6 +25,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.Manifest; import android.annotation.NonNull; import android.annotation.UserIdInt; +import android.content.Context; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -37,7 +38,6 @@ import android.util.Pair; import android.util.Slog; import com.android.internal.util.ArrayUtils; -import com.android.server.pm.permission.PermissionManagerServiceInternal; import java.util.Collections; import java.util.HashSet; @@ -53,13 +53,13 @@ public final class PermissionHelper { private static final String NOTIFICATION_PERMISSION = Manifest.permission.POST_NOTIFICATIONS; - private final PermissionManagerServiceInternal mPmi; + private final Context mContext; private final IPackageManager mPackageManager; private final IPermissionManager mPermManager; - public PermissionHelper(PermissionManagerServiceInternal pmi, IPackageManager packageManager, + public PermissionHelper(Context context, IPackageManager packageManager, IPermissionManager permManager) { - mPmi = pmi; + mContext = context; mPackageManager = packageManager; mPermManager = permManager; } @@ -71,7 +71,7 @@ public final class PermissionHelper { public boolean hasPermission(int uid) { final long callingId = Binder.clearCallingIdentity(); try { - return mPmi.checkUidPermission(uid, NOTIFICATION_PERMISSION) == PERMISSION_GRANTED; + return mContext.checkPermission(NOTIFICATION_PERMISSION, -1, uid) == PERMISSION_GRANTED; } finally { Binder.restoreCallingIdentity(callingId); } @@ -193,8 +193,8 @@ public final class PermissionHelper { return; } - boolean currentlyGranted = mPmi.checkPermission(packageName, NOTIFICATION_PERMISSION, - userId) != PackageManager.PERMISSION_DENIED; + int uid = mPackageManager.getPackageUid(packageName, 0, userId); + boolean currentlyGranted = hasPermission(uid); if (grant && !currentlyGranted) { mPermManager.grantRuntimePermission(packageName, NOTIFICATION_PERMISSION, userId); } else if (!grant && currentlyGranted) { diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java index 48ee64f888cd..8a2888881e59 100644 --- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java +++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java @@ -437,6 +437,16 @@ public class DefaultCrossProfileIntentFiltersUtils { .addCategory(Intent.CATEGORY_DEFAULT) .build(); + private static final DefaultCrossProfileIntentFilter CLONE_TO_PARENT_PHOTOPICKER_SELECTION = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + /* flags= */ 0x00000018, // 0x00000018 means FLAG_IS_PACKAGE_FOR_FILTER + // and FLAG_ALLOW_CHAINED_RESOLUTION set + /* letsPersonalDataIntoProfile= */ false) + .addAction(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP) + .addCategory(Intent.CATEGORY_DEFAULT) + .build(); + /* Allowing send action from clone to parent profile to share content from clone apps to parent apps @@ -611,7 +621,8 @@ public class DefaultCrossProfileIntentFiltersUtils { CLONE_TO_PARENT_VIEW_ACTION, CLONE_TO_PARENT_PICK_INSERT_ACTION, CLONE_TO_PARENT_DIAL_DATA, - CLONE_TO_PARENT_SMS_MMS + CLONE_TO_PARENT_SMS_MMS, + CLONE_TO_PARENT_PHOTOPICKER_SELECTION ); } } diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java index 92f0011339a5..9c3b38ab51a3 100644 --- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java +++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java @@ -395,7 +395,7 @@ public final class SingleKeyGestureDetector { private class KeyHandler extends Handler { KeyHandler() { - super(Looper.getMainLooper()); + super(Looper.myLooper()); } @Override diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 580b4d62bdd0..077f8d527ab5 100755 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -1124,18 +1124,6 @@ class TvInputHardwareManager implements TvInputHal.Callback { } } - private boolean setTvMessageEnabled(int deviceId, TvStreamConfig streamConfig, int type, - boolean enabled) { - synchronized (mImplLock) { - if (mReleased) { - return false; - } - - return mHal.setTvMessageEnabled(deviceId, streamConfig, type, enabled) - == TvInputHal.SUCCESS; - } - } - private boolean startCapture(Surface surface, TvStreamConfig config) { synchronized (mImplLock) { if (mReleased) { diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 98d2d3d4b997..9020cb3405a2 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1353,9 +1353,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub void complete() { // Only changes from home+lock to just home or lock need attention - // If setting the wallpaper fails, this callback will be called - // when the wallpaper is detached, in which case wallpapers may have - // already changed. Make sure we're not overwriting a more recent wallpaper. if (mNewWallpaper.mSystemWasBoth) { if (DEBUG) { Slog.v(TAG, "Handling change from system+lock wallpaper"); @@ -1378,7 +1375,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub mOriginalSystem.wallpaperComponent; lockWp.connection = mOriginalSystem.connection; lockWp.connection.mWallpaper = lockWp; - updateEngineFlags(mOriginalSystem, FLAG_LOCK); + mOriginalSystem.mWhich = FLAG_LOCK; + updateEngineFlags(mOriginalSystem); notifyWallpaperColorsChanged(lockWp, FLAG_LOCK); } else { // Failed rename, use current system wp for both @@ -1387,7 +1385,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } WallpaperData currentSystem = mWallpaperMap.get(mNewWallpaper.userId); currentSystem.mWhich = FLAG_SYSTEM | FLAG_LOCK; - updateEngineFlags(currentSystem, FLAG_SYSTEM | FLAG_LOCK); + updateEngineFlags(currentSystem); mLockWallpaperMap.remove(mNewWallpaper.userId); } } else { @@ -1396,7 +1394,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub Slog.v(TAG, "live system+lock to system success"); } mOriginalSystem.mWhich = FLAG_LOCK; - updateEngineFlags(mOriginalSystem, FLAG_LOCK); + updateEngineFlags(mOriginalSystem); mLockWallpaperMap.put(mNewWallpaper.userId, mOriginalSystem); mLastLockWallpaper = mOriginalSystem; notifyWallpaperColorsChanged(mOriginalSystem, FLAG_LOCK); @@ -1409,7 +1407,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub WallpaperData currentSystem = mWallpaperMap.get(mNewWallpaper.userId); if (currentSystem.wallpaperId == mOriginalSystem.wallpaperId) { currentSystem.mWhich = FLAG_SYSTEM; - updateEngineFlags(currentSystem, FLAG_SYSTEM); + updateEngineFlags(currentSystem); } } } @@ -1422,24 +1420,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub Slog.v(TAG, "new lastLockWp: " + mLastLockWallpaper); } } - - private void updateEngineFlags(WallpaperData wallpaper, @SetWallpaperFlags int which) { - if (wallpaper.connection == null) { - return; - } - wallpaper.connection.forEachDisplayConnector( - connector -> { - try { - if (connector.mEngine != null) { - connector.mEngine.setWallpaperFlags(which); - mWindowManagerInternal.setWallpaperShowWhenLocked( - connector.mToken, (which & FLAG_LOCK) != 0); - } - } catch (RemoteException e) { - Slog.e(TAG, "Failed to update wallpaper engine flags", e); - } - }); - } } class MyPackageMonitor extends PackageMonitor { @@ -3095,6 +3075,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub newWallpaper.userId); if (lockedWallpaper != null) { detachWallpaperLocked(lockedWallpaper); + if (same) { + updateEngineFlags(newWallpaper); + } } mLockWallpaperMap.remove(newWallpaper.userId); } @@ -3430,6 +3413,27 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } + // Updates the given wallpaper's Engine so that its destination flags are the same as those of + // the wallpaper, e.g., after a wallpaper has been changed from displaying on home+lock to home + // or lock only. + private void updateEngineFlags(WallpaperData wallpaper) { + if (wallpaper.connection == null) { + return; + } + wallpaper.connection.forEachDisplayConnector( + connector -> { + try { + if (connector.mEngine != null) { + connector.mEngine.setWallpaperFlags(wallpaper.mWhich); + mWindowManagerInternal.setWallpaperShowWhenLocked( + connector.mToken, (wallpaper.mWhich & FLAG_LOCK) != 0); + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to update wallpaper engine flags", e); + } + }); + } + private void clearWallpaperComponentLocked(WallpaperData wallpaper) { wallpaper.wallpaperComponent = null; detachWallpaperLocked(wallpaper); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 78c066bdc212..c3cd3eca84f2 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -5400,7 +5400,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; } if (inFinishingTransition) { - // Let the finishing transition commit the visibility. + // Let the finishing transition commit the visibility, but let the controller know + // about it so that we can recover from degenerate cases. + mTransitionController.mValidateCommitVis.add(this); return; } // If we are preparing an app transition, then delay changing diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index f5079d37b324..e47787e97f20 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -33,6 +33,7 @@ import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.os.Process.SYSTEM_UID; +import static android.view.MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; @@ -209,7 +210,8 @@ class RecentTasks { private final PointerEventListener mListener = new PointerEventListener() { @Override public void onPointerEvent(MotionEvent ev) { - if (!mFreezeTaskListReordering || ev.getAction() != MotionEvent.ACTION_DOWN) { + if (!mFreezeTaskListReordering || ev.getAction() != MotionEvent.ACTION_DOWN + || ev.getClassification() == CLASSIFICATION_MULTI_FINGER_SWIPE) { // Skip if we aren't freezing or starting a gesture return; } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 3f4296a5eee5..d3edeaebbf99 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -3239,7 +3239,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if (task.getActivity(activity -> !activity.finishing && activity.mUserId == userId) != null) { mService.getTaskChangeNotificationController().notifyTaskProfileLocked( - task.getTaskInfo()); + task.getTaskInfo(), userId); } }, true /* traverseTopToBottom */); } diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index b5df3e0937ec..bbb85636f1ee 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -193,6 +193,7 @@ class ScreenRotationAnimation { .setSourceCrop(new Rect(0, 0, width, height)) .setAllowProtected(true) .setCaptureSecureLayers(true) + .setHintForSeamlessTransition(true) .build(); screenshotBuffer = ScreenCapture.captureDisplay(captureArgs); } else { @@ -202,6 +203,7 @@ class ScreenRotationAnimation { .setCaptureSecureLayers(true) .setAllowProtected(true) .setSourceCrop(new Rect(0, 0, width, height)) + .setHintForSeamlessTransition(true) .build(); screenshotBuffer = ScreenCapture.captureLayers(captureArgs); } diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index 49d064f83345..9324e29daafb 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -144,7 +144,7 @@ class TaskChangeNotificationController { }; private final TaskStackConsumer mNotifyTaskProfileLocked = (l, m) -> { - l.onTaskProfileLocked((RunningTaskInfo) m.obj); + l.onTaskProfileLocked((RunningTaskInfo) m.obj, m.arg1); }; private final TaskStackConsumer mNotifyTaskSnapshotChanged = (l, m) -> { @@ -467,9 +467,9 @@ class TaskChangeNotificationController { * Notify listeners that the task has been put in a locked state because one or more of the * activities inside it belong to a managed profile user that has been locked. */ - void notifyTaskProfileLocked(ActivityManager.RunningTaskInfo taskInfo) { + void notifyTaskProfileLocked(RunningTaskInfo taskInfo, int userId) { final Message msg = mHandler.obtainMessage(NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG, - taskInfo); + userId, 0, taskInfo); forAllLocalListeners(mNotifyTaskProfileLocked, msg); msg.sendToTarget(); } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index d531ad175f10..7bd3b3253614 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -55,6 +55,7 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; import static android.window.TransitionInfo.FLAG_TASK_LAUNCHING_BEHIND; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import static android.window.TransitionInfo.FLAG_WILL_IME_SHOWN; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT; import static com.android.server.wm.ActivityRecord.State.RESUMED; import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM; @@ -65,12 +66,14 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityOptions; import android.app.IApplicationThread; import android.content.pm.ActivityInfo; import android.graphics.Point; import android.graphics.Rect; import android.hardware.HardwareBuffer; import android.os.Binder; +import android.os.Bundle; import android.os.IBinder; import android.os.IRemoteCallback; import android.os.RemoteException; @@ -85,6 +88,7 @@ import android.view.SurfaceControl; import android.view.WindowManager; import android.window.ScreenCapture; import android.window.TransitionInfo; +import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; @@ -185,7 +189,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { final ArraySet<WindowContainer> mParticipants = new ArraySet<>(); /** The final animation targets derived from participants after promotion. */ - private ArrayList<ChangeInfo> mTargets; + ArrayList<ChangeInfo> mTargets; /** The displays that this transition is running on. */ private final ArrayList<DisplayContent> mTargetDisplays = new ArrayList<>(); @@ -271,9 +275,14 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { /** Any 2 transitions of this type can run in parallel with each other. Used for testing. */ static final int PARALLEL_TYPE_MUTUAL = 1; + /** This is a recents transition. */ + static final int PARALLEL_TYPE_RECENTS = 2; + + @IntDef(prefix = { "PARALLEL_TYPE_" }, value = { PARALLEL_TYPE_NONE, - PARALLEL_TYPE_MUTUAL + PARALLEL_TYPE_MUTUAL, + PARALLEL_TYPE_RECENTS }) @Retention(RetentionPolicy.SOURCE) @interface ParallelType {} @@ -328,6 +337,21 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { mFlags |= flag; } + void calcParallelCollectType(WindowContainerTransaction wct) { + for (int i = 0; i < wct.getHierarchyOps().size(); ++i) { + final WindowContainerTransaction.HierarchyOp hop = wct.getHierarchyOps().get(i); + if (hop.getType() != HIERARCHY_OP_TYPE_PENDING_INTENT) continue; + final Bundle b = hop.getLaunchOptions(); + if (b == null || b.isEmpty()) continue; + final boolean transientLaunch = b.getBoolean(ActivityOptions.KEY_TRANSIENT_LAUNCH); + if (transientLaunch) { + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, + "Starting a Recents transition which can be parallel."); + mParallelCollectType = PARALLEL_TYPE_RECENTS; + } + } + } + /** Records an activity as transient-launch. This activity must be already collected. */ void setTransientLaunch(@NonNull ActivityRecord activity, @Nullable Task restoreBelow) { if (mTransientLaunches == null) { @@ -380,6 +404,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { return false; } + boolean hasTransientLaunch() { + return mTransientLaunches != null && !mTransientLaunches.isEmpty(); + } + boolean isTransientLaunch(@NonNull ActivityRecord activity) { return mTransientLaunches != null && mTransientLaunches.containsKey(activity); } @@ -984,13 +1012,20 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // Record all the now-hiding activities so that they are committed. Just use // mParticipants because we can avoid a new list this way. for (int i = 0; i < mTransientHideTasks.size(); ++i) { - // Only worry about tasks that were actually hidden. Otherwise, we could end-up - // committing visibility for activity-level changes that aren't part of this - // transition. - if (mTransientHideTasks.get(i).isVisibleRequested()) continue; - mTransientHideTasks.get(i).forAllActivities(r -> { + final Task rootTask = mTransientHideTasks.get(i); + rootTask.forAllActivities(r -> { // Only check leaf-tasks that were collected if (!mParticipants.contains(r.getTask())) return; + if (rootTask.isVisibleRequested()) { + // This transient-hide didn't hide, so don't commit anything (otherwise we + // could prematurely commit invisible on unrelated activities). To be safe, + // though, notify the controller to prevent degenerate cases. + if (!r.isVisibleRequested()) { + mController.mValidateCommitVis.add(r); + } + return; + } + // This did hide: commit immediately so that other transitions know about it. mParticipants.add(r); }); } @@ -2964,11 +2999,14 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { Rect cropBounds = new Rect(bounds); cropBounds.offsetTo(0, 0); + final boolean isDisplayRotation = wc.asDisplayContent() != null + && wc.asDisplayContent().isRotationChanging(); ScreenCapture.LayerCaptureArgs captureArgs = new ScreenCapture.LayerCaptureArgs.Builder(wc.getSurfaceControl()) .setSourceCrop(cropBounds) .setCaptureSecureLayers(true) .setAllowProtected(true) + .setHintForSeamlessTransition(isDisplayRotation) .build(); ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = ScreenCapture.captureLayers(captureArgs); @@ -2979,8 +3017,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { Slog.w(TAG, "Failed to capture screenshot for " + wc); return false; } - final boolean isDisplayRotation = wc.asDisplayContent() != null - && wc.asDisplayContent().isRotationChanging(); // Some tests may check the name "RotationLayer" to detect display rotation. final String name = isDisplayRotation ? "RotationLayer" : "transition snapshot: " + wc; SurfaceControl snapshotSurface = wc.makeAnimationLeash() diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 7950edaaa267..e1da91a6cb21 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -133,6 +133,13 @@ class TransitionController { final ArrayList<Runnable> mStateValidators = new ArrayList<>(); /** + * List of activity-records whose visibility changed outside the main/tracked part of a + * transition (eg. in the finish-transaction). These will be checked when idle to recover from + * degenerate states. + */ + final ArrayList<ActivityRecord> mValidateCommitVis = new ArrayList<>(); + + /** * Currently playing transitions (in the order they were started). When finished, records are * removed from this list. */ @@ -848,6 +855,15 @@ class TransitionController { } } mStateValidators.clear(); + for (int i = 0; i < mValidateCommitVis.size(); ++i) { + final ActivityRecord ar = mValidateCommitVis.get(i); + if (!ar.isVisibleRequested() && ar.isVisible()) { + Slog.e(TAG, "Uncommitted visibility change: " + ar); + ar.commitVisibility(ar.isVisibleRequested(), false /* layout */, + false /* fromTransition */); + } + } + mValidateCommitVis.clear(); } /** @@ -879,6 +895,8 @@ class TransitionController { // If it's a legacy sync, then it needs to wait until there is no collecting transition. if (queued.mTransition == null) return; if (!canStartCollectingNow(queued.mTransition)) return; + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from collecting" + + " to waiting.", mCollectingTransition.getSyncId()); mWaitingTransitions.add(mCollectingTransition); mCollectingTransition = null; } else if (mSyncEngine.hasActiveSync()) { @@ -930,10 +948,37 @@ class TransitionController { * in {@link #getIsIndependent} later. */ boolean getCanBeIndependent(Transition collecting, Transition queued) { + // For tests if (queued.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL && collecting.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL) { return true; } + // For recents + if (queued.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) { + if (collecting.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) { + // Must serialize with itself. + return false; + } + // allow this if `collecting` only has activities + for (int i = 0; i < collecting.mParticipants.size(); ++i) { + final WindowContainer wc = collecting.mParticipants.valueAt(i); + final ActivityRecord ar = wc.asActivityRecord(); + if (ar == null && wc.asWindowState() == null && wc.asWindowToken() == null) { + // Is task or above, so can't be independent + return false; + } + if (ar != null && ar.isActivityTypeHomeOrRecents()) { + // It's a recents or home type, so it conflicts. + return false; + } + } + return true; + } else if (collecting.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) { + // We can collect simultaneously with recents if it is populated. This is because + // we know that recents will not collect/trampoline any more stuff. If anything in the + // queued transition overlaps, it will end up just waiting in sync-queue anyways. + return true; + } return false; } @@ -942,11 +987,47 @@ class TransitionController { * `running` is playing based on its current state. */ static boolean getIsIndependent(Transition running, Transition incoming) { + // For tests if (running.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL && incoming.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL) { return true; } - return false; + // For now there's only one mutually-independent pair: an all activity-level transition and + // a transient-launch where none of the activities are part of the transient-launch task, + // so the following logic is hard-coded specifically for this. + // Also, we currently restrict valid transient-launches to just recents. + final Transition recents; + final Transition other; + if (running.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS + && running.hasTransientLaunch()) { + if (incoming.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) { + // Recents can't be independent from itself. + return false; + } + recents = running; + other = incoming; + } else if (incoming.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS + && incoming.hasTransientLaunch()) { + recents = incoming; + other = running; + } else { + return false; + } + // Check against *targets* because that is the post-promotion set of containers that are + // actually animating. + for (int i = 0; i < other.mTargets.size(); ++i) { + final WindowContainer wc = other.mTargets.get(i).mContainer; + final ActivityRecord ar = wc.asActivityRecord(); + if (ar == null && wc.asWindowState() == null && wc.asWindowToken() == null) { + // Is task or above, so for now don't let them be independent. + return false; + } + if (ar != null && recents.isTransientLaunch(ar)) { + // Change overlaps with recents, so serialize. + return false; + } + } + return true; } void assignTrack(Transition transition, TransitionInfo info) { @@ -970,9 +1051,15 @@ class TransitionController { if (track < 0) { // Didn't overlap with anything, so give it its own track track = mTrackCount; + if (track > 0) { + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Playing #%d in parallel on " + + "track #%d", transition.getSyncId(), track); + } } if (sync) { info.setFlags(info.getFlags() | TransitionInfo.FLAG_SYNC); + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Marking #%d animation as SYNC.", + transition.getSyncId()); } transition.mAnimationTrack = track; info.setTrack(track); @@ -1145,6 +1232,8 @@ class TransitionController { // Check if we can run in parallel here. if (canStartCollectingNow(transit)) { // start running in parallel. + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from" + + " collecting to waiting.", mCollectingTransition.getSyncId()); mWaitingTransitions.add(mCollectingTransition); mCollectingTransition = null; moveToCollecting(transit); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 510e6756b8ef..a5cdd0b43eb0 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -3594,8 +3594,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< && !mTransitionController.useShellTransitionsRotation()) { if (deltaRotation != Surface.ROTATION_0) { updateSurfaceRotation(t, deltaRotation, null /* positionLeash */); + t.setFixedTransformHint(mSurfaceControl, + getWindowConfiguration().getDisplayRotation()); } else if (deltaRotation != mLastDeltaRotation) { t.setMatrix(mSurfaceControl, 1, 0, 0, 1); + t.unsetFixedTransformHint(mSurfaceControl); } } mLastDeltaRotation = deltaRotation; diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index cd42528ad79b..d5aa520e1b6e 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -299,6 +299,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final boolean needsSetReady = t != null; final Transition nextTransition = new Transition(type, 0 /* flags */, mTransitionController, mService.mWindowManager.mSyncEngine); + nextTransition.calcParallelCollectType(wct); mTransitionController.startCollectOrQueue(nextTransition, (deferred) -> { nextTransition.start(); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 6f07b7719be0..032f08a92abb 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1980,19 +1980,16 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP /** * Like isOnScreen(), but we don't return true if the window is part - * of a transition but has not yet started animating. + * of a transition that has not yet been started. */ boolean isReadyForDisplay() { - if (!mHasSurface || mDestroying || !isVisibleByPolicy()) { - return false; - } - if (mToken.waitingToShow && getDisplayContent().mAppTransition.isTransitionSet() - && !isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_APP_TRANSITION)) { + if (mToken.waitingToShow && getDisplayContent().mAppTransition.isTransitionSet()) { return false; } final boolean parentAndClientVisible = !isParentWindowHidden() && mViewVisibility == View.VISIBLE && mToken.isVisible(); - return parentAndClientVisible || isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_ALL); + return mHasSurface && isVisibleByPolicy() && !mDestroying + && (parentAndClientVisible || isAnimating(TRANSITION | PARENTS)); } boolean isFullyTransparent() { diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index da54b15fd0a4..4c5efef795d8 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -596,6 +596,7 @@ class WindowToken extends WindowContainer<WindowState> { .build(); t.setPosition(leash, mLastSurfacePosition.x, mLastSurfacePosition.y); t.reparent(getSurfaceControl(), leash); + t.setFixedTransformHint(leash, getWindowConfiguration().getDisplayRotation()); mFixedRotationTransformLeash = leash; updateSurfaceRotation(t, rotation, mFixedRotationTransformLeash); return mFixedRotationTransformLeash; diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java index fc7fd1afe58f..b073ff400e44 100644 --- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java @@ -98,7 +98,9 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR mRequestId, mClientRequest, mClientAppInfo.getPackageName(), PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(), - Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)), + Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS), + // TODO(b/279480457): populate + /*defaultProviderId=*/new ArrayList<>()), providerDataList); mClientCallback.onPendingIntent(mPendingIntent); } catch (RemoteException e) { diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java index 50e5163cea55..4e82ee71ac1b 100644 --- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java +++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java @@ -35,6 +35,7 @@ import java.util.Map; /** * For all future metric additions, this will contain their names for local usage after importing * from {@link com.android.internal.util.FrameworkStatsLog}. + * TODO(b/271135048) - Emit all atoms, including all V4 atoms (specifically the rest of track 1). */ public class MetricUtilities { private static final boolean LOG_FLAG = true; diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java index 520b937d24c5..40514b2a997f 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java @@ -75,7 +75,8 @@ public final class ProviderCreateSession extends ProviderSession< CreateCredentialRequest providerCreateRequest = createProviderRequest(providerInfo.getCapabilities(), createRequestSession.mClientRequest, - createRequestSession.mClientAppInfo); + createRequestSession.mClientAppInfo, + providerInfo.isSystemProvider()); if (providerCreateRequest != null) { return new ProviderCreateSession( context, @@ -114,9 +115,16 @@ public final class ProviderCreateSession extends ProviderSession< } @Nullable - private static CreateCredentialRequest createProviderRequest(List<String> providerCapabilities, + private static CreateCredentialRequest createProviderRequest( + List<String> providerCapabilities, android.credentials.CreateCredentialRequest clientRequest, - CallingAppInfo callingAppInfo) { + CallingAppInfo callingAppInfo, + boolean isSystemProvider) { + if (clientRequest.isSystemProviderRequired() && !isSystemProvider) { + // Request requires system provider but this session does not correspond to a + // system service + return null; + } String capability = clientRequest.getType(); if (providerCapabilities.contains(capability)) { return new CreateCredentialRequest(callingAppInfo, capability, diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java index a62d9e805a93..0c3d2a4c000a 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java @@ -40,7 +40,6 @@ import android.service.credentials.CredentialEntry; import android.service.credentials.CredentialProviderService; import android.service.credentials.GetCredentialRequest; import android.service.credentials.RemoteEntry; -import android.util.Log; import android.util.Pair; import android.util.Slog; @@ -413,11 +412,9 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential */ private boolean onAuthenticationEntrySelected( @Nullable ProviderPendingIntentResponse providerPendingIntentResponse) { - Log.i(TAG, "onAuthenticationEntrySelected"); // Authentication entry is expected to have a BeginGetCredentialResponse instance. If it // does not have it, we remove the authentication entry and do not add any more content. if (providerPendingIntentResponse == null) { - Log.i(TAG, "providerPendingIntentResponse is null"); // Nothing received. This is equivalent to no content received. return false; } diff --git a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java index c10f5640c466..ead86cefc5d4 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java @@ -266,7 +266,7 @@ public class ProviderRegistryGetSession extends ProviderSession<CredentialOption .collect(Collectors.toList()); updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED, /*source=*/ CredentialsSource.REGISTRY); - // TODO(use metric later) + // TODO(b/273353677) : metric should be emitted similarly to sibling classes } @Nullable diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java index d02a8c1ee510..73fdc1ce2635 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java @@ -268,12 +268,9 @@ public abstract class ProviderSession<T, R> /*pId=*/-1, appInfo.uid) == PackageManager.PERMISSION_GRANTED) { return true; } - } catch (SecurityException e) { + } catch (SecurityException | PackageManager.NameNotFoundException e) { Slog.e(TAG, "Error getting info for " + mComponentName.flattenToString(), e); return false; - } catch (PackageManager.NameNotFoundException e) { - Slog.i(TAG, "Error getting info for " + mComponentName.flattenToString(), e); - return false; } return false; } diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java index 721d3d782653..b212606b8271 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java @@ -30,7 +30,6 @@ import java.util.stream.Collectors; * Some types are redundant across these metric collectors, but that has debug use-cases as * these data-types are available at different moments of the flow (and typically, one can feed * into the next). - * TODO(b/270403549) - iterate on this in V3+ */ public class CandidatePhaseMetric { @@ -56,10 +55,7 @@ public class CandidatePhaseMetric { private int mProviderQueryStatus = -1; // Indicates if an exception was thrown by this provider, false by default private boolean mHasException = false; - // Indicates the number of total entries available. We can also locally store the entries, but - // cannot emit them in the current split form. TODO(b/271135048) - possibly readjust candidate - // entries. Also, it may be okay to remove this and instead aggregate from inner counts. - // Defaults to -1 + // Indicates the number of total entries available, defaults to -1 private int mNumEntriesTotal = -1; // The count of action entries from this provider, defaults to -1 private int mActionEntryCount = -1; diff --git a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java index c80cc24fa455..8f08bb02dfd0 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java @@ -29,11 +29,8 @@ import java.util.List; * Some types are redundant across these metric collectors, but that has debug use-cases as * these data-types are available at different moments of the flow (and typically, one can feed * into the next). - * TODO(b/270403549) - iterate on this in V3+ */ public class ChosenProviderFinalPhaseMetric { - - // TODO(b/270403549) - applies elsewhere, likely removed or replaced w/ some hashed/count index private static final String TAG = "ChosenFinalPhaseMetric"; // The session id associated with this API call, used to unite split emits private int mSessionId = -1; diff --git a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java index 0ecd9cc79e48..5cfb0e7d375b 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java @@ -26,7 +26,6 @@ import java.util.Map; * Some types are redundant across these metric collectors, but that has debug use-cases as * these data-types are available at different moments of the flow (and typically, one can feed * into the next). - * TODO(b/270403549) - iterate on this in V3+ */ public class InitialPhaseMetric { private static final String TAG = "InitialPhaseMetric"; @@ -47,7 +46,6 @@ public class InitialPhaseMetric { private long mCredentialServiceBeginQueryTimeNanoseconds = -1; // Indicates if the origin was specified when making this API request - // TODO(b/271135048) - Emit once metrics approved private boolean mOriginSpecified = false; // Stores the deduped request information, particularly {"req":5}. diff --git a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java index 547c09a625f6..4ecdfef401b6 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java @@ -337,10 +337,6 @@ public class RequestSessionMetric { */ public void logApiCalledAtFinish(int apiStatus) { try { - // TODO (b/270403549) - this browsing phase object is fine but also have a new emit - // For the returned types by authentication entries - i.e. a CandidatePhase During - // Browse - // Possibly think of adding in more atoms for other APIs as well. logApiCalledFinalPhase(mChosenProviderFinalPhaseMetric, mCandidateBrowsingPhaseMetric, apiStatus, ++mSequenceCounter); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index a35f34dcde86..675ebd3ddd60 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -521,6 +521,7 @@ import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.text.DateFormat; import java.time.LocalDate; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -533,6 +534,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.Queue; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -568,7 +570,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final int REQUEST_PROFILE_OFF_DEADLINE = 5572; + // Binary XML serializer doesn't support longer strings + private static final int MAX_POLICY_STRING_LENGTH = 65535; + // FrameworkParsingPackageUtils#MAX_FILE_NAME_SIZE, Android packages are used in dir names. + private static final int MAX_PACKAGE_NAME_LENGTH = 223; + private static final int MAX_PROFILE_NAME_LENGTH = 200; + private static final int MAX_LONG_SUPPORT_MESSAGE_LENGTH = 20000; + private static final int MAX_SHORT_SUPPORT_MESSAGE_LENGTH = 200; + private static final int MAX_ORG_NAME_LENGTH = 200; private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1); @@ -864,7 +874,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // TODO(b/265683382) remove the flag after rollout. private static final String KEEP_PROFILES_RUNNING_FLAG = "enable_keep_profiles_running"; - private static final boolean DEFAULT_KEEP_PROFILES_RUNNING_FLAG = false; + private static final boolean DEFAULT_KEEP_PROFILES_RUNNING_FLAG = true; private static final String ENABLE_WORK_PROFILE_TELEPHONY_FLAG = "enable_work_profile_telephony"; @@ -6041,7 +6051,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public void lockNow(int flags, String callerPackageName, boolean parent) { CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { caller = getCallerIdentity(callerPackageName); } else { caller = getCallerIdentity(); @@ -6053,7 +6063,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { ActiveAdmin admin; // Make sure the caller has any active admin with the right policy or // the required permission. - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { admin = enforcePermissionAndGetEnforcingAdmin( /* admin= */ null, /* permission= */ MANAGE_DEVICE_POLICY_LOCK, @@ -8907,13 +8917,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); } - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { // The effect of this policy is device-wide. enforcePermission(SET_TIME, caller.getPackageName(), UserHandle.USER_ALL); } else { @@ -8941,13 +8951,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return false; } CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); } - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { enforceCanQuery(SET_TIME, caller.getPackageName(), UserHandle.USER_ALL); } else { Objects.requireNonNull(who, "ComponentName is null"); @@ -8976,7 +8986,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { caller = getCallerIdentity(who); } - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { // The effect of this policy is device-wide. EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( who, @@ -9016,13 +9026,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); } - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { // The effect of this policy is device-wide. enforceCanQuery(SET_TIME_ZONE, caller.getPackageName(), UserHandle.USER_ALL); } else { @@ -9325,7 +9335,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); @@ -9335,7 +9345,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final int userHandle = caller.getUserId(); int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle; synchronized (getLockObject()) { - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { // SUPPORT USES_POLICY_DISABLE_KEYGUARD_FEATURES EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin( who, MANAGE_DEVICE_POLICY_KEYGUARD, caller.getPackageName(), @@ -9414,7 +9424,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { synchronized (getLockObject()) { if (who != null) { - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { EnforcingAdmin admin = getEnforcingAdminForCaller( who, who.getPackageName()); Integer features = mDevicePolicyEngine.getLocalPolicySetByAdmin( @@ -9428,7 +9438,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { Integer features = mDevicePolicyEngine.getResolvedPolicy( PolicyDefinition.KEYGUARD_DISABLED_FEATURES, affectedUserId); @@ -11625,7 +11635,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final CallerIdentity caller = getCallerIdentity(who, callerPackage); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_APPLICATION_RESTRICTIONS); - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( who, MANAGE_DEVICE_POLICY_APP_RESTRICTIONS, @@ -11730,7 +11740,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } Objects.requireNonNull(agent, "agent is null"); - int userHandle = UserHandle.getCallingUserId(); + + enforceMaxPackageNameLength(agent.getPackageName()); + final String agentAsString = agent.flattenToString(); + enforceMaxStringLength(agentAsString, "agent name"); + if (args != null) { + enforceMaxStringLength(args, "args"); + } + + int userHandle = mInjector.userHandleGetCallingUserId(); synchronized (getLockObject()) { ActiveAdmin ap; if (isPermissionCheckFlagEnabled()) { @@ -11747,7 +11765,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { checkCanExecuteOrThrowUnsafe( DevicePolicyManager.OPERATION_SET_TRUST_AGENT_CONFIGURATION); - ap.trustAgentInfos.put(agent.flattenToString(), new TrustAgentInfo(args)); + ap.trustAgentInfos.put(agentAsString, new TrustAgentInfo(args)); saveSettingsLocked(userHandle); } } @@ -12017,6 +12035,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { isDeviceOwner(caller) || isProfileOwner(caller)); if (packageList != null) { + for (String pkg : packageList) { + enforceMaxPackageNameLength(pkg); + } + int userId = caller.getUserId(); final List<AccessibilityServiceInfo> enabledServices; long id = mInjector.binderClearCallingIdentity(); @@ -12174,7 +12196,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isPolicyEngineForFinanceFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); @@ -12184,7 +12206,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int userId = getProfileParentUserIfRequested( caller.getUserId(), calledOnParentInstance); if (calledOnParentInstance) { - if (!isPermissionCheckFlagEnabled()) { + if (!isPolicyEngineForFinanceFlagEnabled()) { Preconditions.checkCallAuthorization( isProfileOwnerOfOrganizationOwnedDevice(caller)); } @@ -12192,12 +12214,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { "Permitted input methods must allow all input methods or only " + "system input methods when called on the parent instance of an " + "organization-owned device"); - } else if (!isPermissionCheckFlagEnabled()) { + } else if (!isPolicyEngineForFinanceFlagEnabled()) { Preconditions.checkCallAuthorization( isDefaultDeviceOwner(caller) || isProfileOwner(caller)); } if (packageList != null) { + for (String pkg : packageList) { + enforceMaxPackageNameLength(pkg); + } + List<InputMethodInfo> enabledImes = mInjector.binderWithCleanCallingIdentity(() -> InputMethodManagerInternal.get().getEnabledInputMethodListAsUser(userId)); if (enabledImes != null) { @@ -12216,7 +12242,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { synchronized (getLockObject()) { if (isPolicyEngineForFinanceFlagEnabled()) { - EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackageName); + EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin( + who, MANAGE_DEVICE_POLICY_INPUT_METHODS, + caller.getPackageName(), userId); mDevicePolicyEngine.setLocalPolicy( PolicyDefinition.PERMITTED_INPUT_METHODS, admin, @@ -13034,7 +13062,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { String packageName) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforceCanQueryAndGetEnforcingAdmin( who, MANAGE_DEVICE_POLICY_APP_RESTRICTIONS, @@ -13104,7 +13132,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final CallerIdentity caller = getCallerIdentity(who, callerPackage); ActiveAdmin admin; - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( who, MANAGE_DEVICE_POLICY_PACKAGE_STATE, @@ -13201,7 +13229,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public boolean isPackageSuspended(ComponentName who, String callerPackage, String packageName) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { enforcePermission( MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), @@ -13411,6 +13439,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public void setUserRestrictionGlobally(String callerPackage, String key) { final CallerIdentity caller = getCallerIdentity(callerPackage); + EnforcingAdmin admin = enforcePermissionForUserRestriction( + /* who= */ null, + key, + caller.getPackageName(), + UserHandle.USER_ALL + ); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION); if (!isPolicyEngineForFinanceFlagEnabled()) { @@ -13427,13 +13462,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { throw new IllegalArgumentException("Invalid restriction key: " + key); } - EnforcingAdmin admin = enforcePermissionForUserRestriction( - /* who= */ null, - key, - caller.getPackageName(), - UserHandle.USER_ALL - ); - setGlobalUserRestrictionInternal(admin, key, /* enabled= */ true); logUserRestrictionCall(key, /* enabled= */ true, /* parent= */ false, caller); @@ -13807,7 +13835,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean hidden, boolean parent) { CallerIdentity caller = getCallerIdentity(who, callerPackage); final int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId(); - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { // TODO: We need to ensure the delegate with DELEGATION_PACKAGE_ACCESS can do this enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId); } else { @@ -13826,7 +13854,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean result; synchronized (getLockObject()) { if (parent) { - if (!isPolicyEngineForFinanceFlagEnabled()) { + if (!isPermissionCheckFlagEnabled()) { Preconditions.checkCallAuthorization( isProfileOwnerOfOrganizationOwnedDevice( caller.getUserId()) && isManagedProfile(caller.getUserId())); @@ -13843,7 +13871,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Slogf.v(LOG_TAG, "calling pm.setApplicationHiddenSettingAsUser(%s, %b, %d)", packageName, hidden, userId); } - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage); mDevicePolicyEngine.setLocalPolicy( PolicyDefinition.APPLICATION_HIDDEN(packageName), @@ -13882,7 +13910,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { String packageName, boolean parent) { CallerIdentity caller = getCallerIdentity(who, callerPackage); int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId(); - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { // TODO: Also support DELEGATION_PACKAGE_ACCESS enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId); } else { @@ -13894,7 +13922,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { synchronized (getLockObject()) { if (parent) { - if (!isPolicyEngineForFinanceFlagEnabled()) { + if (!isPermissionCheckFlagEnabled()) { Preconditions.checkCallAuthorization( isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId()) && isManagedProfile(caller.getUserId())); @@ -14082,14 +14110,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return; } + + enforceMaxStringLength(accountType, "account type"); + CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); } synchronized (getLockObject()) { - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { int affectedUser = getAffectedUser(parent); EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( who, @@ -14152,7 +14183,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { CallerIdentity caller; Preconditions.checkArgumentNonnegative(userId, "Invalid userId"); final ArraySet<String> resultSet = new ArraySet<>(); - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { int affectedUser = parent ? getProfileParentId(userId) : userId; caller = getCallerIdentity(callerPackageName); if (!hasPermission(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, @@ -14772,6 +14803,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public void setLockTaskPackages(ComponentName who, String callerPackageName, String[] packages) throws SecurityException { Objects.requireNonNull(packages, "packages is null"); + for (String pkg : packages) { + enforceMaxPackageNameLength(pkg); + } + CallerIdentity caller; if (isPolicyEngineForFinanceFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); @@ -15519,12 +15554,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public boolean setStatusBarDisabled(ComponentName who, String callerPackageName, boolean disabled) { CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); } else { caller = getCallerIdentity(who); } - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { enforcePermission(MANAGE_DEVICE_POLICY_STATUS_BAR, caller.getPackageName(), UserHandle.USER_ALL); } else { @@ -15535,7 +15570,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int userId = caller.getUserId(); synchronized (getLockObject()) { - if (!isPolicyEngineForFinanceFlagEnabled()) { + if (!isPermissionCheckFlagEnabled()) { Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId), "Admin " + who + " is neither the device owner or affiliated " + "user's profile owner."); @@ -15594,7 +15629,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public boolean isStatusBarDisabled(String callerPackage) { final CallerIdentity caller = getCallerIdentity(callerPackage); - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { enforceCanQuery( MANAGE_DEVICE_POLICY_STATUS_BAR, caller.getPackageName(), caller.getUserId()); } else { @@ -15604,7 +15639,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int userId = caller.getUserId(); synchronized (getLockObject()) { - if (!isPolicyEngineForFinanceFlagEnabled()) { + if (!isPermissionCheckFlagEnabled()) { Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId), "Admin " + callerPackage + " is neither the device owner or affiliated user's profile owner."); @@ -16339,7 +16374,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // TODO(b/128928355): if this restriction is enforced by multiple DPCs, return // the admin for the calling user. Slogf.w(LOG_TAG, "getEnforcingAdminAndUserDetailsInternal(%d, %s): multiple " - + "sources for restriction %s on user %d", restriction, userId); + + "sources for restriction %s on user %d", + userId, restriction, restriction, userId); result = new Bundle(); result.putInt(Intent.EXTRA_USER_ID, userId); return result; @@ -16764,7 +16800,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } EnforcingAdmin enforcingAdmin; - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( admin, MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, @@ -16935,7 +16971,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public int getPermissionGrantState(ComponentName admin, String callerPackage, String packageName, String permission) throws RemoteException { final CallerIdentity caller = getCallerIdentity(admin, callerPackage); - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { enforceCanQuery(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, caller.getPackageName(), caller.getUserId()); } else { @@ -17374,6 +17410,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { CallerIdentity caller; ActiveAdmin admin; + message = truncateIfLonger(message, MAX_SHORT_SUPPORT_MESSAGE_LENGTH); + if (isPermissionCheckFlagEnabled()) { caller = getCallerIdentity(who, callerPackageName); EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( @@ -17434,6 +17472,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return; } + + message = truncateIfLonger(message, MAX_LONG_SUPPORT_MESSAGE_LENGTH); + Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); synchronized (getLockObject()) { @@ -17598,6 +17639,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); } + text = truncateIfLonger(text, MAX_ORG_NAME_LENGTH); + synchronized (getLockObject()) { if (!isPermissionCheckFlagEnabled()) { admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()); @@ -17878,9 +17921,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { throw new IllegalArgumentException("ids must not be null"); } for (String id : ids) { - if (TextUtils.isEmpty(id)) { - throw new IllegalArgumentException("ids must not contain empty string"); - } + Preconditions.checkArgument(!TextUtils.isEmpty(id), "ids must not have empty string"); + enforceMaxStringLength(id, "affiliation id"); } final Set<String> affiliationIds = new ArraySet<>(ids); @@ -19067,14 +19109,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { throw new IllegalArgumentException("token must be at least 32-byte long"); } CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { caller = getCallerIdentity(admin, callerPackageName); } else { caller = getCallerIdentity(admin); } final int userId = caller.getUserId(); - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( admin, MANAGE_DEVICE_POLICY_RESET_PASSWORD, @@ -19130,7 +19172,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return false; } CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { caller = getCallerIdentity(admin, callerPackageName); } else { caller = getCallerIdentity(admin); @@ -19138,7 +19180,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final int userId = caller.getUserId(); boolean result = false; - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( admin, MANAGE_DEVICE_POLICY_RESET_PASSWORD, @@ -19177,14 +19219,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return false; } CallerIdentity caller; - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { caller = getCallerIdentity(admin, callerPackageName); } else { caller = getCallerIdentity(admin); } int userId = caller.getUserId(); - if (isPolicyEngineForFinanceFlagEnabled()) { + if (isPermissionCheckFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( admin, MANAGE_DEVICE_POLICY_RESET_PASSWORD, @@ -19393,6 +19435,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { "Provided administrator and target are the same object."); Preconditions.checkArgument(!admin.getPackageName().equals(target.getPackageName()), "Provided administrator and target have the same package name."); + if (bundle != null) { + enforceMaxStringLength(bundle, "bundle"); + } final CallerIdentity caller = getCallerIdentity(admin); Preconditions.checkCallAuthorization( @@ -22798,6 +22843,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_DISPLAY, MANAGE_DEVICE_POLICY_FACTORY_RESET, MANAGE_DEVICE_POLICY_FUN, + MANAGE_DEVICE_POLICY_INPUT_METHODS, MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES, MANAGE_DEVICE_POLICY_KEYGUARD, MANAGE_DEVICE_POLICY_LOCALE, @@ -22873,9 +22919,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_BLUETOOTH, MANAGE_DEVICE_POLICY_CALLS, MANAGE_DEVICE_POLICY_CAMERA, + MANAGE_DEVICE_POLICY_CERTIFICATES, MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES, MANAGE_DEVICE_POLICY_DISPLAY, MANAGE_DEVICE_POLICY_FACTORY_RESET, + MANAGE_DEVICE_POLICY_INPUT_METHODS, MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES, MANAGE_DEVICE_POLICY_KEYGUARD, MANAGE_DEVICE_POLICY_LOCALE, @@ -22908,7 +22956,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_ACROSS_USERS, MANAGE_DEVICE_POLICY_AIRPLANE_MODE, MANAGE_DEVICE_POLICY_APPS_CONTROL, - MANAGE_DEVICE_POLICY_CERTIFICATES, MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE, MANAGE_DEVICE_POLICY_DEFAULT_SMS, MANAGE_DEVICE_POLICY_LOCALE, @@ -23033,11 +23080,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { //Map of Permission to Delegate Scope. private static final HashMap<String, String> DELEGATE_SCOPES = new HashMap<>(); { - DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, DELEGATION_PERMISSION_GRANT); DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_APP_RESTRICTIONS, DELEGATION_APP_RESTRICTIONS); DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL, DELEGATION_BLOCK_UNINSTALL); - DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, DELEGATION_SECURITY_LOGGING); + DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_CERTIFICATES, DELEGATION_CERT_INSTALL); DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_PACKAGE_STATE, DELEGATION_PACKAGE_ACCESS); + DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, DELEGATION_PERMISSION_GRANT); + DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, DELEGATION_SECURITY_LOGGING); } private static final HashMap<String, String> CROSS_USER_PERMISSIONS = @@ -24137,6 +24185,53 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { }); } + /** + * Truncates char sequence to maximum length, nulls are ignored. + */ + private static CharSequence truncateIfLonger(CharSequence input, int maxLength) { + return input == null || input.length() <= maxLength + ? input + : input.subSequence(0, maxLength); + } + + /** + * Throw if string argument is too long to be serialized. + */ + private static void enforceMaxStringLength(String str, String argName) { + Preconditions.checkArgument( + str.length() <= MAX_POLICY_STRING_LENGTH, argName + " loo long"); + } + + private static void enforceMaxPackageNameLength(String pkg) { + Preconditions.checkArgument( + pkg.length() <= MAX_PACKAGE_NAME_LENGTH, "Package name too long"); + } + + /** + * Throw if persistable bundle contains any string that we can't serialize. + */ + private static void enforceMaxStringLength(PersistableBundle bundle, String argName) { + // Persistable bundles can have other persistable bundles as values, traverse with a queue. + Queue<PersistableBundle> queue = new ArrayDeque<>(); + queue.add(bundle); + while (!queue.isEmpty()) { + PersistableBundle current = queue.remove(); + for (String key : current.keySet()) { + enforceMaxStringLength(key, "key in " + argName); + Object value = current.get(key); + if (value instanceof String) { + enforceMaxStringLength((String) value, "string value in " + argName); + } else if (value instanceof String[]) { + for (String str : (String[]) value) { + enforceMaxStringLength(str, "string value in " + argName); + } + } else if (value instanceof PersistableBundle) { + queue.add((PersistableBundle) value); + } + } + } + } + private ActiveAdmin getActiveAdminForCaller(@Nullable ComponentName who, CallerIdentity caller) { synchronized (getLockObject()) { diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java index 3a47b476a131..2b57c5927678 100644 --- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java @@ -154,6 +154,7 @@ public class DeviceIdleControllerTest { // Freeze time for testing. long nowElapsed; boolean useMotionSensor = true; + boolean isLocationPrefetchEnabled = true; InjectorForTest(Context ctx) { super(ctx); @@ -223,6 +224,11 @@ public class DeviceIdleControllerTest { } @Override + boolean isLocationPrefetchEnabled() { + return isLocationPrefetchEnabled; + } + + @Override PowerManager getPowerManager() { return mPowerManager; } @@ -991,6 +997,43 @@ public class DeviceIdleControllerTest { } @Test + public void testStepIdleStateLocked_ValidStates_LocationPrefetchDisabled() { + mInjector.locationManager = mLocationManager; + mInjector.isLocationPrefetchEnabled = false; + cleanupDeviceIdleController(); + setupDeviceIdleController(); + doReturn(mock(LocationProvider.class)).when(mLocationManager).getProvider(anyString()); + // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon. + setAlarmSoon(false); + // Set state to INACTIVE. + mDeviceIdleController.becomeActiveLocked("testing", 0); + setChargingOn(false); + setScreenOn(false); + verifyStateConditions(STATE_INACTIVE); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE_PENDING); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_SENSING); + + mDeviceIdleController.stepIdleStateLocked("testing"); + // Prefetch location is off, so SENSING should go straight through to IDLE. + verifyStateConditions(STATE_IDLE); + + // Should just alternate between IDLE and IDLE_MAINTENANCE now. + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE_MAINTENANCE); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE_MAINTENANCE); + } + + @Test public void testStepIdleStateLocked_ValidStates_WithLocationManager_NoProviders() { // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon. setAlarmSoon(false); @@ -1024,6 +1067,46 @@ public class DeviceIdleControllerTest { } @Test + public void testStepIdleStateLocked_ValidStates_WithLocationManager_MissingProviders() { + mInjector.locationManager = mLocationManager; + doReturn(null).when(mLocationManager) + .getProvider(eq(LocationManager.FUSED_PROVIDER)); + doReturn(null).when(mLocationManager) + .getProvider(eq(LocationManager.GPS_PROVIDER)); + doReturn(mock(LocationProvider.class)).when(mLocationManager) + .getProvider(eq(LocationManager.NETWORK_PROVIDER)); + // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon. + setAlarmSoon(false); + // Set state to INACTIVE. + mDeviceIdleController.becomeActiveLocked("testing", 0); + setChargingOn(false); + setScreenOn(false); + verifyStateConditions(STATE_INACTIVE); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE_PENDING); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_SENSING); + + mDeviceIdleController.stepIdleStateLocked("testing"); + // Location manager exists, but the required providers don't exist, + // so SENSING should go straight through to IDLE. + verifyStateConditions(STATE_IDLE); + + // Should just alternate between IDLE and IDLE_MAINTENANCE now. + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE_MAINTENANCE); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE); + + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE_MAINTENANCE); + } + + @Test public void testStepIdleStateLocked_ValidStates_WithLocationManager_WithProviders() { mInjector.locationManager = mLocationManager; doReturn(mock(LocationProvider.class)).when(mLocationManager).getProvider(anyString()); diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java index c4aa0bbc24b5..8dc0ac6515cd 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java @@ -64,6 +64,7 @@ import com.android.server.ExtendedMockitoRule; import com.android.server.LocalServices; import com.android.server.am.BatteryStatsService; import com.android.server.display.RampAnimator.DualRampAnimator; +import com.android.server.display.brightness.BrightnessEvent; import com.android.server.display.color.ColorDisplayService; import com.android.server.display.layout.Layout; import com.android.server.display.whitebalance.DisplayWhiteBalanceController; @@ -596,17 +597,17 @@ public final class DisplayPowerController2Test { // We should still set screen state for the default display DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); + advanceTime(1); // Run updatePowerState verify(mHolder.displayPowerState, times(2)).setScreenState(anyInt()); mHolder = createDisplayPowerController(42, UNIQUE_ID); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); + advanceTime(1); // Run updatePowerState verify(mHolder.displayPowerState, never()).setScreenState(anyInt()); mHolder.dpc.onBootCompleted(); - advanceTime(1); + advanceTime(1); // Run updatePowerState verify(mHolder.displayPowerState).setScreenState(anyInt()); } @@ -632,8 +633,8 @@ public final class DisplayPowerController2Test { .thenReturn(brightness); dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness()) - .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); + when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( + any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); advanceTime(1); // Run updatePowerState @@ -667,8 +668,8 @@ public final class DisplayPowerController2Test { .thenReturn(brightness); dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness()) - .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); + when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness( + any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); advanceTime(1); // Run updatePowerState @@ -822,6 +823,21 @@ public final class DisplayPowerController2Test { ); } + @Test + public void testUpdateBrightnessThrottlingDataId() { + mHolder.display.getDisplayInfoLocked().thermalBrightnessThrottlingDataId = + "throttling-data-id"; + clearInvocations(mHolder.display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig()); + + mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY); + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig()) + .getThermalBrightnessThrottlingDataMapByThrottlingId(); + } + /** * Creates a mock and registers it to {@link LocalServices}. */ @@ -862,8 +878,6 @@ public final class DisplayPowerController2Test { when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info); when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled); when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false); - when(logicalDisplayMock.getThermalBrightnessThrottlingDataIdLocked()).thenReturn( - DisplayDeviceConfig.DEFAULT_ID); when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo); when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId); when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock); diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java index 415adbbac91e..5c0810fdca44 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -829,6 +829,21 @@ public final class DisplayPowerControllerTest { ); } + @Test + public void testUpdateBrightnessThrottlingDataId() { + mHolder.display.getDisplayInfoLocked().thermalBrightnessThrottlingDataId = + "throttling-data-id"; + clearInvocations(mHolder.display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig()); + + mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY); + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig()) + .getThermalBrightnessThrottlingDataMapByThrottlingId(); + } + /** * Creates a mock and registers it to {@link LocalServices}. */ @@ -869,8 +884,6 @@ public final class DisplayPowerControllerTest { when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info); when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled); when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false); - when(logicalDisplayMock.getThermalBrightnessThrottlingDataIdLocked()).thenReturn( - DisplayDeviceConfig.DEFAULT_ID); when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo); when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId); when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock); diff --git a/services/tests/mockingservicestests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java deleted file mode 100644 index 17fba9f43707..000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java +++ /dev/null @@ -1,121 +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.server.display; - -import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.when; - -import android.hardware.display.DisplayManager; -import android.provider.Settings; -import android.testing.TestableContext; -import android.view.Display; - -import androidx.test.filters.SmallTest; -import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.display.RefreshRateSettingsUtils; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class RefreshRateSettingsUtilsTest { - - @Rule - public final TestableContext mContext = new TestableContext( - InstrumentationRegistry.getInstrumentation().getContext()); - - @Mock - private DisplayManager mDisplayManagerMock; - @Mock - private Display mDisplayMock; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - mContext.addMockSystemService(DisplayManager.class, mDisplayManagerMock); - - Display.Mode[] modes = new Display.Mode[] { - new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600, - /* refreshRate= */ 60), - new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600, - /* refreshRate= */ 120), - new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600, - /* refreshRate= */ 90) - }; - - when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock); - when(mDisplayMock.getSupportedModes()).thenReturn(modes); - } - - @Test - public void testFindHighestRefreshRateForDefaultDisplay() { - when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null); - assertEquals(DEFAULT_REFRESH_RATE, - RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext), - /* delta= */ 0); - - when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock); - assertEquals(120, - RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext), - /* delta= */ 0); - } - - @Test - public void testGetMinRefreshRate() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.FORCE_PEAK_REFRESH_RATE, -1); - assertEquals(0, RefreshRateSettingsUtils.getMinRefreshRate(mContext), /* delta= */ 0); - - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.FORCE_PEAK_REFRESH_RATE, 0); - assertEquals(0, RefreshRateSettingsUtils.getMinRefreshRate(mContext), /* delta= */ 0); - - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.FORCE_PEAK_REFRESH_RATE, 1); - assertEquals(120, RefreshRateSettingsUtils.getMinRefreshRate(mContext), /* delta= */ 0); - } - - @Test - public void testGetPeakRefreshRate() { - float defaultPeakRefreshRate = 100; - - Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, -1); - assertEquals(defaultPeakRefreshRate, - RefreshRateSettingsUtils.getPeakRefreshRate(mContext, defaultPeakRefreshRate), - /* delta= */ 0); - - Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, 0); - assertEquals(DEFAULT_REFRESH_RATE, - RefreshRateSettingsUtils.getPeakRefreshRate(mContext, defaultPeakRefreshRate), - /* delta= */ 0); - - Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, 1); - assertEquals(120, - RefreshRateSettingsUtils.getPeakRefreshRate(mContext, defaultPeakRefreshRate), - /* delta= */ 0); - } -} diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java new file mode 100644 index 000000000000..eec026ccfc8a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.companion.datatransfer.contextsync; + +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.when; + +import android.platform.test.annotations.Presubmit; +import android.testing.AndroidTestingRunner; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.server.companion.transport.CompanionTransportManager; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Presubmit +@RunWith(AndroidTestingRunner.class) +public class CrossDeviceSyncControllerTest { + + private CrossDeviceSyncController mCrossDeviceSyncController; + @Mock + private CompanionTransportManager mMockCompanionTransportManager; + @Mock + private CrossDeviceCall mMockCrossDeviceCall; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mCrossDeviceSyncController = new CrossDeviceSyncController( + InstrumentationRegistry.getInstrumentation().getContext(), + mMockCompanionTransportManager); + } + + @Test + public void processTelecomDataFromSync_createCallUpdateMessage_emptyCallsAndRequests() { + final byte[] data = mCrossDeviceSyncController.createCallUpdateMessage(new HashSet<>(), + InstrumentationRegistry.getInstrumentation().getContext().getUserId()); + final CallMetadataSyncData callMetadataSyncData = + mCrossDeviceSyncController.processTelecomDataFromSync(data); + assertWithMessage("Unexpectedly found a call").that( + callMetadataSyncData.getCalls()).isEmpty(); + assertWithMessage("Unexpectedly found a request").that( + callMetadataSyncData.getRequests()).isEmpty(); + } + + @Test + public void processTelecomDataFromSync_createEmptyMessage_emptyCallsAndRequests() { + final byte[] data = CrossDeviceSyncController.createEmptyMessage(); + final CallMetadataSyncData callMetadataSyncData = + mCrossDeviceSyncController.processTelecomDataFromSync(data); + assertWithMessage("Unexpectedly found a call").that( + callMetadataSyncData.getCalls()).isEmpty(); + assertWithMessage("Unexpectedly found a request").that( + callMetadataSyncData.getRequests()).isEmpty(); + } + + @Test + public void processTelecomDataFromSync_createCallUpdateMessage_hasCalls() { + when(mMockCrossDeviceCall.getId()).thenReturn(5L); + final String callerId = "Firstname Lastname"; + when(mMockCrossDeviceCall.getReadableCallerId(anyBoolean())).thenReturn(callerId); + final String appName = "AppName"; + when(mMockCrossDeviceCall.getCallingAppName()).thenReturn(appName); + final String appIcon = "ABCD"; + when(mMockCrossDeviceCall.getCallingAppIcon()).thenReturn(appIcon.getBytes()); + when(mMockCrossDeviceCall.getStatus()).thenReturn(android.companion.Telecom.Call.RINGING); + final Set<Integer> controls = Set.of( + android.companion.Telecom.Call.ACCEPT, + android.companion.Telecom.Call.REJECT, + android.companion.Telecom.Call.SILENCE); + when(mMockCrossDeviceCall.getControls()).thenReturn(controls); + final byte[] data = mCrossDeviceSyncController.createCallUpdateMessage( + new HashSet<>(List.of(mMockCrossDeviceCall)), + InstrumentationRegistry.getInstrumentation().getContext().getUserId()); + final CallMetadataSyncData callMetadataSyncData = + mCrossDeviceSyncController.processTelecomDataFromSync(data); + assertWithMessage("Wrong number of active calls").that( + callMetadataSyncData.getCalls()).hasSize(1); + final CallMetadataSyncData.Call call = + callMetadataSyncData.getCalls().stream().findAny().orElseThrow(); + assertWithMessage("Wrong id").that(call.getId()).isEqualTo(5L); + assertWithMessage("Wrong app icon").that(new String(call.getAppIcon())).isEqualTo(appIcon); + assertWithMessage("Wrong app name").that(call.getAppName()).isEqualTo(appName); + assertWithMessage("Wrong caller id").that(call.getCallerId()).isEqualTo(callerId); + assertWithMessage("Wrong status").that(call.getStatus()) + .isEqualTo(android.companion.Telecom.Call.RINGING); + assertWithMessage("Wrong controls").that(call.getControls()).isEqualTo(controls); + assertWithMessage("Unexpectedly has requests").that( + callMetadataSyncData.getRequests()).isEmpty(); + } + + @Test + public void processTelecomDataFromMessage_createCallControlMessage_hasCallControlRequest() { + final byte[] data = CrossDeviceSyncController.createCallControlMessage( + /* callId= */ 5L, /* status= */ android.companion.Telecom.Call.ACCEPT); + final CallMetadataSyncData callMetadataSyncData = + mCrossDeviceSyncController.processTelecomDataFromSync(data); + assertWithMessage("Wrong number of requests").that( + callMetadataSyncData.getRequests()).hasSize(1); + final CallMetadataSyncData.Call call = + callMetadataSyncData.getRequests().stream().findAny().orElseThrow(); + assertWithMessage("Wrong id").that(call.getId()).isEqualTo(5L); + assertWithMessage("Wrong app icon").that(call.getAppIcon()).isNull(); + assertWithMessage("Wrong app name").that(call.getAppName()).isNull(); + assertWithMessage("Wrong caller id").that(call.getCallerId()).isNull(); + assertWithMessage("Wrong status").that(call.getStatus()) + .isEqualTo(android.companion.Telecom.Call.UNKNOWN_STATUS); + assertWithMessage("Wrong controls").that(call.getControls()) + .isEqualTo(Set.of(android.companion.Telecom.Call.ACCEPT)); + assertWithMessage("Unexpectedly has active calls").that( + callMetadataSyncData.getCalls()).isEmpty(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 34b88b03547b..1e342f580745 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -130,6 +130,7 @@ import android.os.Build; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.IpcDataCache; +import android.os.PersistableBundle; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; @@ -1511,7 +1512,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { * Validates that when the device owner is removed, the reset password token is cleared */ @Test - @Ignore("b/277916462") public void testClearDeviceOwner_clearResetPasswordToken() throws Exception { mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); @@ -2602,7 +2602,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore("b/277916462") public void testSetApplicationHiddenWithDO() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -2628,7 +2627,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore("b/277916462") public void testSetApplicationHiddenWithPOOfOrganizationOwnedDevice() throws Exception { final int MANAGED_PROFILE_USER_ID = CALLER_USER_HANDLE; final int MANAGED_PROFILE_ADMIN_UID = @@ -4375,7 +4373,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore("b/277916462") public void testSetAutoTimeZoneEnabledModifiesSetting() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -4387,7 +4384,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore("b/277916462") public void testSetAutoTimeZoneEnabledWithPOOnUser0() throws Exception { mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; setupProfileOwnerOnUser0(); @@ -4399,7 +4395,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore("b/277916462") public void testSetAutoTimeZoneEnabledFailWithPONotOnUser0() throws Exception { setupProfileOwner(); assertExpectException(SecurityException.class, null, @@ -4409,7 +4404,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore("b/277916462") public void testSetAutoTimeZoneEnabledWithPOOfOrganizationOwnedDevice() throws Exception { setupProfileOwner(); configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE); @@ -5383,7 +5377,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore("b/277916462") public void testResetPasswordWithToken() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -5418,7 +5411,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore("b/277916462") public void resetPasswordWithToken_NumericPin() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -5439,7 +5431,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore("b/277916462") public void resetPasswordWithToken_EmptyPassword() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -7260,7 +7251,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore("b/277916462") public void testCanProfileOwnerResetPasswordWhenLocked() throws Exception { setDeviceEncryptionPerUser(); setupProfileOwner(); @@ -7324,7 +7314,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore("b/277916462") public void testSetAccountTypesWithManagementDisabledOnManagedProfile() throws Exception { setupProfileOwner(); @@ -7344,7 +7333,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore("b/277916462") public void testSetAccountTypesWithManagementDisabledOnOrgOwnedManagedProfile() throws Exception { mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS); @@ -8539,6 +8527,46 @@ public class DevicePolicyManagerTest extends DpmTestBase { eq(FUSED_PROVIDER), any(), eq(getServices().executor), any()); } + /** + * Verifies that bundles with tons of moderately long strings are persisted correctly. + * + * Policy is serialized into binary XML and there is a limit on the max string length: 65535. + * This test ensures that as long as each string in the trust agent configuration is below this + * limit, the policy can be serialized and deserialized correctly, even when the total length + * of the configuration is above that limit. This should be the case because PersistableBundle + * contents are stored as XML subtrees rather than as strings. + */ + @Test + public void testSetTrustAgentConfiguration_largeBundlePersisted() { + setAsProfileOwner(admin1); + + ComponentName agent = new ComponentName("some.trust.agent", "some.trust.agent.Agent"); + PersistableBundle configIn = new PersistableBundle(); + String kilobyteString = new String(new char[1024]).replace('\0', 'A'); + for (int i = 0; i < 1024; i++) { + configIn.putString("key-" + i, kilobyteString); + } + + runAsCaller(mAdmin1Context, dpms, dpm -> { + dpm.setTrustAgentConfiguration(admin1, agent, configIn); + }); + + // Re-read policies to see if they were serialized/deserialized correctly. + initializeDpms(); + + List<PersistableBundle> configsOut = new ArrayList<>(); + runAsCaller(mAdmin1Context, dpms, dpm -> { + configsOut.addAll(dpm.getTrustAgentConfiguration(admin1, agent)); + }); + + assertThat(configsOut.size()).isEqualTo(1); + PersistableBundle configOut = configsOut.get(0); + assertThat(configOut.size()).isEqualTo(1024); + for (int i = 0; i < 1024; i++) { + assertThat(configOut.getString("key-" + i, null)).isEqualTo(kilobyteString); + } + } + private void setupVpnAuthorization(String userVpnPackage, int userVpnUid) { final AppOpsManager.PackageOps vpnOp = new AppOpsManager.PackageOps(userVpnPackage, userVpnUid, List.of(new AppOpsManager.OpEntry( diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java index 7536c7949bcb..1eec70da3d20 100644 --- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java +++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java @@ -649,9 +649,9 @@ public class LogicalDisplayMapperTest { assertEquals(0, mLogicalDisplayMapper.getDisplayLocked(device2) .getLeadDisplayIdLocked()); assertEquals("concurrent", mLogicalDisplayMapper.getDisplayLocked(device1) - .getThermalBrightnessThrottlingDataIdLocked()); + .getDisplayInfoLocked().thermalBrightnessThrottlingDataId); assertEquals("concurrent", mLogicalDisplayMapper.getDisplayLocked(device2) - .getThermalBrightnessThrottlingDataIdLocked()); + .getDisplayInfoLocked().thermalBrightnessThrottlingDataId); mLogicalDisplayMapper.setDeviceStateLocked(1, false); advanceTime(1000); @@ -661,10 +661,10 @@ public class LogicalDisplayMapperTest { assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isInTransitionLocked()); assertEquals(DisplayDeviceConfig.DEFAULT_ID, mLogicalDisplayMapper.getDisplayLocked(device1) - .getThermalBrightnessThrottlingDataIdLocked()); + .getDisplayInfoLocked().thermalBrightnessThrottlingDataId); assertEquals(DisplayDeviceConfig.DEFAULT_ID, mLogicalDisplayMapper.getDisplayLocked(device2) - .getThermalBrightnessThrottlingDataIdLocked()); + .getDisplayInfoLocked().thermalBrightnessThrottlingDataId); mLogicalDisplayMapper.setDeviceStateLocked(2, false); advanceTime(1000); @@ -674,10 +674,10 @@ public class LogicalDisplayMapperTest { assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isInTransitionLocked()); assertEquals(DisplayDeviceConfig.DEFAULT_ID, mLogicalDisplayMapper.getDisplayLocked(device1) - .getThermalBrightnessThrottlingDataIdLocked()); + .getDisplayInfoLocked().thermalBrightnessThrottlingDataId); assertEquals(DisplayDeviceConfig.DEFAULT_ID, mLogicalDisplayMapper.getDisplayLocked(device2) - .getThermalBrightnessThrottlingDataIdLocked()); + .getDisplayInfoLocked().thermalBrightnessThrottlingDataId); } @Test diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java index 5ea30298890f..f6cf57161f59 100644 --- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java +++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java @@ -205,4 +205,19 @@ public class LogicalDisplayTest { assertNotEquals(info3, info2); assertTrue(refreshRanges.contentEquals(info3.thermalRefreshRateThrottling)); } + + @Test + public void testSetThermalBrightnessThrottlingDataId() { + String brightnessThrottlingDataId = "throttling_data_id"; + DisplayInfo info1 = mLogicalDisplay.getDisplayInfoLocked(); + mLogicalDisplay.setThermalBrightnessThrottlingDataIdLocked(brightnessThrottlingDataId); + DisplayInfo info2 = mLogicalDisplay.getDisplayInfoLocked(); + // Display info should only be updated when updateLocked is called + assertEquals(info2, info1); + + mLogicalDisplay.updateLocked(mDeviceRepo); + DisplayInfo info3 = mLogicalDisplay.getDisplayInfoLocked(); + assertNotEquals(info3, info2); + assertEquals(brightnessThrottlingDataId, info3.thermalBrightnessThrottlingDataId); + } } diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java index eb208d2e6c7f..d9cf15b35c2b 100644 --- a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java +++ b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java @@ -18,6 +18,7 @@ package com.android.server.display.brightness.strategy; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -40,6 +41,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.server.display.AutomaticBrightnessController; +import com.android.server.display.brightness.BrightnessEvent; import com.android.server.display.brightness.BrightnessReason; import org.junit.After; @@ -262,12 +264,13 @@ public class AutomaticBrightnessStrategyTest { float automaticScreenBrightness = 0.3f; AutomaticBrightnessController automaticBrightnessController = mock( AutomaticBrightnessController.class); - when(automaticBrightnessController.getAutomaticScreenBrightness()).thenReturn( - automaticScreenBrightness); + when(automaticBrightnessController.getAutomaticScreenBrightness(any(BrightnessEvent.class))) + .thenReturn(automaticScreenBrightness); mAutomaticBrightnessStrategy.setAutomaticBrightnessController( automaticBrightnessController); assertEquals(automaticScreenBrightness, - mAutomaticBrightnessStrategy.getAutomaticScreenBrightness(), 0.0f); + mAutomaticBrightnessStrategy.getAutomaticScreenBrightness( + new BrightnessEvent(DISPLAY_ID)), 0.0f); } @Test diff --git a/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java index e49225216b6b..b2a3a57cccf8 100644 --- a/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java @@ -84,7 +84,6 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.internal.display.BrightnessSynchronizer; -import com.android.internal.display.RefreshRateSettingsUtils; import com.android.internal.os.BackgroundThread; import com.android.internal.util.Preconditions; import com.android.internal.util.test.FakeSettingsProvider; @@ -159,9 +158,6 @@ public class DisplayModeDirectorTest { LocalServices.addService(SensorManagerInternal.class, mSensorManagerInternalMock); LocalServices.removeServiceForTest(DisplayManagerInternal.class); LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock); - - clearSmoothDisplaySetting(); - clearForcePeakRefreshRateSetting(); } private DisplayModeDirector createDirectorFromRefreshRateArray( @@ -922,6 +918,7 @@ public class DisplayModeDirectorTest { public void testLockFpsForLowZone() throws Exception { DisplayModeDirector director = createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + setPeakRefreshRate(90); director.getSettingsObserver().setDefaultRefreshRate(90); director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); @@ -929,7 +926,6 @@ public class DisplayModeDirectorTest { config.setRefreshRateInLowZone(90); config.setLowDisplayBrightnessThresholds(new int[] { 10 }); config.setLowAmbientBrightnessThresholds(new int[] { 20 }); - config.setDefaultPeakRefreshRate(90); Sensor lightSensor = createLightSensor(); SensorManager sensorManager = createMockSensorManager(lightSensor); @@ -980,6 +976,7 @@ public class DisplayModeDirectorTest { public void testLockFpsForHighZone() throws Exception { DisplayModeDirector director = createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + setPeakRefreshRate(90 /*fps*/); director.getSettingsObserver().setDefaultRefreshRate(90); director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); @@ -987,7 +984,6 @@ public class DisplayModeDirectorTest { config.setRefreshRateInHighZone(60); config.setHighDisplayBrightnessThresholds(new int[] { 255 }); config.setHighAmbientBrightnessThresholds(new int[] { 8000 }); - config.setDefaultPeakRefreshRate(90); Sensor lightSensor = createLightSensor(); SensorManager sensorManager = createMockSensorManager(lightSensor); @@ -1035,123 +1031,16 @@ public class DisplayModeDirectorTest { } @Test - public void testSmoothDisplay() { - float defaultRefreshRate = 60; - int defaultPeakRefreshRate = 100; - DisplayModeDirector director = - createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); - director.getSettingsObserver().setDefaultRefreshRate(defaultRefreshRate); - director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); - - final FakeDeviceConfig config = mInjector.getDeviceConfig(); - config.setDefaultPeakRefreshRate(defaultPeakRefreshRate); - - Sensor lightSensor = createLightSensor(); - SensorManager sensorManager = createMockSensorManager(lightSensor); - director.start(sensorManager); - - // Default value of the setting - - Vote vote = director.getVote(Display.DEFAULT_DISPLAY, - Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ - defaultPeakRefreshRate); - vote = director.getVote(Display.DEFAULT_DISPLAY, - Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ - Float.POSITIVE_INFINITY); - vote = director.getVote(Display.DEFAULT_DISPLAY, - Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ - defaultRefreshRate); - - setSmoothDisplayEnabled(false); - - vote = director.getVote(Display.DEFAULT_DISPLAY, - Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ - RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE); - vote = director.getVote(Display.DEFAULT_DISPLAY, - Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ - Float.POSITIVE_INFINITY); - vote = director.getVote(Display.DEFAULT_DISPLAY, - Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ - defaultRefreshRate); - - setSmoothDisplayEnabled(true); - - vote = director.getVote(Display.DEFAULT_DISPLAY, - Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ - RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext)); - vote = director.getVote(Display.DEFAULT_DISPLAY, - Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ - Float.POSITIVE_INFINITY); - vote = director.getVote(Display.DEFAULT_DISPLAY, - Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ - defaultRefreshRate); - } - - @Test - public void testForcePeakRefreshRate() { - float defaultRefreshRate = 60; - DisplayModeDirector director = - createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); - director.getSettingsObserver().setDefaultRefreshRate(defaultRefreshRate); - director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); - - Sensor lightSensor = createLightSensor(); - SensorManager sensorManager = createMockSensorManager(lightSensor); - director.start(sensorManager); - - setForcePeakRefreshRateEnabled(false); - setSmoothDisplayEnabled(false); - - Vote vote = director.getVote(Display.DEFAULT_DISPLAY, - Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ - RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE); - vote = director.getVote(Display.DEFAULT_DISPLAY, - Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, - /* frameRateHigh= */ Float.POSITIVE_INFINITY); - vote = director.getVote(Display.DEFAULT_DISPLAY, - Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ - defaultRefreshRate); - - setForcePeakRefreshRateEnabled(true); - - vote = director.getVote(Display.DEFAULT_DISPLAY, - Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ - RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext)); - vote = director.getVote(Display.DEFAULT_DISPLAY, - Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ - RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext), - /* frameRateHigh= */ Float.POSITIVE_INFINITY); - vote = director.getVote(Display.DEFAULT_DISPLAY, - Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE); - assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ - defaultRefreshRate); - } - - @Test public void testSensorRegistration() { // First, configure brightness zones or DMD won't register for sensor data. final FakeDeviceConfig config = mInjector.getDeviceConfig(); config.setRefreshRateInHighZone(60); config.setHighDisplayBrightnessThresholds(new int[] { 255 }); config.setHighAmbientBrightnessThresholds(new int[] { 8000 }); - config.setDefaultPeakRefreshRate(90); DisplayModeDirector director = createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + setPeakRefreshRate(90 /*fps*/); director.getSettingsObserver().setDefaultRefreshRate(90); director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); @@ -2527,10 +2416,10 @@ public class DisplayModeDirectorTest { config.setRefreshRateInHighZone(60); config.setHighDisplayBrightnessThresholds(new int[] { 255 }); config.setHighAmbientBrightnessThresholds(new int[] { 8000 }); - config.setDefaultPeakRefreshRate(90); DisplayModeDirector director = createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + setPeakRefreshRate(90 /*fps*/); director.getSettingsObserver().setDefaultRefreshRate(90); director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); @@ -2828,30 +2717,10 @@ public class DisplayModeDirectorTest { listener.onDisplayChanged(DISPLAY_ID); } - private void setSmoothDisplayEnabled(boolean enabled) { - Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, - enabled ? 1 : 0); - mInjector.notifySmoothDisplaySettingChanged(); - waitForIdleSync(); - } - - private void clearSmoothDisplaySetting() { - Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, -1); - mInjector.notifySmoothDisplaySettingChanged(); - waitForIdleSync(); - } - - private void setForcePeakRefreshRateEnabled(boolean enabled) { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.FORCE_PEAK_REFRESH_RATE, enabled ? 1 : 0); - mInjector.notifyForcePeakRefreshRateSettingChanged(); - waitForIdleSync(); - } - - private void clearForcePeakRefreshRateSetting() { - Settings.System.putInt(mContext.getContentResolver(), - Settings.System.FORCE_PEAK_REFRESH_RATE, -1); - mInjector.notifySmoothDisplaySettingChanged(); + private void setPeakRefreshRate(float fps) { + Settings.System.putFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE, + fps); + mInjector.notifyPeakRefreshRateChanged(); waitForIdleSync(); } @@ -2900,8 +2769,7 @@ public class DisplayModeDirectorTest { private final Display mDisplay; private boolean mDisplayInfoValid = true; private ContentObserver mBrightnessObserver; - private ContentObserver mSmoothDisplaySettingObserver; - private ContentObserver mForcePeakRefreshRateSettingObserver; + private ContentObserver mPeakRefreshRateObserver; FakesInjector() { mDeviceConfig = new FakeDeviceConfig(); @@ -2918,15 +2786,9 @@ public class DisplayModeDirectorTest { } @Override - public void registerSmoothDisplayObserver(@NonNull ContentResolver cr, - @NonNull ContentObserver observer) { - mSmoothDisplaySettingObserver = observer; - } - - @Override - public void registerForcePeakRefreshRateObserver(@NonNull ContentResolver cr, + public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, @NonNull ContentObserver observer) { - mForcePeakRefreshRateSettingObserver = observer; + mPeakRefreshRateObserver = observer; } @Override @@ -2976,17 +2838,10 @@ public class DisplayModeDirectorTest { ApplicationProvider.getApplicationContext().getResources()); } - void notifySmoothDisplaySettingChanged() { - if (mSmoothDisplaySettingObserver != null) { - mSmoothDisplaySettingObserver.dispatchChange(false /*selfChange*/, - SMOOTH_DISPLAY_URI); - } - } - - void notifyForcePeakRefreshRateSettingChanged() { - if (mForcePeakRefreshRateSettingObserver != null) { - mForcePeakRefreshRateSettingObserver.dispatchChange(false /*selfChange*/, - FORCE_PEAK_REFRESH_RATE_URI); + void notifyPeakRefreshRateChanged() { + if (mPeakRefreshRateObserver != null) { + mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/, + PEAK_REFRESH_RATE_URI); } } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java index a446e109c921..c53a7a708cfd 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java @@ -18,6 +18,7 @@ package com.android.server.hdmi; import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_DESTINATION; import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER; +import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER_LONG; import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER_SHORT; import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_SOURCE; import static com.android.server.hdmi.HdmiCecMessageValidator.OK; @@ -145,11 +146,12 @@ public class HdmiCecMessageValidatorTest { @Test public void isValid_systemAudioModeStatus() { assertMessageValidity("40:7E:00").isEqualTo(OK); - assertMessageValidity("40:7E:01:01").isEqualTo(OK); + assertMessageValidity("40:7E:01").isEqualTo(OK); assertMessageValidity("0F:7E:00").isEqualTo(ERROR_DESTINATION); assertMessageValidity("F0:7E").isEqualTo(ERROR_SOURCE); assertMessageValidity("40:7E").isEqualTo(ERROR_PARAMETER_SHORT); + assertMessageValidity("40:7E:01:1F:28").isEqualTo(ERROR_PARAMETER_LONG); assertMessageValidity("40:7E:02").isEqualTo(ERROR_PARAMETER); } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 4dd5e94541e4..fd6eb9286651 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -761,6 +761,13 @@ public class HdmiControlServiceTest { assertThat(mHdmiControlServiceSpy.handleCecCommand(message)) .isEqualTo(Constants.ABORT_INVALID_OPERAND); + + // Validating ERROR_PARAMETER_LONG will generate ABORT_INVALID_OPERAND. + // Taken from HdmiCecMessageValidatorTest#isValid_systemAudioModeStatus + HdmiCecMessage systemAudioModeStatus = HdmiUtils.buildMessage("40:7E:01:1F:28"); + + assertThat(mHdmiControlServiceSpy.handleCecCommand(systemAudioModeStatus)) + .isEqualTo(Constants.ABORT_INVALID_OPERAND); } @Test diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java index d07831dd7929..fd65807a1976 100644 --- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java @@ -20,16 +20,25 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import android.platform.test.annotations.Presubmit; + import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.inputmethod.SoftInputShowHideReason; + import org.junit.Test; import org.junit.runner.RunWith; +import java.io.PrintWriter; +import java.io.StringWriter; + +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class InputMethodManagerServiceTests { @@ -87,4 +96,25 @@ public class InputMethodManagerServiceTests { InputMethodManagerService.computeImeDisplayIdForTarget( SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, sChecker)); } + + @Test + public void testSoftInputShowHideHistoryDump_withNulls_doesntThrow() { + var writer = new StringWriter(); + var history = new InputMethodManagerService.SoftInputShowHideHistory(); + history.addEntry(new InputMethodManagerService.SoftInputShowHideHistory.Entry( + null, + null, + null, + SOFT_INPUT_STATE_UNSPECIFIED, + SoftInputShowHideReason.SHOW_SOFT_INPUT, + false, + null, + null, + null, + null)); + + history.dump(new PrintWriter(writer), "" /* prefix */); + + // Asserts that dump doesn't throw an NPE. + } } diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java index d9cd77d8cd7c..af144cf49a46 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java @@ -37,7 +37,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import android.annotation.NonNull; -import android.content.AttributionSource; import android.content.Context; import android.net.ConnectivityManager; import android.net.INetd; @@ -50,7 +49,7 @@ import android.os.IBinder; import android.os.PermissionEnforcer; import android.os.Process; import android.os.RemoteException; -import android.permission.PermissionCheckerManager; +import android.os.test.FakePermissionEnforcer; import android.platform.test.annotations.Presubmit; import android.test.suitebuilder.annotation.SmallTest; import android.util.ArrayMap; @@ -90,7 +89,6 @@ public class NetworkManagementServiceTest { private ArgumentCaptor<INetdUnsolicitedEventListener> mUnsolListenerCaptor; private final MockDependencies mDeps = new MockDependencies(); - private final MockPermissionEnforcer mPermissionEnforcer = new MockPermissionEnforcer(); private final class MockDependencies extends NetworkManagementService.Dependencies { @Override @@ -118,24 +116,6 @@ public class NetworkManagementServiceTest { } } - private static final class MockPermissionEnforcer extends PermissionEnforcer { - @Override - protected int checkPermission(@NonNull String permission, - @NonNull AttributionSource source) { - String[] granted = new String [] { - android.Manifest.permission.NETWORK_SETTINGS, - android.Manifest.permission.OBSERVE_NETWORK_POLICY, - android.Manifest.permission.SHUTDOWN - }; - for (String p : granted) { - if (p.equals(permission)) { - return PermissionCheckerManager.PERMISSION_GRANTED; - } - } - return PermissionCheckerManager.PERMISSION_HARD_DENIED; - } - } - @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -145,12 +125,15 @@ public class NetworkManagementServiceTest { eq(ConnectivityManager.class)); doReturn(mCm).when(mContext).getSystemService(eq(Context.CONNECTIVITY_SERVICE)); // The AIDL stub will use PermissionEnforcer to check permission from the caller. - // Mock the service. See MockPermissionEnforcer above. + // Mock the service and grant the expected permissions. + FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer(); + permissionEnforcer.grant(android.Manifest.permission.NETWORK_SETTINGS); + permissionEnforcer.grant(android.Manifest.permission.OBSERVE_NETWORK_POLICY); + permissionEnforcer.grant(android.Manifest.permission.SHUTDOWN); doReturn(Context.PERMISSION_ENFORCER_SERVICE).when(mContext).getSystemServiceName( eq(PermissionEnforcer.class)); - doReturn(mPermissionEnforcer).when(mContext).getSystemService( + doReturn(permissionEnforcer).when(mContext).getSystemService( eq(Context.PERMISSION_ENFORCER_SERVICE)); - // Start the service and wait until it connects to our socket. mNMService = NetworkManagementService.create(mContext, mDeps); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java index 397e3c1f55a2..539f329cae98 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java @@ -36,6 +36,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.Manifest; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; @@ -47,7 +48,6 @@ import android.util.Pair; import androidx.test.runner.AndroidJUnit4; import com.android.server.UiServiceTestCase; -import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -67,7 +67,7 @@ import java.util.Set; public class PermissionHelperTest extends UiServiceTestCase { @Mock - private PermissionManagerServiceInternal mPmi; + private Context mContext; @Mock private IPackageManager mPackageManager; @Mock @@ -80,7 +80,7 @@ public class PermissionHelperTest extends UiServiceTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager); + mPermissionHelper = new PermissionHelper(mContext, mPackageManager, mPermManager); PackageInfo testPkgInfo = new PackageInfo(); testPkgInfo.requestedPermissions = new String[]{ Manifest.permission.POST_NOTIFICATIONS }; when(mPackageManager.getPackageInfo(anyString(), anyLong(), anyInt())) @@ -89,12 +89,12 @@ public class PermissionHelperTest extends UiServiceTestCase { @Test public void testHasPermission() throws Exception { - when(mPmi.checkUidPermission(anyInt(), anyString())) + when(mContext.checkPermission(anyString(), anyInt(), anyInt())) .thenReturn(PERMISSION_GRANTED); assertThat(mPermissionHelper.hasPermission(1)).isTrue(); - when(mPmi.checkUidPermission(anyInt(), anyString())) + when(mContext.checkPermission(anyString(), anyInt(), anyInt())) .thenReturn(PERMISSION_DENIED); assertThat(mPermissionHelper.hasPermission(1)).isFalse(); @@ -241,7 +241,7 @@ public class PermissionHelperTest extends UiServiceTestCase { @Test public void testSetNotificationPermission_grantUserSet() throws Exception { - when(mPmi.checkPermission(anyString(), anyString(), anyInt())) + when(mContext.checkPermission(anyString(), anyInt(), anyInt())) .thenReturn(PERMISSION_DENIED); mPermissionHelper.setNotificationPermission("pkg", 10, true, true); @@ -255,7 +255,7 @@ public class PermissionHelperTest extends UiServiceTestCase { @Test public void testSetNotificationPermission_pkgPerm_grantedByDefaultPermSet_allUserSet() throws Exception { - when(mPmi.checkPermission(anyString(), anyString(), anyInt())) + when(mContext.checkPermission(anyString(), anyInt(), anyInt())) .thenReturn(PERMISSION_DENIED); when(mPermManager.getPermissionFlags(anyString(), eq(Manifest.permission.POST_NOTIFICATIONS), @@ -273,7 +273,7 @@ public class PermissionHelperTest extends UiServiceTestCase { @Test public void testSetNotificationPermission_revokeUserSet() throws Exception { - when(mPmi.checkPermission(anyString(), anyString(), anyInt())) + when(mContext.checkPermission(anyString(), anyInt(), anyInt())) .thenReturn(PERMISSION_GRANTED); mPermissionHelper.setNotificationPermission("pkg", 10, false, true); @@ -287,7 +287,7 @@ public class PermissionHelperTest extends UiServiceTestCase { @Test public void testSetNotificationPermission_grantNotUserSet() throws Exception { - when(mPmi.checkPermission(anyString(), anyString(), anyInt())) + when(mContext.checkPermission(anyString(), anyInt(), anyInt())) .thenReturn(PERMISSION_DENIED); mPermissionHelper.setNotificationPermission("pkg", 10, true, false); @@ -300,7 +300,7 @@ public class PermissionHelperTest extends UiServiceTestCase { @Test public void testSetNotificationPermission_revokeNotUserSet() throws Exception { - when(mPmi.checkPermission(anyString(), anyString(), anyInt())) + when(mContext.checkPermission(anyString(), anyInt(), anyInt())) .thenReturn(PERMISSION_GRANTED); mPermissionHelper.setNotificationPermission("pkg", 10, false, false); @@ -340,7 +340,7 @@ public class PermissionHelperTest extends UiServiceTestCase { @Test public void testSetNotificationPermission_alreadyGrantedNotRegranted() throws Exception { - when(mPmi.checkPermission(anyString(), anyString(), anyInt())) + when(mContext.checkPermission(anyString(), anyInt(), anyInt())) .thenReturn(PERMISSION_GRANTED); mPermissionHelper.setNotificationPermission("pkg", 10, true, false); @@ -350,7 +350,7 @@ public class PermissionHelperTest extends UiServiceTestCase { @Test public void testSetNotificationPermission_alreadyRevokedNotRerevoked() throws Exception { - when(mPmi.checkPermission(anyString(), anyString(), anyInt())) + when(mContext.checkPermission(anyString(), anyInt(), anyInt())) .thenReturn(PERMISSION_DENIED); mPermissionHelper.setNotificationPermission("pkg", 10, false, false); @@ -360,16 +360,19 @@ public class PermissionHelperTest extends UiServiceTestCase { @Test public void testSetNotificationPermission_doesntRequestNotChanged() throws Exception { - when(mPmi.checkPermission(anyString(), anyString(), anyInt())) + int testUid = -1; + when(mContext.checkPermission(anyString(), anyInt(), anyInt())) .thenReturn(PERMISSION_GRANTED); + when(mPackageManager.getPackageUid(anyString(), anyInt(), anyInt())) + .thenReturn(testUid); PackageInfo testPkgInfo = new PackageInfo(); testPkgInfo.requestedPermissions = new String[]{ Manifest.permission.RECORD_AUDIO }; when(mPackageManager.getPackageInfo(anyString(), anyLong(), anyInt())) .thenReturn(testPkgInfo); mPermissionHelper.setNotificationPermission("pkg", 10, false, false); - verify(mPmi, never()).checkPermission( - eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10)); + verify(mContext, never()).checkPermission( + eq(Manifest.permission.POST_NOTIFICATIONS), eq(-1), eq(testUid)); verify(mPermManager, never()).revokeRuntimePermission( eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10), anyString()); } diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index 5636795520e9..3513557d8374 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -202,9 +202,11 @@ class TestPhoneWindowManager { .canonicalToCurrentPackageNames(any()); } catch (PackageManager.NameNotFoundException ignored) { } - doReturn(mTelecomManager).when(mContext).getSystemService(eq(Context.TELECOM_SERVICE)); - doReturn(mNotificationManager).when(mContext) - .getSystemService(eq(NotificationManager.class)); + doReturn(false).when(mTelecomManager).isInCall(); + doReturn(false).when(mTelecomManager).isRinging(); + doReturn(mTelecomManager).when(mPhoneWindowManager).getTelecommService(); + doNothing().when(mNotificationManager).silenceNotificationSound(); + doReturn(mNotificationManager).when(mPhoneWindowManager).getNotificationService(); doReturn(mVibrator).when(mContext).getSystemService(eq(Context.VIBRATOR_SERVICE)); final PowerManager.WakeLock wakeLock = mock(PowerManager.WakeLock.class); @@ -235,6 +237,7 @@ class TestPhoneWindowManager { doNothing().when(mPhoneWindowManager).screenTurnedOn(anyInt()); doNothing().when(mPhoneWindowManager).startedWakingUp(anyInt(), anyInt()); doNothing().when(mPhoneWindowManager).finishedWakingUp(anyInt(), anyInt()); + doNothing().when(mPhoneWindowManager).lockNow(any()); mPhoneWindowManager.init(new TestInjector(mContext, mWindowManagerFuncsImpl)); mPhoneWindowManager.systemReady(); @@ -249,6 +252,7 @@ class TestPhoneWindowManager { void tearDown() { mHandlerThread.quitSafely(); LocalServices.removeServiceForTest(InputMethodManagerInternal.class); + Mockito.reset(mPhoneWindowManager); mMockitoSession.finishMocking(); } @@ -322,6 +326,7 @@ class TestPhoneWindowManager { void overrideDisplayState(int state) { doReturn(state).when(mDisplay).getState(); + doReturn(state == STATE_ON).when(mDisplayPolicy).isAwake(); Mockito.reset(mPowerManager); } @@ -388,6 +393,7 @@ class TestPhoneWindowManager { } void assertDreamRequest() { + waitForIdle(); verify(mDreamManagerInternal).requestDream(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index dfc453f0f1b6..d173ce9d522a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -1134,7 +1134,7 @@ public class RootWindowContainerTests extends WindowTestsBase { TaskChangeNotificationController controller = mAtm.getTaskChangeNotificationController(); spyOn(controller); mWm.mRoot.lockAllProfileTasks(profileUserId); - verify(controller).notifyTaskProfileLocked(any()); + verify(controller).notifyTaskProfileLocked(any(), eq(profileUserId)); // Create the work lock activity on top of the task final ActivityRecord workLockActivity = new ActivityBuilder(mAtm).setTask(task).build(); @@ -1144,7 +1144,7 @@ public class RootWindowContainerTests extends WindowTestsBase { // Make sure the listener won't be notified again. clearInvocations(controller); mWm.mRoot.lockAllProfileTasks(profileUserId); - verify(controller, never()).notifyTaskProfileLocked(any()); + verify(controller, never()).notifyTaskProfileLocked(any(), anyInt()); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java index 342ab83de6f0..4f45d5c2bf98 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java @@ -125,9 +125,10 @@ public class SurfaceControlTests { public void testSurfaceChangedOnRotation() { final Instrumentation instrumentation = getInstrumentation(); final Context context = instrumentation.getContext(); - final Activity activity = instrumentation.startActivitySync(new Intent().setComponent( + final Intent intent = new Intent().setComponent( new ComponentName(context, ActivityOptionsTest.MainActivity.class)) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP); + final Activity activity = instrumentation.startActivitySync(intent); final SurfaceView sv = new SurfaceView(activity); final AtomicInteger surfaceChangedCount = new AtomicInteger(); instrumentation.runOnMainSync(() -> activity.setContentView(sv)); @@ -157,12 +158,27 @@ public class SurfaceControlTests { instrumentation.waitForIdleSync(); final int newRotation = activity.getResources().getConfiguration() .windowConfiguration.getRotation(); + if (rotation == newRotation) { + // The device might not support requested orientation. + activity.finishAndRemoveTask(); + return; + } final int count = surfaceChangedCount.get(); + activity.moveTaskToBack(true /* nonRoot */); + instrumentation.getUiAutomation().syncInputTransactions(); + context.startActivity(intent); + instrumentation.getUiAutomation().syncInputTransactions(); + final int countAfterToFront = count - surfaceChangedCount.get(); activity.finishAndRemoveTask(); + // The first count is triggered from creation, so the target number is 2. - if (rotation != newRotation && count > 2) { + if (count > 2) { fail("More than once surfaceChanged for rotation change: " + count); } + if (countAfterToFront > 1) { + fail("More than once surfaceChanged for app transition with rotation change: " + + countAfterToFront); + } } private SurfaceControl buildTestSurface() { diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index a98429ad4902..ef1359449b9b 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -2539,10 +2539,20 @@ public class UsageStatsService extends SystemService implements } @Override - public void reportChooserSelection(String packageName, int userId, String contentType, - String[] annotations, String action) { + public void reportChooserSelection(@NonNull String packageName, int userId, + @NonNull String contentType, String[] annotations, @NonNull String action) { if (packageName == null) { - Slog.w(TAG, "Event report user selecting a null package"); + throw new IllegalArgumentException("Package selection must not be null."); + } + if (contentType == null) { + throw new IllegalArgumentException("Content type for selection must not be null."); + } + if (action == null) { + throw new IllegalArgumentException("Selection action must not be null."); + } + // Verify if this package exists before reporting an event for it. + if (mPackageManagerInternal.getPackageUid(packageName, 0, userId) < 0) { + Slog.w(TAG, "Event report user selecting an invalid package"); return; } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java index b062e6b08e00..913535e06a21 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java @@ -296,7 +296,10 @@ public class SoundTriggerService extends SystemService { // Helper to add session logger to the capacity limited detached list. // If we are at capacity, remove the oldest, and retry - private void addDetachedSessionLogger(EventLogger logger) { + private void detachSessionLogger(EventLogger logger) { + if (!mSessionEventLoggers.remove(logger)) { + return; + } // Attempt to push to the top of the queue while (!mDetachedSessionEventLoggers.offerFirst(logger)) { // Remove the oldest element, if one still exists @@ -872,8 +875,7 @@ public class SoundTriggerService extends SystemService { private void detach() { mSoundTriggerHelper.detach(); - mSessionEventLoggers.remove(mEventLogger); - addDetachedSessionLogger(mEventLogger); + detachSessionLogger(mEventLogger); } private void enforceCallingPermission(String permission) { @@ -1659,8 +1661,7 @@ public class SoundTriggerService extends SystemService { private void detachInternal() { mEventLogger.enqueue(new SessionEvent(Type.DETACH, null)); - mSessionEventLoggers.remove(mEventLogger); - addDetachedSessionLogger(mEventLogger); + detachSessionLogger(mEventLogger); mSoundTriggerHelper.detach(); } } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java index 13fe14caa1f9..00cedd77414e 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java @@ -78,7 +78,7 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware public @NonNull SoundTriggerModuleDescriptor[] listModules() { Identity identity = getIdentity(); - enforcePermissionsForPreflight(identity); + enforcePermissionForPreflight(mContext, identity, CAPTURE_AUDIO_HOTWORD); return mDelegate.listModules(); } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 486945d9159e..edaaf3fbbccc 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -913,8 +913,10 @@ final class HotwordDetectionConnection { } // Handle case where all hotword detector sessions are destroyed with only the visual // detector session left - if (mDetectorSessions.size() == 1 - && mDetectorSessions.get(0) instanceof VisualQueryDetectorSession) { + boolean allHotwordDetectionServiceSessionsRemoved = mDetectorSessions.size() == 0 + || (mDetectorSessions.size() == 1 && mDetectorSessions.get(0) + instanceof VisualQueryDetectorSession); + if (allHotwordDetectionServiceSessionsRemoved) { unbindHotwordDetectionService(); } } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index f27c23b29d67..188894339f6a 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -5773,14 +5773,15 @@ public class CarrierConfigManager { * </ul> * @hide */ - public static final String KEY_SA_DISABLE_POLICY_INT = KEY_PREFIX + "sa_disable_policy_int"; + public static final String KEY_NR_SA_DISABLE_POLICY_INT = + KEY_PREFIX + "sa_disable_policy_int"; /** @hide */ @IntDef({ - SA_DISABLE_POLICY_NONE, - SA_DISABLE_POLICY_WFC_ESTABLISHED, - SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED, - SA_DISABLE_POLICY_VOWIFI_REGISTERED + NR_SA_DISABLE_POLICY_NONE, + NR_SA_DISABLE_POLICY_WFC_ESTABLISHED, + NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED, + NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED }) public @interface NrSaDisablePolicy {} @@ -5788,14 +5789,14 @@ public class CarrierConfigManager { * Do not disables NR SA mode. * @hide */ - public static final int SA_DISABLE_POLICY_NONE = 0; + public static final int NR_SA_DISABLE_POLICY_NONE = 0; /** * Disables NR SA mode when VoWiFi call is established in order to improve the delay or * voice mute when the handover from ePDG to NR is not supported in UE or network. * @hide */ - public static final int SA_DISABLE_POLICY_WFC_ESTABLISHED = 1; + public static final int NR_SA_DISABLE_POLICY_WFC_ESTABLISHED = 1; /** * Disables NR SA mode when VoWiFi call is established when VoNR is disabled in order to @@ -5803,14 +5804,14 @@ public class CarrierConfigManager { * in UE or network. * @hide */ - public static final int SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED = 2; + public static final int NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED = 2; /** * Disables NR SA mode when IMS is registered over WiFi in order to improve the delay or * voice mute when the handover from ePDG to NR is not supported in UE or network. * @hide */ - public static final int SA_DISABLE_POLICY_VOWIFI_REGISTERED = 3; + public static final int NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED = 3; private Ims() {} @@ -5883,7 +5884,7 @@ public class CarrierConfigManager { defaults.putInt(KEY_REGISTRATION_RETRY_BASE_TIMER_MILLIS_INT, 30000); defaults.putInt(KEY_REGISTRATION_RETRY_MAX_TIMER_MILLIS_INT, 1800000); defaults.putInt(KEY_REGISTRATION_SUBSCRIBE_EXPIRY_TIMER_SEC_INT, 600000); - defaults.putInt(KEY_SA_DISABLE_POLICY_INT, SA_DISABLE_POLICY_NONE); + defaults.putInt(KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_NONE); defaults.putIntArray( KEY_IPSEC_AUTHENTICATION_ALGORITHMS_INT_ARRAY, |