diff options
| author | 2022-02-01 17:21:51 +0000 | |
|---|---|---|
| committer | 2022-02-01 17:21:51 +0000 | |
| commit | 6c966b35eaf996590ac7bbff08523dba24b70c73 (patch) | |
| tree | 619a9a086ac43d2cc22e87f56a5166381a041f0f | |
| parent | d00a8b52ee2e899f2af2cc9613d5203399875fe9 (diff) | |
| parent | 6f1aecf1bfb99806855e356f42b42de5d9aa78f0 (diff) | |
Merge "Add system prompt for system language change after <Set Menu Language> CEC message"
13 files changed, 380 insertions, 1 deletions
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index 5874385f8191..ec55e121bf74 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -105,6 +105,12 @@ public final class HdmiControlManager { "android.hardware.hdmi.extra.MESSAGE_EXTRA_PARAM1"; /** + * Used as an extra field in the Set Menu Language intent. Contains the requested locale. + * @hide + */ + public static final String EXTRA_LOCALE = "android.hardware.hdmi.extra.LOCALE"; + + /** * Volume value for mute state. */ public static final int AVR_VOLUME_MUTED = 101; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4e9aec751bdb..c4fe1a44d161 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -10665,6 +10665,15 @@ public final class Settings { public static final String FAST_PAIR_SCAN_ENABLED = "fast_pair_scan_enabled"; /** + * Setting to store denylisted system languages by the CEC {@code <Set Menu Language>} + * confirmation dialog. + * + * @hide + */ + public static final String HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST = + "hdmi_cec_set_menu_language_denylist"; + + /** * These entries are considered common between the personal and the managed profile, * since the managed profile doesn't get to change them. */ diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 73568c1deaab..a06b2cb28037 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2891,6 +2891,11 @@ <string name="config_sensorUseStartedActivity" translatable="false" >com.android.systemui/com.android.systemui.sensorprivacy.SensorUseStartedActivity</string> + <!-- Component name of the activity used to ask a user to confirm system language change after + receiving <Set Menu Language> CEC message. --> + <string name="config_hdmiCecSetMenuLanguageActivity" + >com.android.systemui/com.android.systemui.hdmi.HdmiCecSetMenuLanguageActivity</string> + <!-- Name of the dialog that is used to request the user's consent for a Platform VPN --> <string name="config_platformVpnConfirmDialogComponent" translatable="false" >com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 94043ed6d8ed..4c1cc4dc8f70 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -375,6 +375,7 @@ <java-symbol type="string" name="config_usbConfirmActivity" /> <java-symbol type="string" name="config_usbResolverActivity" /> <java-symbol type="string" name="config_sensorUseStartedActivity" /> + <java-symbol type="string" name="config_hdmiCecSetMenuLanguageActivity" /> <java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" /> <java-symbol type="integer" name="config_maxNumVisibleRecentTasks_lowRam" /> <java-symbol type="integer" name="config_minNumVisibleRecentTasks_grid" /> diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml index d95644a02e69..ae350ec547c3 100644 --- a/data/etc/com.android.systemui.xml +++ b/data/etc/com.android.systemui.xml @@ -74,5 +74,6 @@ <permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" /> <permission name="android.permission.FORCE_STOP_PACKAGES" /> <permission name="android.permission.ACCESS_FPS_COUNTER" /> + <permission name="android.permission.CHANGE_CONFIGURATION" /> </privapp-permissions> </permissions> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 2e6a58f7f5da..55bf6e531fa2 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -303,6 +303,9 @@ <uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" /> <uses-permission android:name="android.permission.SET_CLIP_SOURCE" /> + <!-- To change system language (HDMI CEC) --> + <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> + <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" /> @@ -486,6 +489,16 @@ android:excludeFromRecents="true"> </activity> + <!-- started from HdmiCecLocalDevicePlayback --> + <activity android:name=".hdmi.HdmiCecSetMenuLanguageActivity" + android:exported="true" + android:launchMode="singleTop" + android:permission="android.permission.CHANGE_CONFIGURATION" + android:theme="@style/BottomSheet" + android:finishOnCloseSystemDialogs="true" + android:excludeFromRecents="true"> + </activity> + <!-- started from SensoryPrivacyService --> <activity android:name=".sensorprivacy.SensorUseStartedActivity" android:exported="true" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 75ae52c6e6f4..4dca0b0076d9 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -123,6 +123,18 @@ <!-- Message of notification shown when trying to enable USB debugging but a secondary user is the current foreground user. --> <string name="usb_debugging_secondary_user_message">The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user.</string> + <!-- Title of confirmation dialog for wireless debugging [CHAR LIMIT=80] --> + <string name="hdmi_cec_set_menu_language_title">Do you want to change the system language to <xliff:g id="language" example="German">%1$s</xliff:g>?</string> + + <!-- Description for the <Set Menu Language> confirmation dialog [CHAR LIMIT=NONE] --> + <string name="hdmi_cec_set_menu_language_description">System language change requested by another device</string> + + <!-- Button label for accepting language change [CHAR LIMIT=25] --> + <string name="hdmi_cec_set_menu_language_accept">Change language</string> + + <!-- Button label for declining language change [CHAR LIMIT=25] --> + <string name="hdmi_cec_set_menu_language_decline">Keep current language</string> + <!-- Title of confirmation dialog for wireless debugging [CHAR LIMIT=NONE] --> <string name="wifi_debugging_title">Allow wireless debugging on this network?</string> diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 08ed24caec44..b32c2b639f16 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -49,6 +49,7 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.fragments.FragmentService; +import com.android.systemui.hdmi.HdmiCecSetMenuLanguageHelper; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.media.dialog.MediaOutputDialogFactory; @@ -250,6 +251,7 @@ public class Dependency { @Inject Lazy<LocationController> mLocationController; @Inject Lazy<RotationLockController> mRotationLockController; @Inject Lazy<ZenModeController> mZenModeController; + @Inject Lazy<HdmiCecSetMenuLanguageHelper> mHdmiCecSetMenuLanguageHelper; @Inject Lazy<HotspotController> mHotspotController; @Inject Lazy<CastController> mCastController; @Inject Lazy<FlashlightController> mFlashlightController; diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java index bce878434aa1..1653e0adadb5 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java @@ -19,6 +19,7 @@ package com.android.systemui.dagger; import android.app.Activity; import com.android.systemui.ForegroundServicesDialog; +import com.android.systemui.hdmi.HdmiCecSetMenuLanguageActivity; import com.android.systemui.keyguard.WorkLockActivity; import com.android.systemui.people.PeopleSpaceActivity; import com.android.systemui.people.widget.LaunchConversationActivity; @@ -120,4 +121,11 @@ public abstract class DefaultActivityBinder { @IntoMap @ClassKey(TvUnblockSensorActivity.class) public abstract Activity bindTvUnblockSensorActivity(TvUnblockSensorActivity activity); + + /** Inject into HdmiCecSetMenuLanguageActivity. */ + @Binds + @IntoMap + @ClassKey(HdmiCecSetMenuLanguageActivity.class) + public abstract Activity bindHdmiCecSetMenuLanguageActivity( + HdmiCecSetMenuLanguageActivity activity); } diff --git a/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageActivity.java b/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageActivity.java new file mode 100644 index 000000000000..b304c3ca737a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageActivity.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.hdmi; + +import android.hardware.hdmi.HdmiControlManager; +import android.os.Bundle; +import android.view.View; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.systemui.R; +import com.android.systemui.tv.TvBottomSheetActivity; + +import javax.inject.Inject; + +/** + * Confirmation dialog shown when Set Menu Language CEC message was received. + */ +public class HdmiCecSetMenuLanguageActivity extends TvBottomSheetActivity + implements View.OnClickListener { + private static final String TAG = HdmiCecSetMenuLanguageActivity.class.getSimpleName(); + + private final HdmiCecSetMenuLanguageHelper mHdmiCecSetMenuLanguageHelper; + + @Inject + public HdmiCecSetMenuLanguageActivity( + HdmiCecSetMenuLanguageHelper hdmiCecSetMenuLanguageHelper) { + mHdmiCecSetMenuLanguageHelper = hdmiCecSetMenuLanguageHelper; + } + + @Override + public final void onCreate(Bundle b) { + super.onCreate(b); + getWindow().addPrivateFlags( + WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); + String languageTag = getIntent().getStringExtra(HdmiControlManager.EXTRA_LOCALE); + mHdmiCecSetMenuLanguageHelper.setLocale(languageTag); + if (mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()) { + finish(); + } + } + + @Override + public void onResume() { + super.onResume(); + CharSequence title = getString(R.string.hdmi_cec_set_menu_language_title, + mHdmiCecSetMenuLanguageHelper.getLocale().getDisplayLanguage()); + CharSequence text = getString(R.string.hdmi_cec_set_menu_language_description); + initUI(title, text); + } + + @Override + public void onClick(View v) { + if (v.getId() == R.id.bottom_sheet_positive_button) { + mHdmiCecSetMenuLanguageHelper.acceptLocale(); + } else { + mHdmiCecSetMenuLanguageHelper.declineLocale(); + } + finish(); + } + + void initUI(CharSequence title, CharSequence text) { + TextView titleTextView = findViewById(R.id.bottom_sheet_title); + TextView contentTextView = findViewById(R.id.bottom_sheet_body); + ImageView icon = findViewById(R.id.bottom_sheet_icon); + ImageView secondIcon = findViewById(R.id.bottom_sheet_second_icon); + Button okButton = findViewById(R.id.bottom_sheet_positive_button); + Button cancelButton = findViewById(R.id.bottom_sheet_negative_button); + + titleTextView.setText(title); + contentTextView.setText(text); + icon.setImageResource(com.android.internal.R.drawable.ic_settings_language); + secondIcon.setVisibility(View.GONE); + + okButton.setText(R.string.hdmi_cec_set_menu_language_accept); + okButton.setOnClickListener(this); + + cancelButton.setText(R.string.hdmi_cec_set_menu_language_decline); + cancelButton.setOnClickListener(this); + cancelButton.requestFocus(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelper.java b/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelper.java new file mode 100644 index 000000000000..1f58112c5dc6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelper.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.hdmi; + +import android.provider.Settings; + +import com.android.internal.app.LocalePicker; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.util.settings.SecureSettings; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Locale; +import java.util.concurrent.Executor; + +import javax.inject.Inject; + +/** + * Helper class to separate model and view for system language change initiated by HDMI CEC. + */ +@SysUISingleton +public class HdmiCecSetMenuLanguageHelper { + private static final String TAG = HdmiCecSetMenuLanguageHelper.class.getSimpleName(); + private static final String SEPARATOR = ","; + + private final Executor mBackgroundExecutor; + private final SecureSettings mSecureSettings; + + private Locale mLocale; + private HashSet<String> mDenylist; + + @Inject + public HdmiCecSetMenuLanguageHelper(@Background Executor executor, + SecureSettings secureSettings) { + mBackgroundExecutor = executor; + mSecureSettings = secureSettings; + String denylist = mSecureSettings.getString( + Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST); + mDenylist = new HashSet<>(denylist == null + ? Collections.EMPTY_SET + : Arrays.asList(denylist.split(SEPARATOR))); + } + + /** + * Set internal locale based on given language tag. + */ + public void setLocale(String languageTag) { + mLocale = Locale.forLanguageTag(languageTag); + } + + /** + * Returns the locale from {@code <Set Menu Language>} CEC message. + */ + public Locale getLocale() { + return mLocale; + } + + /** + * Returns whether the locale from {@code <Set Menu Language>} CEC message was already + * denylisted. + */ + public boolean isLocaleDenylisted() { + return mDenylist.contains(mLocale.toLanguageTag()); + } + + /** + * Accepts the new locale and updates system language. + */ + public void acceptLocale() { + mBackgroundExecutor.execute(() -> LocalePicker.updateLocale(mLocale)); + } + + /** + * Declines the locale and puts it on the denylist. + */ + public void declineLocale() { + mDenylist.add(mLocale.toLanguageTag()); + mSecureSettings.putString(Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, + String.join(SEPARATOR, mDenylist)); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelperTest.java new file mode 100644 index 000000000000..1171bd299c1f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelperTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.hdmi; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.provider.Settings; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.settings.SecureSettings; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Locale; +import java.util.concurrent.Executor; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class HdmiCecSetMenuLanguageHelperTest extends SysuiTestCase { + + private HdmiCecSetMenuLanguageHelper mHdmiCecSetMenuLanguageHelper; + + @Mock + private Executor mExecutor; + + @Mock + private SecureSettings mSecureSettings; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mSecureSettings.getString( + Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST)).thenReturn(null); + mHdmiCecSetMenuLanguageHelper = + new HdmiCecSetMenuLanguageHelper(mExecutor, mSecureSettings); + } + + @Test + public void testSetGetLocale() { + mHdmiCecSetMenuLanguageHelper.setLocale("en"); + assertThat(mHdmiCecSetMenuLanguageHelper.getLocale()).isEqualTo(Locale.ENGLISH); + } + + @Test + public void testIsLocaleDenylisted_EmptyByDefault() { + mHdmiCecSetMenuLanguageHelper.setLocale("en"); + assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(false); + } + + @Test + public void testIsLocaleDenylisted_AcceptLanguage() { + mHdmiCecSetMenuLanguageHelper.setLocale("de"); + mHdmiCecSetMenuLanguageHelper.acceptLocale(); + assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(false); + verify(mExecutor).execute(any()); + } + + @Test + public void testIsLocaleDenylisted_DeclineLanguage() { + mHdmiCecSetMenuLanguageHelper.setLocale("de"); + mHdmiCecSetMenuLanguageHelper.declineLocale(); + assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(true); + verify(mSecureSettings).putString( + Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, "de"); + } + + @Test + public void testIsLocaleDenylisted_DeclineTwoLanguages() { + mHdmiCecSetMenuLanguageHelper.setLocale("de"); + mHdmiCecSetMenuLanguageHelper.declineLocale(); + assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(true); + verify(mSecureSettings).putString( + Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, "de"); + mHdmiCecSetMenuLanguageHelper.setLocale("pl"); + mHdmiCecSetMenuLanguageHelper.declineLocale(); + assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(true); + verify(mSecureSettings).putString( + Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, "de,pl"); + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index b23395f36e63..f413fbd5c9b2 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -17,10 +17,15 @@ package com.android.server.hdmi; import android.annotation.CallSuper; +import android.content.ActivityNotFoundException; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.tv.cec.V1_0.SendMessageResult; +import android.os.Binder; import android.os.Handler; import android.os.PowerManager; import android.os.PowerManager.WakeLock; @@ -408,7 +413,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { // locale from being chosen. 'eng' in the CEC command, for instance, // will always be mapped to en-AU among other variants like en-US, en-GB, // an en-IN, which may not be the expected one. - LocalePicker.updateLocale(localeInfo.getLocale()); + startSetMenuLanguageActivity(localeInfo.getLocale()); return Constants.HANDLED; } } @@ -420,6 +425,24 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { } } + private void startSetMenuLanguageActivity(Locale locale) { + final long identity = Binder.clearCallingIdentity(); + try { + Context context = mService.getContext(); + Intent intent = new Intent(); + intent.putExtra(HdmiControlManager.EXTRA_LOCALE, locale.toLanguageTag()); + intent.setComponent( + ComponentName.unflattenFromString(context.getResources().getString( + com.android.internal.R.string.config_hdmiCecSetMenuLanguageActivity))); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivityAsUser(intent, context.getUser()); + } catch (ActivityNotFoundException e) { + Slog.e(TAG, "unable to start HdmiCecSetMenuLanguageActivity"); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + @Override @Constants.HandleMessageResult protected int handleSetSystemAudioMode(HdmiCecMessage message) { |