diff options
14 files changed, 539 insertions, 280 deletions
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 411d157fa927..4851279eea97 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -694,12 +694,22 @@ public class Dialog implements DialogInterface, Window.Callback, */ @Override public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) { - if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) - && event.isTracking() - && !event.isCanceled() - && !WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) { - onBackPressed(); - return true; + if (event.isTracking() && !event.isCanceled()) { + switch (keyCode) { + case KeyEvent.KEYCODE_BACK: + if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) { + onBackPressed(); + return true; + } + break; + case KeyEvent.KEYCODE_ESCAPE: + if (mCancelable) { + cancel(); + } else { + dismiss(); + } + return true; + } } return false; } diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index c87911e407e3..244523f2978d 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -441,7 +441,7 @@ <string name="permdesc_writeSettings" msgid="8293047411196067188">"Колдонмого системанын коопсуздук параметрлеринин дайындарын өзгөртүү мүмкүнчүлүгүн берет. Кесепттүү колдонмолор системаңыздын конфигурациясын бузуп салышы мүмкүн."</string> <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"түзмөктү жандырганда иштеп баштоо"</string> <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"Колдонмого тутум жүктөлүп бүтөөрү менен өзүн-өзү иштетүү мүмкүнчүлүгүн берет. Бул планшеттин ишке киргизилишин кыйла создуктуруп, планшеттин үзгүлтүксүз иштешин жайлатып салышы мүмкүн."</string> - <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"Тутум күйгүзүлөрү менен колдонмого өз алдынча иштеп баштоого уруксат берет. Ага байланыштуу Android TV түзмөгүңүз кечирээк күйгүзүлүп, ошондой эле колдонмо такай иштеп тургандыктан, түзмөк жайыраак иштеп калышы мүмкүн."</string> + <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"Система күйгүзүлөрү менен колдонмого өз алдынча иштеп баштоого уруксат берет. Ага байланыштуу Android TV түзмөгүңүз кечирээк күйгүзүлүп, ошондой эле колдонмо такай иштеп тургандыктан, түзмөк жайыраак иштеп калышы мүмкүн."</string> <string name="permdesc_receiveBootCompleted" product="default" msgid="7912677044558690092">"Колдонмого тутум жүктөлүп бүтөөрү менен өзүн-өзү иштетүү мүмкүнчүлүгүн берет. Бул телефондун ишке киргизилишин кыйла создуктуруп, телефондун үзгүлтүксүз иштешин жайлатып салышы мүмкүн."</string> <string name="permlab_broadcastSticky" msgid="4552241916400572230">"жабышчаак таркатманы жөнөтүү"</string> <string name="permdesc_broadcastSticky" product="tablet" msgid="5058486069846384013">"Колдонмого берүү токтогондон кийин улантыла берүүчү жабышкак берүүлөрдү жөнөтүү уруксатын берет. Муну ашыкча колдонуу, эстутумду өтө көп пайдаланууга алып келип, планшеттин жай же туруксуз иштөөсүнүнө себепкер болушу мүмкүн."</string> @@ -1207,7 +1207,7 @@ <string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Сүрөткө тартуу"</string> <string name="alwaysUse" msgid="3153558199076112903">"Бул аракет үчүн демейки боюнча колдонулсун."</string> <string name="use_a_different_app" msgid="4987790276170972776">"Башка колдонмону пайдалануу"</string> - <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Тутум жөндөөлөрүндөгү демейкини тазалоо > Колдонмолор > Жүктөлүп алынды."</string> + <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Система жөндөөлөрүндөгү демейкини тазалоо > Колдонмолор > Жүктөлүп алынды."</string> <string name="chooseActivity" msgid="8563390197659779956">"Аракет тандаңыз"</string> <string name="chooseUsbActivity" msgid="2096269989990986612">"USB түзмөгү үчүн колдонмо тандаңыз"</string> <string name="noApplications" msgid="1186909265235544019">"Бул аракетти аткара турган колдонмо жок."</string> @@ -1623,7 +1623,7 @@ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Тышкы түзмөк"</string> <string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Кулакчын"</string> <string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string> - <string name="default_audio_route_category_name" msgid="5241740395748134483">"Тутум"</string> + <string name="default_audio_route_category_name" msgid="5241740395748134483">"Система"</string> <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"Bluetooth аудио"</string> <string name="wireless_display_route_description" msgid="8297563323032966831">"Зымсыз дисплей"</string> <string name="media_route_button_content_description" msgid="2299223698196869956">"Тышкы экранга чыгаруу"</string> @@ -2068,7 +2068,7 @@ <string name="screenshot_edit" msgid="7408934887203689207">"Түзөтүү"</string> <string name="volume_dialog_ringer_guidance_vibrate" msgid="2055927873175228519">"Чалуулар менен билдирмелер дирилдөө режиминде иштейт"</string> <string name="volume_dialog_ringer_guidance_silent" msgid="1011246774949993783">"Чалуулар менен эскертмелердин үнү өчүрүлөт"</string> - <string name="notification_channel_system_changes" msgid="2462010596920209678">"Тутум өзгөрүүлөрү"</string> + <string name="notification_channel_system_changes" msgid="2462010596920209678">"Система өзгөрүүлөрү"</string> <string name="notification_channel_do_not_disturb" msgid="7832584281883687653">"Тынчымды алба"</string> <string name="zen_upgrade_notification_visd_title" msgid="2001148984371968620">"Жаңы: \"Тынчымды алба\" режими билдирмелерди жашырууда"</string> <string name="zen_upgrade_notification_visd_content" msgid="3683314609114134946">"Көбүрөөк маалымат алып, өзгөртүү үчүн таптаңыз."</string> @@ -2078,7 +2078,7 @@ <string name="review_notification_settings_text" msgid="5916244866751849279">"Android 13 версиясынан баштап билдирмелерди жөнөтүү үчүн орноткон колдонмолоруңузга уруксат берүү керек. Учурдагы колдонмолор үчүн бул уруксатты өзгөртүү үчүн таптап коюңуз."</string> <string name="review_notification_settings_remind_me_action" msgid="1081081018678480907">"Кийинчерээк эскертүү"</string> <string name="review_notification_settings_dismiss" msgid="4160916504616428294">"Жабуу"</string> - <string name="notification_app_name_system" msgid="3045196791746735601">"Тутум"</string> + <string name="notification_app_name_system" msgid="3045196791746735601">"Система"</string> <string name="notification_app_name_settings" msgid="9088548800899952531">"Параметрлер"</string> <string name="notification_appops_camera_active" msgid="8177643089272352083">"Камера"</string> <string name="notification_appops_microphone_active" msgid="581333393214739332">"Микрофон"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 903ee0b81574..aad9eb278583 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -694,7 +694,7 @@ <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"आफ्नो फोनमा अझ सीधा हेर्नुहोस्"</string> <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"आफ्नो फोनमा अझ सीधा हेर्नुहोस्"</string> <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"आफ्नो फोनमा अझ सीधा हेर्नुहोस्"</string> - <string name="face_acquired_obscured" msgid="4917643294953326639">"तपाईंको अनुहार लुकाउने सबै कुरा हटाउनुहोस्।"</string> + <string name="face_acquired_obscured" msgid="4917643294953326639">"आफ्नो अनुहार छोप्ने सबै कुरा हटाउनुहोस्।"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"कालो रङको पट्टीलगायत आफ्नो स्क्रिनको माथिल्लो भाग सफा गर्नुहोस्"</string> <!-- no translation found for face_acquired_dark_glasses_detected (5643703296620631986) --> <skip /> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 6f5b3ff9ad66..d327499842ad 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -115,8 +115,7 @@ <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"ສຽງ HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"ສຽງ HD"</string> <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"ອຸປະກອນຊ່ວຍຟັງ"</string> - <!-- no translation found for bluetooth_profile_le_audio (6125267378720026515) --> - <skip /> + <string name="bluetooth_profile_le_audio" msgid="6125267378720026515">"ສຽງ LE (ການທົດລອງ)"</string> <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"ເຊື່ອມຕໍ່ຫາອຸປະກອນຊ່ວຍຟັງແລ້ວ"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"ເຊື່ອມຕໍ່ຫາສຽງ LE ແລ້ວ"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"ເຊື່ອມຕໍ່ກັບສື່ດ້ານສຽງແລ້ວ"</string> diff --git a/packages/SystemUI/res-product/values-my/strings.xml b/packages/SystemUI/res-product/values-my/strings.xml index 68711e8fa0ee..4ec1ff6e3bde 100644 --- a/packages/SystemUI/res-product/values-my/strings.xml +++ b/packages/SystemUI/res-product/values-my/strings.xml @@ -23,7 +23,7 @@ <string name="dock_alignment_not_charging" product="default" msgid="3980752926226749808">"ကြိုးမဲ့အားသွင်းရန် ဖုန်းကို ပြန်၍ချိန်ပါ"</string> <string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"Android TV စက်သည် မကြာမီ ပိတ်သွားပါမည်၊ ဆက်ဖွင့်ထားရန် ခလုတ်တစ်ခုကို နှိပ်ပါ။"</string> <string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"စက်သည် မကြာမီ ပိတ်သွားပါမည်၊ ဆက်ဖွင့်ထားရန် နှိပ်ပါ။"</string> - <string name="keyguard_missing_sim_message" product="tablet" msgid="408124574073032188">"တက်ဘလက်တွင် ဆင်းမ်ကတ်မရှိပါ။"</string> + <string name="keyguard_missing_sim_message" product="tablet" msgid="408124574073032188">"တက်ဘလက်တွင် ဆင်းမ်မရှိပါ။"</string> <string name="keyguard_missing_sim_message" product="default" msgid="2605468359948247208">"ဖုန်းတွင် ဆင်းမ်မရှိပါ။"</string> <string name="kg_invalid_confirm_pin_hint" product="default" msgid="6278551068943958651">"ပင်နံပါတ် ကိုက်ညီမှု မရှိပါ"</string> <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"တက်ဘလက်ကို <xliff:g id="NUMBER_0">%1$d</xliff:g> ကြိမ် မှားယွင်းစွာ လော့ခ်ဖွင့်ရန် ကြိုးစားခဲ့ပါသည်။ <xliff:g id="NUMBER_1">%2$d</xliff:g> ကြိမ် ထပ်မံမှားယွင်းခဲ့လျှင် ဤတက်ဘလက်ကို ပြင်ဆင်သတ်မှတ်လိုက်မည် ဖြစ်ပြီး ၎င်းအတွင်းရှိ ဒေတာအားလုံးကိုလည်း ဖျက်လိုက်ပါမည်။"</string> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 93b6cda29dc5..4118b6b1ff73 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -985,8 +985,10 @@ <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Luidsprekers en skerms"</string> <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Voorgestelde toestelle"</string> - <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Stop jou gedeelde sessie om media na ’n ander toestel toe te skuif"</string> - <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Stop"</string> + <!-- no translation found for media_output_end_session_dialog_summary (5954520685989877347) --> + <skip /> + <!-- no translation found for media_output_end_session_dialog_stop (208189434474624412) --> + <skip /> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Hoe uitsaai werk"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Saai uit"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Mense in jou omtrek met versoenbare Bluetooth-toestelle kan na die media luister wat jy uitsaai"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 7b4e12d3512e..c7ecb620a6d1 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -1009,8 +1009,10 @@ <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%% <xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string> <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Bozgorailuak eta pantailak"</string> <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Iradokitako gailuak"</string> - <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Gelditu partekatutako saioa multimedia-edukia beste gailu batera eramateko"</string> - <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Gelditu"</string> + <!-- no translation found for media_output_end_session_dialog_summary (5954520685989877347) --> + <skip /> + <!-- no translation found for media_output_end_session_dialog_stop (208189434474624412) --> + <skip /> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Nola funtzionatzen dute iragarpenek?"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Iragarri"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Bluetooth bidezko gailu bateragarriak dituzten inguruko pertsonek iragartzen ari zaren multimedia-edukia entzun dezakete"</string> diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 4b6fd34305ed..32a6481cca56 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -691,8 +691,7 @@ object Flags { // TODO(b/259428678): Tracking Bug @JvmField - val KEYBOARD_BACKLIGHT_INDICATOR = - unreleasedFlag(2601, "keyboard_backlight_indicator", teamfood = true) + val KEYBOARD_BACKLIGHT_INDICATOR = releasedFlag(2601, "keyboard_backlight_indicator") // TODO(b/277192623): Tracking Bug @JvmField 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 42de7f0f3a8b..fcae081f7edd 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -38,6 +38,7 @@ import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Region; import android.hardware.input.InputManager; +import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.SystemClock; @@ -192,6 +193,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private final ViewConfiguration mViewConfiguration; private final WindowManager mWindowManager; private final IWindowManager mWindowManagerService; + private final InputManager mInputManager; private final Optional<Pip> mPipOptional; private final Optional<DesktopMode> mDesktopModeOptional; private final FalsingManager mFalsingManager; @@ -203,6 +205,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private final int mDisplayId; private final Executor mMainExecutor; + private final Handler mMainHandler; private final Executor mBackgroundExecutor; private final Rect mPipExcludedBounds = new Rect(); @@ -246,6 +249,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private boolean mIsAttached; private boolean mIsGesturalModeEnabled; + private boolean mIsTrackpadConnected; + private boolean mUsingThreeButtonNav; private boolean mIsEnabled; private boolean mIsNavBarShownTransiently; private boolean mIsBackGestureAllowed; @@ -343,12 +348,48 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack } }; + private final InputManager.InputDeviceListener mInputDeviceListener = + new InputManager.InputDeviceListener() { + + // Only one trackpad can be connected to a device at a time, since it takes over the + // only USB port. + private int mTrackpadDeviceId; + + @Override + public void onInputDeviceAdded(int deviceId) { + if (isTrackpadDevice(deviceId)) { + mTrackpadDeviceId = deviceId; + update(true /* isTrackpadConnected */); + } + } + + @Override + public void onInputDeviceChanged(int deviceId) { } + + @Override + public void onInputDeviceRemoved(int deviceId) { + if (mTrackpadDeviceId == deviceId) { + update(false /* isTrackpadConnected */); + } + } + + private void update(boolean isTrackpadConnected) { + boolean isPreviouslyTrackpadConnected = mIsTrackpadConnected; + mIsTrackpadConnected = isTrackpadConnected; + if (isPreviouslyTrackpadConnected != mIsTrackpadConnected) { + updateIsEnabled(); + updateCurrentUserResources(); + } + } + }; + EdgeBackGestureHandler( Context context, OverviewProxyService overviewProxyService, SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor, + @Main Handler handler, @Background Executor backgroundExecutor, UserTracker userTracker, ProtoTracer protoTracer, @@ -357,6 +398,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack ViewConfiguration viewConfiguration, WindowManager windowManager, IWindowManager windowManagerService, + InputManager inputManager, Optional<Pip> pipOptional, Optional<DesktopMode> desktopModeOptional, FalsingManager falsingManager, @@ -367,6 +409,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mContext = context; mDisplayId = context.getDisplayId(); mMainExecutor = executor; + mMainHandler = handler; mBackgroundExecutor = backgroundExecutor; mUserTracker = userTracker; mOverviewProxyService = overviewProxyService; @@ -378,6 +421,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mViewConfiguration = viewConfiguration; mWindowManager = windowManager; mWindowManagerService = windowManagerService; + mInputManager = inputManager; mPipOptional = pipOptional; mDesktopModeOptional = desktopModeOptional; mFalsingManager = falsingManager; @@ -386,6 +430,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mFeatureFlags = featureFlags; mLightBarControllerProvider = lightBarControllerProvider; mLastReportedConfig.setTo(mContext.getResources().getConfiguration()); + mIsTrackpadGestureFeaturesEnabled = mFeatureFlags.isEnabled( + Flags.TRACKPAD_GESTURE_FEATURES); ComponentName recentsComponentName = ComponentName.unflattenFromString( context.getString(com.android.internal.R.string.config_recentsComponentName)); if (recentsComponentName != null) { @@ -417,7 +463,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack ViewConfiguration.getLongPressTimeout()); mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver( - mContext.getMainThreadHandler(), mContext, this::onNavigationSettingsChanged); + mMainHandler, mContext, this::onNavigationSettingsChanged); updateCurrentUserResources(); } @@ -496,6 +542,15 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mProtoTracer.add(this); mOverviewProxyService.addCallback(mQuickSwitchListener); mSysUiState.addCallback(mSysUiStateCallback); + if (mIsTrackpadGestureFeaturesEnabled) { + mInputManager.registerInputDeviceListener(mInputDeviceListener, mMainHandler); + int [] inputDevices = mInputManager.getInputDeviceIds(); + for (int inputDeviceId : inputDevices) { + if (isTrackpadDevice(inputDeviceId)) { + mIsTrackpadConnected = true; + } + } + } updateIsEnabled(); mUserTracker.addCallback(mUserChangedCallback, mMainExecutor); } @@ -508,6 +563,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mProtoTracer.remove(this); mOverviewProxyService.removeCallback(mQuickSwitchListener); mSysUiState.removeCallback(mSysUiStateCallback); + mInputManager.unregisterInputDeviceListener(mInputDeviceListener); updateIsEnabled(); mUserTracker.removeCallback(mUserChangedCallback); } @@ -516,7 +572,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack * @see NavigationModeController.ModeChangedListener#onNavigationModeChanged */ public void onNavigationModeChanged(int mode) { - mIsGesturalModeEnabled = QuickStepContract.isGesturalMode(mode); + mUsingThreeButtonNav = QuickStepContract.isLegacyMode(mode); + mIsGesturalModeEnabled = QuickStepContract.isGesturalMode(mode) || ( + mIsTrackpadGestureFeaturesEnabled && mUsingThreeButtonNav && mIsTrackpadConnected); updateIsEnabled(); updateCurrentUserResources(); } @@ -606,8 +664,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack // Add a nav bar panel window mIsNewBackAffordanceEnabled = mFeatureFlags.isEnabled(Flags.NEW_BACK_AFFORDANCE); - mIsTrackpadGestureFeaturesEnabled = mFeatureFlags.isEnabled( - Flags.TRACKPAD_GESTURE_FEATURES); resetEdgeBackPlugin(); mPluginManager.addPluginListener( this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false); @@ -811,6 +867,12 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mDisplaySize.y - insets.bottom); } + private boolean isTrackpadDevice(int deviceId) { + InputDevice inputDevice = mInputManager.getInputDevice(deviceId); + return inputDevice.getSources() == (InputDevice.SOURCE_MOUSE + | InputDevice.SOURCE_TOUCHPAD); + } + private boolean desktopExcludeRegionContains(int x, int y) { return mDesktopModeExcludeRegion.contains(x, y); } @@ -935,17 +997,20 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mMLResults = 0; mLogGesture = false; mInRejectedExclusion = false; - // Trackpad back gestures don't have zones, so we don't need to check if the down event - // 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) + boolean isBackAllowedCommon = !mDisabledForQuickstep && mIsBackGestureAllowed && !mGestureBlockingActivityRunning - && !QuickStepContract.isBackGestureDisabled(mSysUiFlags) - && (isValidTrackpadBackGesture(isTrackpadMultiFingerSwipe) - || isWithinTouchRegion((int) ev.getX(), (int) ev.getY())); + && !QuickStepContract.isBackGestureDisabled(mSysUiFlags); + if (isTrackpadMultiFingerSwipe) { + // Trackpad back gestures don't have zones, so we don't need to check if the down + // event is within insets. + mAllowGesture = isBackAllowedCommon && isValidTrackpadBackGesture( + isTrackpadMultiFingerSwipe); + } else { + mAllowGesture = isBackAllowedCommon && !mUsingThreeButtonNav && isWithinInsets + && isWithinTouchRegion((int) ev.getX(), (int) ev.getY()) + && !isButtonPressFromTrackpad(ev); + } if (mAllowGesture) { mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge); mEdgeBackPlugin.onMotionEvent(ev); @@ -1048,6 +1113,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack } private boolean isButtonPressFromTrackpad(MotionEvent ev) { + // We don't allow back for button press from the trackpad, and yet we do with a mouse. int sources = InputManager.getInstance().getInputDevice(ev.getDeviceId()).getSources(); return (sources & (SOURCE_MOUSE | SOURCE_TOUCHPAD)) == sources && ev.getButtonState() != 0; } @@ -1170,6 +1236,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack pw.println(" mPredictionLog=" + String.join("\n", mPredictionLog)); pw.println(" mGestureLogInsideInsets=" + String.join("\n", mGestureLogInsideInsets)); pw.println(" mGestureLogOutsideInsets=" + String.join("\n", mGestureLogOutsideInsets)); + pw.println(" mIsTrackpadConnected=" + mIsTrackpadConnected); + pw.println(" mUsingThreeButtonNav=" + mUsingThreeButtonNav); pw.println(" mEdgeBackPlugin=" + mEdgeBackPlugin); if (mEdgeBackPlugin != null) { mEdgeBackPlugin.dump(pw); @@ -1219,6 +1287,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private final SysUiState mSysUiState; private final PluginManager mPluginManager; private final Executor mExecutor; + private final Handler mHandler; private final Executor mBackgroundExecutor; private final UserTracker mUserTracker; private final ProtoTracer mProtoTracer; @@ -1227,6 +1296,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private final ViewConfiguration mViewConfiguration; private final WindowManager mWindowManager; private final IWindowManager mWindowManagerService; + private final InputManager mInputManager; private final Optional<Pip> mPipOptional; private final Optional<DesktopMode> mDesktopModeOptional; private final FalsingManager mFalsingManager; @@ -1241,6 +1311,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor, + @Main Handler handler, @Background Executor backgroundExecutor, UserTracker userTracker, ProtoTracer protoTracer, @@ -1249,6 +1320,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack ViewConfiguration viewConfiguration, WindowManager windowManager, IWindowManager windowManagerService, + InputManager inputManager, Optional<Pip> pipOptional, Optional<DesktopMode> desktopModeOptional, FalsingManager falsingManager, @@ -1261,6 +1333,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mSysUiState = sysUiState; mPluginManager = pluginManager; mExecutor = executor; + mHandler = handler; mBackgroundExecutor = backgroundExecutor; mUserTracker = userTracker; mProtoTracer = protoTracer; @@ -1269,6 +1342,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mViewConfiguration = viewConfiguration; mWindowManager = windowManager; mWindowManagerService = windowManagerService; + mInputManager = inputManager; mPipOptional = pipOptional; mDesktopModeOptional = desktopModeOptional; mFalsingManager = falsingManager; @@ -1286,6 +1360,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mSysUiState, mPluginManager, mExecutor, + mHandler, mBackgroundExecutor, mUserTracker, mProtoTracer, @@ -1294,6 +1369,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mViewConfiguration, mWindowManager, mWindowManagerService, + mInputManager, mPipOptional, mDesktopModeOptional, mFalsingManager, diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java index 27215b2fbf30..b0c1d05b0dd4 100644 --- a/services/core/java/com/android/server/DynamicSystemService.java +++ b/services/core/java/com/android/server/DynamicSystemService.java @@ -44,7 +44,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub { private static final String TAG = "DynamicSystemService"; private static final long MINIMUM_SD_MB = (30L << 10); private static final int GSID_ROUGH_TIMEOUT_MS = 8192; - private static final String PATH_DEFAULT = "/data/gsi/"; + private static final String PATH_DEFAULT = "/data/gsi/dsu/"; private Context mContext; private String mInstallPath, mDsuSlot; private volatile IGsiService mGsiService; diff --git a/services/core/java/com/android/server/input/InputFeatureFlagProvider.java b/services/core/java/com/android/server/input/InputFeatureFlagProvider.java new file mode 100644 index 000000000000..3854adad9cd8 --- /dev/null +++ b/services/core/java/com/android/server/input/InputFeatureFlagProvider.java @@ -0,0 +1,67 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.input; + +import android.sysprop.InputProperties; + +import java.util.Optional; + +/** + * A component of {@link InputManagerService} responsible for managing the input sysprop flags + * + * @hide + */ +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public final class InputFeatureFlagProvider { + + // To disable Keyboard backlight control via Framework, run: + // 'adb shell setprop persist.input.keyboard_backlight_control.enabled false' (requires restart) + private static final boolean KEYBOARD_BACKLIGHT_CONTROL_ENABLED = + InputProperties.enable_keyboard_backlight_control().orElse(true); + + // To disable Framework controlled keyboard backlight animation run: + // adb shell setprop persist.input.keyboard.backlight_animation.enabled false (requires restart) + private static final boolean KEYBOARD_BACKLIGHT_ANIMATION_ENABLED = + InputProperties.enable_keyboard_backlight_animation().orElse(false); + + private static Optional<Boolean> sKeyboardBacklightControlOverride = Optional.empty(); + private static Optional<Boolean> sKeyboardBacklightAnimationOverride = Optional.empty(); + + public static boolean isKeyboardBacklightControlEnabled() { + return sKeyboardBacklightControlOverride.orElse(KEYBOARD_BACKLIGHT_CONTROL_ENABLED); + } + + public static boolean isKeyboardBacklightAnimationEnabled() { + return sKeyboardBacklightAnimationOverride.orElse(KEYBOARD_BACKLIGHT_ANIMATION_ENABLED); + } + + public static void setKeyboardBacklightControlEnabled(boolean enabled) { + sKeyboardBacklightControlOverride = Optional.of(enabled); + } + + public static void setKeyboardBacklightAnimationEnabled(boolean enabled) { + sKeyboardBacklightAnimationOverride = Optional.of(enabled); + } + + /** + * Clears all input feature flag overrides. + */ + public static void clearOverrides() { + sKeyboardBacklightControlOverride = Optional.empty(); + sKeyboardBacklightAnimationOverride = Optional.empty(); + } +} diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 662591e3d264..9f3ab885bf16 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -74,7 +74,6 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.VibrationEffect; import android.os.vibrator.StepSegment; @@ -160,11 +159,6 @@ public class InputManagerService extends IInputManager.Stub private static final AdditionalDisplayInputProperties DEFAULT_ADDITIONAL_DISPLAY_INPUT_PROPERTIES = new AdditionalDisplayInputProperties(); - // To disable Keyboard backlight control via Framework, run: - // 'adb shell setprop persist.input.keyboard_backlight_control.enabled false' (requires restart) - private static final boolean KEYBOARD_BACKLIGHT_CONTROL_ENABLED = SystemProperties.getBoolean( - "persist.input.keyboard.backlight_control.enabled", true); - private final NativeInputManagerService mNative; private final Context mContext; @@ -439,10 +433,9 @@ public class InputManagerService extends IInputManager.Stub mKeyboardLayoutManager = new KeyboardLayoutManager(mContext, mNative, mDataStore, injector.getLooper()); mBatteryController = new BatteryController(mContext, mNative, injector.getLooper()); - mKeyboardBacklightController = - KEYBOARD_BACKLIGHT_CONTROL_ENABLED ? new KeyboardBacklightController(mContext, - mNative, mDataStore, injector.getLooper()) - : new KeyboardBacklightControllerInterface() {}; + mKeyboardBacklightController = InputFeatureFlagProvider.isKeyboardBacklightControlEnabled() + ? new KeyboardBacklightController(mContext, mNative, mDataStore, + injector.getLooper()) : new KeyboardBacklightControllerInterface() {}; mKeyRemapper = new KeyRemapper(mContext, mNative, mDataStore, injector.getLooper()); mUseDevInputEventForAudioJack = diff --git a/services/core/java/com/android/server/input/KeyboardBacklightController.java b/services/core/java/com/android/server/input/KeyboardBacklightController.java index 48c346a2fe22..61ca0cbff7bf 100644 --- a/services/core/java/com/android/server/input/KeyboardBacklightController.java +++ b/services/core/java/com/android/server/input/KeyboardBacklightController.java @@ -16,6 +16,7 @@ package com.android.server.input; +import android.animation.ValueAnimator; import android.annotation.BinderThread; import android.content.Context; import android.graphics.Color; @@ -70,6 +71,8 @@ final class KeyboardBacklightController implements private static final int MSG_INTERACTIVE_STATE_CHANGED = 6; private static final int MAX_BRIGHTNESS = 255; private static final int NUM_BRIGHTNESS_CHANGE_STEPS = 10; + private static final long TRANSITION_ANIMATION_DURATION_MILLIS = + Duration.ofSeconds(1).toMillis(); private static final String UEVENT_KEYBOARD_BACKLIGHT_TAG = "kbd_backlight"; @@ -85,6 +88,7 @@ final class KeyboardBacklightController implements @GuardedBy("mDataStore") private final PersistentDataStore mDataStore; private final Handler mHandler; + private final AnimatorFactory mAnimatorFactory; // Always access on handler thread or need to lock this for synchronization. private final SparseArray<KeyboardBacklightState> mKeyboardBacklights = new SparseArray<>(1); // Maintains state if all backlights should be on or turned off @@ -109,10 +113,17 @@ final class KeyboardBacklightController implements KeyboardBacklightController(Context context, NativeInputManagerService nativeService, PersistentDataStore dataStore, Looper looper) { + this(context, nativeService, dataStore, looper, ValueAnimator::ofInt); + } + + @VisibleForTesting + KeyboardBacklightController(Context context, NativeInputManagerService nativeService, + PersistentDataStore dataStore, Looper looper, AnimatorFactory animatorFactory) { mContext = context; mNative = nativeService; mDataStore = dataStore; mHandler = new Handler(looper, this::handleMessage); + mAnimatorFactory = animatorFactory; } @Override @@ -177,8 +188,7 @@ final class KeyboardBacklightController implements } else { newBrightnessLevel = Math.max(currBrightnessLevel - 1, 0); } - updateBacklightState(deviceId, keyboardBacklight, newBrightnessLevel, - true /* isTriggeredByKeyPress */); + updateBacklightState(deviceId, newBrightnessLevel, true /* isTriggeredByKeyPress */); synchronized (mDataStore) { try { @@ -203,8 +213,7 @@ final class KeyboardBacklightController implements if (index < 0) { index = Math.min(NUM_BRIGHTNESS_CHANGE_STEPS, -(index + 1)); } - updateBacklightState(inputDevice.getId(), keyboardBacklight, index, - false /* isTriggeredByKeyPress */); + updateBacklightState(inputDevice.getId(), index, false /* isTriggeredByKeyPress */); if (DEBUG) { Slog.d(TAG, "Restoring brightness level " + brightness.getAsInt()); } @@ -217,14 +226,10 @@ final class KeyboardBacklightController implements if (!mIsInteractive) { return; } - if (!mIsBacklightOn) { - mIsBacklightOn = true; - for (int i = 0; i < mKeyboardBacklights.size(); i++) { - int deviceId = mKeyboardBacklights.keyAt(i); - KeyboardBacklightState state = mKeyboardBacklights.valueAt(i); - updateBacklightState(deviceId, state.mLight, state.mBrightnessLevel, - false /* isTriggeredByKeyPress */); - } + mIsBacklightOn = true; + for (int i = 0; i < mKeyboardBacklights.size(); i++) { + KeyboardBacklightState state = mKeyboardBacklights.valueAt(i); + state.onBacklightStateChanged(); } mHandler.removeMessages(MSG_NOTIFY_USER_INACTIVITY); mHandler.sendEmptyMessageAtTime(MSG_NOTIFY_USER_INACTIVITY, @@ -232,14 +237,10 @@ final class KeyboardBacklightController implements } private void handleUserInactivity() { - if (mIsBacklightOn) { - mIsBacklightOn = false; - for (int i = 0; i < mKeyboardBacklights.size(); i++) { - int deviceId = mKeyboardBacklights.keyAt(i); - KeyboardBacklightState state = mKeyboardBacklights.valueAt(i); - updateBacklightState(deviceId, state.mLight, state.mBrightnessLevel, - false /* isTriggeredByKeyPress */); - } + mIsBacklightOn = false; + for (int i = 0; i < mKeyboardBacklights.size(); i++) { + KeyboardBacklightState state = mKeyboardBacklights.valueAt(i); + state.onBacklightStateChanged(); } } @@ -310,7 +311,7 @@ final class KeyboardBacklightController implements return; } // The keyboard backlight was added or changed. - mKeyboardBacklights.put(deviceId, new KeyboardBacklightState(keyboardBacklight)); + mKeyboardBacklights.put(deviceId, new KeyboardBacklightState(deviceId, keyboardBacklight)); restoreBacklightBrightness(inputDevice, keyboardBacklight); } @@ -372,21 +373,14 @@ final class KeyboardBacklightController implements } } - private void updateBacklightState(int deviceId, Light light, int brightnessLevel, + private void updateBacklightState(int deviceId, int brightnessLevel, boolean isTriggeredByKeyPress) { KeyboardBacklightState state = mKeyboardBacklights.get(deviceId); if (state == null) { return; } - mNative.setLightColor(deviceId, light.getId(), - mIsBacklightOn ? Color.argb(BRIGHTNESS_VALUE_FOR_LEVEL[brightnessLevel], 0, 0, 0) - : 0); - if (DEBUG) { - Slog.d(TAG, "Changing state from " + state.mBrightnessLevel + " to " + brightnessLevel - + "(isBacklightOn = " + mIsBacklightOn + ")"); - } - state.mBrightnessLevel = brightnessLevel; + state.setBrightnessLevel(brightnessLevel); synchronized (mKeyboardBacklightListenerRecords) { for (int i = 0; i < mKeyboardBacklightListenerRecords.size(); i++) { @@ -397,6 +391,10 @@ final class KeyboardBacklightController implements deviceId, callbackState, isTriggeredByKeyPress); } } + + if (DEBUG) { + Slog.d(TAG, "Changing state from " + state.mBrightnessLevel + " to " + brightnessLevel); + } } private void onKeyboardBacklightListenerDied(int pid) { @@ -436,10 +434,7 @@ final class KeyboardBacklightController implements @Override public void dump(PrintWriter pw) { IndentingPrintWriter ipw = new IndentingPrintWriter(pw); - ipw.println( - TAG + ": " + mKeyboardBacklights.size() + " keyboard backlights, isBacklightOn = " - + mIsBacklightOn); - + ipw.println(TAG + ": " + mKeyboardBacklights.size() + " keyboard backlights"); ipw.increaseIndent(); for (int i = 0; i < mKeyboardBacklights.size(); i++) { KeyboardBacklightState state = mKeyboardBacklights.valueAt(i); @@ -448,6 +443,10 @@ final class KeyboardBacklightController implements ipw.decreaseIndent(); } + private static boolean isAnimationEnabled() { + return InputFeatureFlagProvider.isKeyboardBacklightAnimationEnabled(); + } + // A record of a registered Keyboard backlight listener from one process. private class KeyboardBacklightListenerRecord implements IBinder.DeathRecipient { public final int mPid; @@ -478,14 +477,55 @@ final class KeyboardBacklightController implements } } - private static class KeyboardBacklightState { + private class KeyboardBacklightState { + private final int mDeviceId; private final Light mLight; private int mBrightnessLevel; + private ValueAnimator mAnimator; - KeyboardBacklightState(Light light) { + KeyboardBacklightState(int deviceId, Light light) { + mDeviceId = deviceId; mLight = light; } + private void onBacklightStateChanged() { + setBacklightValue(mIsBacklightOn ? BRIGHTNESS_VALUE_FOR_LEVEL[mBrightnessLevel] : 0); + } + private void setBrightnessLevel(int brightnessLevel) { + if (mIsBacklightOn) { + setBacklightValue(BRIGHTNESS_VALUE_FOR_LEVEL[brightnessLevel]); + } + mBrightnessLevel = brightnessLevel; + } + + private void cancelAnimation() { + if (mAnimator != null && mAnimator.isRunning()) { + mAnimator.cancel(); + } + } + + private void setBacklightValue(int toValue) { + int fromValue = Color.alpha(mNative.getLightColor(mDeviceId, mLight.getId())); + if (fromValue == toValue) { + return; + } + if (isAnimationEnabled()) { + startAnimation(fromValue, toValue); + } else { + mNative.setLightColor(mDeviceId, mLight.getId(), Color.argb(toValue, 0, 0, 0)); + } + } + + private void startAnimation(int fromValue, int toValue) { + // Cancel any ongoing animation before starting a new one + cancelAnimation(); + mAnimator = mAnimatorFactory.makeIntAnimator(fromValue, toValue); + mAnimator.addUpdateListener( + (animation) -> mNative.setLightColor(mDeviceId, mLight.getId(), + Color.argb((int) animation.getAnimatedValue(), 0, 0, 0))); + mAnimator.setDuration(TRANSITION_ANIMATION_DURATION_MILLIS).start(); + } + @Override public String toString() { return "KeyboardBacklightState{Light=" + mLight.getId() @@ -493,4 +533,9 @@ final class KeyboardBacklightController implements + "}"; } } + + @VisibleForTesting + interface AnimatorFactory { + ValueAnimator makeIntAnimator(int from, int to); + } } diff --git a/services/tests/servicestests/src/com/android/server/input/KeyboardBacklightControllerTests.kt b/services/tests/servicestests/src/com/android/server/input/KeyboardBacklightControllerTests.kt index 64c05dc8ab84..272679280a62 100644 --- a/services/tests/servicestests/src/com/android/server/input/KeyboardBacklightControllerTests.kt +++ b/services/tests/servicestests/src/com/android/server/input/KeyboardBacklightControllerTests.kt @@ -16,6 +16,7 @@ package com.android.server.input +import android.animation.ValueAnimator import android.content.Context import android.content.ContextWrapper import android.graphics.Color @@ -29,6 +30,7 @@ import android.os.UEventObserver import android.os.test.TestLooper import android.platform.test.annotations.Presubmit import android.view.InputDevice +import androidx.test.annotation.UiThreadTest import androidx.test.core.app.ApplicationProvider import com.android.server.input.KeyboardBacklightController.BRIGHTNESS_VALUE_FOR_LEVEL import com.android.server.input.KeyboardBacklightController.USER_INACTIVITY_THRESHOLD_MILLIS @@ -96,9 +98,11 @@ class KeyboardBacklightControllerTests { private lateinit var context: Context private lateinit var dataStore: PersistentDataStore private lateinit var testLooper: TestLooper + private val totalLevels = BRIGHTNESS_VALUE_FOR_LEVEL.size private var lightColorMap: HashMap<Int, Int> = HashMap() private var lastBacklightState: KeyboardBacklightState? = null private var sysfsNodeChanges = 0 + private var lastAnimationValues = IntArray(2) @Before fun setup() { @@ -115,8 +119,8 @@ class KeyboardBacklightControllerTests { override fun finishWrite(fos: FileOutputStream?, success: Boolean) {} }) testLooper = TestLooper() - keyboardBacklightController = - KeyboardBacklightController(context, native, dataStore, testLooper.looper) + keyboardBacklightController = KeyboardBacklightController(context, native, dataStore, + testLooper.looper, FakeAnimatorFactory()) InputManagerGlobal.resetInstance(iInputManager) val inputManager = InputManager(context) `when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager) @@ -125,6 +129,10 @@ class KeyboardBacklightControllerTests { val args = it.arguments lightColorMap.put(args[1] as Int, args[2] as Int) } + `when`(native.getLightColor(anyInt(), anyInt())).thenAnswer { + val args = it.arguments + lightColorMap.getOrDefault(args[1] as Int, 0) + } lightColorMap.clear() `when`(native.sysfsNodeChanged(any())).then { sysfsNodeChanges++ @@ -138,271 +146,287 @@ class KeyboardBacklightControllerTests { @Test fun testKeyboardBacklightIncrementDecrement() { - val keyboardWithBacklight = createKeyboard(DEVICE_ID) - val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + BacklightAnimationFlag(false).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - for (level in 1 until BRIGHTNESS_VALUE_FOR_LEVEL.size) { + for (level in 1 until totalLevels) { + incrementKeyboardBacklight(DEVICE_ID) + assertEquals( + "Light value for level $level mismatched", + Color.argb(BRIGHTNESS_VALUE_FOR_LEVEL[level], 0, 0, 0), + lightColorMap[LIGHT_ID] + ) + assertEquals( + "Light value for level $level must be correctly stored in the datastore", + BRIGHTNESS_VALUE_FOR_LEVEL[level], + dataStore.getKeyboardBacklightBrightness( + keyboardWithBacklight.descriptor, + LIGHT_ID + ).asInt + ) + } + + // Increment above max level incrementKeyboardBacklight(DEVICE_ID) assertEquals( - "Light value for level $level mismatched", - Color.argb(BRIGHTNESS_VALUE_FOR_LEVEL[level], 0, 0, 0), + "Light value for max level mismatched", + Color.argb(MAX_BRIGHTNESS, 0, 0, 0), lightColorMap[LIGHT_ID] ) assertEquals( - "Light value for level $level must be correctly stored in the datastore", - BRIGHTNESS_VALUE_FOR_LEVEL[level], + "Light value for max level must be correctly stored in the datastore", + MAX_BRIGHTNESS, dataStore.getKeyboardBacklightBrightness( - keyboardWithBacklight.descriptor, - LIGHT_ID + keyboardWithBacklight.descriptor, + LIGHT_ID ).asInt ) - } - // Increment above max level - incrementKeyboardBacklight(DEVICE_ID) - assertEquals( - "Light value for max level mismatched", - Color.argb(MAX_BRIGHTNESS, 0, 0, 0), - lightColorMap[LIGHT_ID] - ) - assertEquals( - "Light value for max level must be correctly stored in the datastore", - MAX_BRIGHTNESS, - dataStore.getKeyboardBacklightBrightness( - keyboardWithBacklight.descriptor, - LIGHT_ID - ).asInt - ) + for (level in totalLevels - 2 downTo 0) { + decrementKeyboardBacklight(DEVICE_ID) + assertEquals( + "Light value for level $level mismatched", + Color.argb(BRIGHTNESS_VALUE_FOR_LEVEL[level], 0, 0, 0), + lightColorMap[LIGHT_ID] + ) + assertEquals( + "Light value for level $level must be correctly stored in the datastore", + BRIGHTNESS_VALUE_FOR_LEVEL[level], + dataStore.getKeyboardBacklightBrightness( + keyboardWithBacklight.descriptor, + LIGHT_ID + ).asInt + ) + } - for (level in BRIGHTNESS_VALUE_FOR_LEVEL.size - 2 downTo 0) { + // Decrement below min level decrementKeyboardBacklight(DEVICE_ID) assertEquals( - "Light value for level $level mismatched", - Color.argb(BRIGHTNESS_VALUE_FOR_LEVEL[level], 0, 0, 0), + "Light value for min level mismatched", + Color.argb(0, 0, 0, 0), lightColorMap[LIGHT_ID] ) assertEquals( - "Light value for level $level must be correctly stored in the datastore", - BRIGHTNESS_VALUE_FOR_LEVEL[level], + "Light value for min level must be correctly stored in the datastore", + 0, dataStore.getKeyboardBacklightBrightness( - keyboardWithBacklight.descriptor, - LIGHT_ID + keyboardWithBacklight.descriptor, + LIGHT_ID ).asInt ) } - - // Decrement below min level - decrementKeyboardBacklight(DEVICE_ID) - assertEquals( - "Light value for min level mismatched", - Color.argb(0, 0, 0, 0), - lightColorMap[LIGHT_ID] - ) - assertEquals( - "Light value for min level must be correctly stored in the datastore", - 0, - dataStore.getKeyboardBacklightBrightness( - keyboardWithBacklight.descriptor, - LIGHT_ID - ).asInt - ) } @Test fun testKeyboardWithoutBacklight() { - val keyboardWithoutBacklight = createKeyboard(DEVICE_ID) - val keyboardInputLight = createLight(LIGHT_ID, Light.LIGHT_TYPE_INPUT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithoutBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardInputLight)) - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - - incrementKeyboardBacklight(DEVICE_ID) - assertTrue("Non Keyboard backlights should not change", lightColorMap.isEmpty()) + BacklightAnimationFlag(false).use { + val keyboardWithoutBacklight = createKeyboard(DEVICE_ID) + val keyboardInputLight = createLight(LIGHT_ID, Light.LIGHT_TYPE_INPUT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithoutBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardInputLight)) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + + incrementKeyboardBacklight(DEVICE_ID) + assertTrue("Non Keyboard backlights should not change", lightColorMap.isEmpty()) + } } @Test fun testKeyboardWithMultipleLight() { - val keyboardWithBacklight = createKeyboard(DEVICE_ID) - val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - val keyboardInputLight = createLight(SECOND_LIGHT_ID, Light.LIGHT_TYPE_INPUT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn( - listOf( - keyboardBacklight, - keyboardInputLight + BacklightAnimationFlag(false).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + val keyboardInputLight = createLight(SECOND_LIGHT_ID, Light.LIGHT_TYPE_INPUT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn( + listOf( + keyboardBacklight, + keyboardInputLight + ) ) - ) - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - incrementKeyboardBacklight(DEVICE_ID) - assertEquals("Only keyboard backlights should change", 1, lightColorMap.size) - assertNotNull("Keyboard backlight should change", lightColorMap[LIGHT_ID]) - assertNull("Input lights should not change", lightColorMap[SECOND_LIGHT_ID]) + incrementKeyboardBacklight(DEVICE_ID) + assertEquals("Only keyboard backlights should change", 1, lightColorMap.size) + assertNotNull("Keyboard backlight should change", lightColorMap[LIGHT_ID]) + assertNull("Input lights should not change", lightColorMap[SECOND_LIGHT_ID]) + } } @Test fun testRestoreBacklightOnInputDeviceAdded() { - val keyboardWithBacklight = createKeyboard(DEVICE_ID) - val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) - - for (level in 1 until BRIGHTNESS_VALUE_FOR_LEVEL.size) { - dataStore.setKeyboardBacklightBrightness( + BacklightAnimationFlag(false).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + + for (level in 1 until totalLevels) { + dataStore.setKeyboardBacklightBrightness( keyboardWithBacklight.descriptor, LIGHT_ID, BRIGHTNESS_VALUE_FOR_LEVEL[level] - 1 - ) - - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - keyboardBacklightController.notifyUserActivity() - testLooper.dispatchNext() - assertEquals( - "Keyboard backlight level should be restored to the level saved in the data " + - "store", + ) + + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + keyboardBacklightController.notifyUserActivity() + testLooper.dispatchNext() + assertEquals( + "Keyboard backlight level should be restored to the level saved in the " + + "data store", Color.argb(BRIGHTNESS_VALUE_FOR_LEVEL[level], 0, 0, 0), lightColorMap[LIGHT_ID] - ) - keyboardBacklightController.onInputDeviceRemoved(DEVICE_ID) + ) + keyboardBacklightController.onInputDeviceRemoved(DEVICE_ID) + } } } @Test fun testRestoreBacklightOnInputDeviceChanged() { - val keyboardWithBacklight = createKeyboard(DEVICE_ID) - val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - dataStore.setKeyboardBacklightBrightness( - keyboardWithBacklight.descriptor, - LIGHT_ID, - MAX_BRIGHTNESS - ) + BacklightAnimationFlag(false).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + dataStore.setKeyboardBacklightBrightness( + keyboardWithBacklight.descriptor, + LIGHT_ID, + MAX_BRIGHTNESS + ) - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - keyboardBacklightController.notifyUserActivity() - testLooper.dispatchNext() - assertTrue( - "Keyboard backlight should not be changed until its added", - lightColorMap.isEmpty() - ) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + keyboardBacklightController.notifyUserActivity() + testLooper.dispatchNext() + assertTrue( + "Keyboard backlight should not be changed until its added", + lightColorMap.isEmpty() + ) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) - keyboardBacklightController.onInputDeviceChanged(DEVICE_ID) - keyboardBacklightController.notifyUserActivity() - testLooper.dispatchNext() - assertEquals( - "Keyboard backlight level should be restored to the level saved in the data store", - Color.argb(MAX_BRIGHTNESS, 0, 0, 0), - lightColorMap[LIGHT_ID] - ) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + keyboardBacklightController.onInputDeviceChanged(DEVICE_ID) + keyboardBacklightController.notifyUserActivity() + testLooper.dispatchNext() + assertEquals( + "Keyboard backlight level should be restored to the level saved in the data store", + Color.argb(MAX_BRIGHTNESS, 0, 0, 0), + lightColorMap[LIGHT_ID] + ) + } } @Test fun testKeyboardBacklight_registerUnregisterListener() { - val keyboardWithBacklight = createKeyboard(DEVICE_ID) - val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + BacklightAnimationFlag(false).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - // Register backlight listener - val listener = KeyboardBacklightListener() - keyboardBacklightController.registerKeyboardBacklightListener(listener, 0) + // Register backlight listener + val listener = KeyboardBacklightListener() + keyboardBacklightController.registerKeyboardBacklightListener(listener, 0) - lastBacklightState = null - keyboardBacklightController.incrementKeyboardBacklight(DEVICE_ID) - testLooper.dispatchNext() + lastBacklightState = null + keyboardBacklightController.incrementKeyboardBacklight(DEVICE_ID) + testLooper.dispatchNext() - assertEquals( - "Backlight state device Id should be $DEVICE_ID", - DEVICE_ID, - lastBacklightState!!.deviceId - ) - assertEquals( - "Backlight state brightnessLevel should be " + 1, - 1, - lastBacklightState!!.brightnessLevel - ) - assertEquals( - "Backlight state maxBrightnessLevel should be " + (BRIGHTNESS_VALUE_FOR_LEVEL.size - 1), - (BRIGHTNESS_VALUE_FOR_LEVEL.size - 1), - lastBacklightState!!.maxBrightnessLevel - ) - assertEquals( - "Backlight state isTriggeredByKeyPress should be true", - true, - lastBacklightState!!.isTriggeredByKeyPress - ) + assertEquals( + "Backlight state device Id should be $DEVICE_ID", + DEVICE_ID, + lastBacklightState!!.deviceId + ) + assertEquals( + "Backlight state brightnessLevel should be " + 1, + 1, + lastBacklightState!!.brightnessLevel + ) + assertEquals( + "Backlight state maxBrightnessLevel should be " + (totalLevels - 1), + (totalLevels - 1), + lastBacklightState!!.maxBrightnessLevel + ) + assertEquals( + "Backlight state isTriggeredByKeyPress should be true", + true, + lastBacklightState!!.isTriggeredByKeyPress + ) - // Unregister listener - keyboardBacklightController.unregisterKeyboardBacklightListener(listener, 0) + // Unregister listener + keyboardBacklightController.unregisterKeyboardBacklightListener(listener, 0) - lastBacklightState = null - incrementKeyboardBacklight(DEVICE_ID) + lastBacklightState = null + incrementKeyboardBacklight(DEVICE_ID) - assertNull("Listener should not receive any updates", lastBacklightState) + assertNull("Listener should not receive any updates", lastBacklightState) + } } @Test fun testKeyboardBacklight_userActivity() { - val keyboardWithBacklight = createKeyboard(DEVICE_ID) - val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) - dataStore.setKeyboardBacklightBrightness( - keyboardWithBacklight.descriptor, - LIGHT_ID, - MAX_BRIGHTNESS - ) + BacklightAnimationFlag(false).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + dataStore.setKeyboardBacklightBrightness( + keyboardWithBacklight.descriptor, + LIGHT_ID, + MAX_BRIGHTNESS + ) - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - keyboardBacklightController.notifyUserActivity() - testLooper.dispatchNext() - assertEquals( - "Keyboard backlight level should be restored to the level saved in the data store", - Color.argb(MAX_BRIGHTNESS, 0, 0, 0), - lightColorMap[LIGHT_ID] - ) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + keyboardBacklightController.notifyUserActivity() + testLooper.dispatchNext() + assertEquals( + "Keyboard backlight level should be restored to the level saved in the data store", + Color.argb(MAX_BRIGHTNESS, 0, 0, 0), + lightColorMap[LIGHT_ID] + ) - testLooper.moveTimeForward(USER_INACTIVITY_THRESHOLD_MILLIS + 1000) - testLooper.dispatchNext() - assertEquals( - "Keyboard backlight level should be turned off after inactivity", - 0, - lightColorMap[LIGHT_ID] - ) + testLooper.moveTimeForward(USER_INACTIVITY_THRESHOLD_MILLIS + 1000) + testLooper.dispatchNext() + assertEquals( + "Keyboard backlight level should be turned off after inactivity", + 0, + lightColorMap[LIGHT_ID] + ) + } } @Test fun testKeyboardBacklight_displayOnOff() { - val keyboardWithBacklight = createKeyboard(DEVICE_ID) - val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) - dataStore.setKeyboardBacklightBrightness( - keyboardWithBacklight.descriptor, - LIGHT_ID, - MAX_BRIGHTNESS - ) + BacklightAnimationFlag(false).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + dataStore.setKeyboardBacklightBrightness( + keyboardWithBacklight.descriptor, + LIGHT_ID, + MAX_BRIGHTNESS + ) - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - keyboardBacklightController.handleInteractiveStateChange(true /* isDisplayOn */) - assertEquals( - "Keyboard backlight level should be restored to the level saved in the data " + - "store when display turned on", - Color.argb(MAX_BRIGHTNESS, 0, 0, 0), - lightColorMap[LIGHT_ID] - ) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + keyboardBacklightController.handleInteractiveStateChange(true /* isDisplayOn */) + assertEquals( + "Keyboard backlight level should be restored to the level saved in the data " + + "store when display turned on", + Color.argb(MAX_BRIGHTNESS, 0, 0, 0), + lightColorMap[LIGHT_ID] + ) - keyboardBacklightController.handleInteractiveStateChange(false /* isDisplayOn */) - assertEquals( - "Keyboard backlight level should be turned off after display is turned off", - 0, - lightColorMap[LIGHT_ID] - ) + keyboardBacklightController.handleInteractiveStateChange(false /* isDisplayOn */) + assertEquals( + "Keyboard backlight level should be turned off after display is turned off", + 0, + lightColorMap[LIGHT_ID] + ) + } } @Test @@ -463,6 +487,30 @@ class KeyboardBacklightControllerTests { ) } + @Test + @UiThreadTest + fun testKeyboardBacklightAnimation_onChangeLevels() { + BacklightAnimationFlag(true).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + + incrementKeyboardBacklight(DEVICE_ID) + assertEquals( + "Should start animation from level 0", + BRIGHTNESS_VALUE_FOR_LEVEL[0], + lastAnimationValues[0] + ) + assertEquals( + "Should start animation to level 1", + BRIGHTNESS_VALUE_FOR_LEVEL[1], + lastAnimationValues[1] + ) + } + } + inner class KeyboardBacklightListener : IKeyboardBacklightListener.Stub() { override fun onBrightnessChanged( deviceId: Int, @@ -496,4 +544,22 @@ class KeyboardBacklightControllerTests { val maxBrightnessLevel: Int, val isTriggeredByKeyPress: Boolean ) + + private inner class BacklightAnimationFlag constructor(enabled: Boolean) : AutoCloseable { + init { + InputFeatureFlagProvider.setKeyboardBacklightAnimationEnabled(enabled) + } + + override fun close() { + InputFeatureFlagProvider.clearOverrides() + } + } + + private inner class FakeAnimatorFactory : KeyboardBacklightController.AnimatorFactory { + override fun makeIntAnimator(from: Int, to: Int): ValueAnimator { + lastAnimationValues[0] = from + lastAnimationValues[1] = to + return ValueAnimator.ofInt(from, to) + } + } } |