diff --git a/AndroidManifest.xml b/AndroidManifest.xml index cf93f9b12e0..3581d483ac8 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -909,6 +909,46 @@ android:value="true"/> + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/drawable/ic_settings_keyboards.xml b/res/drawable/ic_settings_keyboards.xml new file mode 100644 index 00000000000..7eb5a13c521 --- /dev/null +++ b/res/drawable/ic_settings_keyboards.xml @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/res/drawable/ic_settings_languages.xml b/res/drawable/ic_settings_languages.xml new file mode 100644 index 00000000000..bee0df6c625 --- /dev/null +++ b/res/drawable/ic_settings_languages.xml @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index ea0253a4f55..1c7ba09af50 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4924,6 +4924,10 @@ Languages & input + + Languages + + Keyboard You don\u2019t have permission to change the device language. @@ -4932,6 +4936,8 @@ Tools Keyboard & input methods + + System Languages Languages @@ -5099,6 +5105,10 @@ Choose active input methods Onscreen keyboard settings + + On-screen keyboard, Speech, Tools + + On-screen keyboard, Physical keyboard, Speech, Tools Physical keyboard @@ -8025,8 +8035,10 @@ Default apps - + Languages, gestures, time, backup + + System languages, app languages diff --git a/res/xml/keyboard_settings.xml b/res/xml/keyboard_settings.xml new file mode 100644 index 00000000000..bff11607ce0 --- /dev/null +++ b/res/xml/keyboard_settings.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/xml/language_and_input.xml b/res/xml/language_and_input.xml index 64b5003bcef..03614bce26a 100644 --- a/res/xml/language_and_input.xml +++ b/res/xml/language_and_input.xml @@ -25,7 +25,8 @@ + android:fragment="com.android.settings.localepicker.LocaleListEditor" + settings:controller="com.android.settings.language.PhoneLanguagePreferenceController" /> + + + + + + + + + + \ No newline at end of file diff --git a/res/xml/languages.xml b/res/xml/languages.xml index 0f455407645..ed65df099d0 100644 --- a/res/xml/languages.xml +++ b/res/xml/languages.xml @@ -39,4 +39,4 @@ android:selectable="false" settings:controller="com.android.settings.localepicker.LocaleHelperPreferenceController"/> - + \ No newline at end of file diff --git a/res/xml/system_dashboard_fragment.xml b/res/xml/system_dashboard_fragment.xml index 9228ddd5f5a..3b242035844 100644 --- a/res/xml/system_dashboard_fragment.xml +++ b/res/xml/system_dashboard_fragment.xml @@ -25,7 +25,25 @@ android:title="@string/language_settings" android:icon="@drawable/ic_settings_language" android:order="-260" - android:fragment="com.android.settings.language.LanguageAndInputSettings"/> + android:fragment="com.android.settings.language.LanguageAndInputSettings" + settings:controller="com.android.settings.language.LanguageAndInputPreferenceController"/> + + + + System Settings.LanguageAndInputSettingsActivity.class.getName(), + Settings.LanguageSettingsActivity.class.getName(), + Settings.KeyboardSettingsActivity.class.getName(), Settings.DateTimeSettingsActivity.class.getName(), Settings.EnterprisePrivacySettingsActivity.class.getName(), Settings.MyDeviceInfoActivity.class.getName(), diff --git a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java index c2b51987adf..a2b1454a2c8 100644 --- a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java +++ b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java @@ -101,6 +101,7 @@ public class DashboardFragmentRegistry { SystemDashboardFragment.class.getName(), CategoryKey.CATEGORY_SYSTEM); PARENT_TO_CATEGORY_KEY_MAP.put(LanguageAndInputSettings.class.getName(), CategoryKey.CATEGORY_SYSTEM_LANGUAGE); + // TODO(b/242680328) Tie new category key to LanguageSettings and KeyboardSettings page PARENT_TO_CATEGORY_KEY_MAP.put(DevelopmentSettingsDashboardFragment.class.getName(), CategoryKey.CATEGORY_SYSTEM_DEVELOPMENT); PARENT_TO_CATEGORY_KEY_MAP.put(ConfigureNotificationSettings.class.getName(), diff --git a/src/com/android/settings/inputmethod/KeyboardPreferenceController.java b/src/com/android/settings/inputmethod/KeyboardPreferenceController.java new file mode 100644 index 00000000000..75351b13d25 --- /dev/null +++ b/src/com/android/settings/inputmethod/KeyboardPreferenceController.java @@ -0,0 +1,95 @@ +/* + * 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.settings.inputmethod; + +import android.content.Context; +import android.hardware.input.InputManager; +import android.util.FeatureFlagUtils; + +import androidx.preference.Preference; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.inputmethod.PhysicalKeyboardFragment.HardKeyboardDeviceInfo; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnPause; +import com.android.settingslib.core.lifecycle.events.OnResume; + +import java.util.List; + +public class KeyboardPreferenceController extends BasePreferenceController + implements PreferenceControllerMixin, LifecycleObserver, OnResume, OnPause, + InputManager.InputDeviceListener { + + private final InputManager mIm; + + private Preference mPreference; + + public KeyboardPreferenceController(Context context, String key) { + super(context, key); + mIm = context.getSystemService(InputManager.class); + } + + @Override + public void onInputDeviceAdded(int deviceId) { + updateSummary(); + } + + @Override + public void onInputDeviceRemoved(int deviceId) { + updateSummary(); + } + + @Override + public void onInputDeviceChanged(int deviceId) { + updateSummary(); + } + + @Override + public void onPause() { + mIm.unregisterInputDeviceListener(this); + } + + @Override + public void onResume() { + mIm.registerInputDeviceListener(this, null); + } + + @Override + public void updateState(Preference preference) { + mPreference = preference; + updateSummary(); + } + + @Override + public int getAvailabilityStatus() { + boolean isFeatureOn = FeatureFlagUtils + .isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI); + return isFeatureOn ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; + } + + private void updateSummary() { + final List keyboards = + PhysicalKeyboardFragment.getHardKeyboards(mContext); + if (keyboards.isEmpty()) { + mPreference.setSummary(R.string.keyboard_settings_summary); + } else { + mPreference.setSummary(R.string.keyboard_settings_with_physical_keyboard_summary); + } + } +} \ No newline at end of file diff --git a/src/com/android/settings/inputmethod/KeyboardSettings.java b/src/com/android/settings/inputmethod/KeyboardSettings.java new file mode 100644 index 00000000000..2d6ac885cd6 --- /dev/null +++ b/src/com/android/settings/inputmethod/KeyboardSettings.java @@ -0,0 +1,136 @@ +/* + * 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.settings.inputmethod; + +import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_DICTIONARY_FOR_WORK; +import static android.app.admin.DevicePolicyResources.Strings.Settings.SPELL_CHECKER_FOR_WORK; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_KEYBOARDS_AND_TOOLS; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.os.Bundle; +import android.util.FeatureFlagUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.language.DefaultVoiceInputPreferenceController; +import com.android.settings.language.PointerSpeedController; +import com.android.settings.language.TtsPreferenceController; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.widget.PreferenceCategoryController; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.search.SearchIndexable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@SearchIndexable +public class KeyboardSettings extends DashboardFragment { + + private static final String TAG = "KeyboardSettings"; + + private static final String KEY_KEYBOARDS_CATEGORY = "keyboards_category"; + private static final String KEY_SPEECH_CATEGORY = "speech_category"; + private static final String KEY_TEXT_TO_SPEECH = "tts_settings_summary"; + private static final String KEY_POINTER_CATEGORY = "pointer_category"; + + @Override + public int getMetricsCategory() { + return SettingsEnums.SETTINGS_KEYBOARDS_CATEGORY; + } + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + replaceEnterpriseStringTitle("language_and_input_for_work_category", + WORK_PROFILE_KEYBOARDS_AND_TOOLS, + R.string.language_and_input_for_work_category_title); + replaceEnterpriseStringTitle("spellcheckers_settings_for_work_pref", + SPELL_CHECKER_FOR_WORK, + R.string.spellcheckers_settings_for_work_title); + replaceEnterpriseStringTitle("user_dictionary_settings_for_work_pref", + PERSONAL_DICTIONARY_FOR_WORK, + R.string.user_dict_settings_for_work_title); + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.keyboard_settings; + } + + @Override + protected List createPreferenceControllers(Context context) { + return buildPreferenceControllers(context, getSettingsLifecycle()); + } + + private static List buildPreferenceControllers( + @NonNull Context context, @Nullable Lifecycle lifecycle) { + final List controllers = new ArrayList<>(); + // Input + final VirtualKeyboardPreferenceController virtualKeyboardPreferenceController = + new VirtualKeyboardPreferenceController(context); + final PhysicalKeyboardPreferenceController physicalKeyboardPreferenceController = + new PhysicalKeyboardPreferenceController(context, lifecycle); + controllers.add(virtualKeyboardPreferenceController); + controllers.add(physicalKeyboardPreferenceController); + controllers.add(new PreferenceCategoryController(context, + KEY_KEYBOARDS_CATEGORY).setChildren( + Arrays.asList(virtualKeyboardPreferenceController, + physicalKeyboardPreferenceController))); + + // Speech + final DefaultVoiceInputPreferenceController defaultVoiceInputPreferenceController = + new DefaultVoiceInputPreferenceController(context, lifecycle); + final TtsPreferenceController ttsPreferenceController = + new TtsPreferenceController(context, KEY_TEXT_TO_SPEECH); + controllers.add(defaultVoiceInputPreferenceController); + controllers.add(ttsPreferenceController); + controllers.add(new PreferenceCategoryController(context, + KEY_SPEECH_CATEGORY).setChildren( + Arrays.asList(defaultVoiceInputPreferenceController, ttsPreferenceController))); + + // Pointer + final PointerSpeedController pointerController = new PointerSpeedController(context); + controllers.add(pointerController); + controllers.add(new PreferenceCategoryController(context, + KEY_POINTER_CATEGORY).setChildren(Arrays.asList(pointerController))); + + // Input Assistance + controllers.add(new SpellCheckerPreferenceController(context)); + + return controllers; + } + + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider(R.xml.keyboard_settings) { + @Override + protected boolean isPageSearchEnabled(Context context) { + return FeatureFlagUtils + .isEnabled(context, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI); + } + }; +} \ No newline at end of file diff --git a/src/com/android/settings/language/LanguageAndInputPreferenceController.java b/src/com/android/settings/language/LanguageAndInputPreferenceController.java index 04bf6227fb8..691d9076ae4 100644 --- a/src/com/android/settings/language/LanguageAndInputPreferenceController.java +++ b/src/com/android/settings/language/LanguageAndInputPreferenceController.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.provider.Settings; import android.text.TextUtils; +import android.util.FeatureFlagUtils; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; @@ -41,7 +42,9 @@ public class LanguageAndInputPreferenceController extends BasePreferenceControll @Override public int getAvailabilityStatus() { - return AVAILABLE; + boolean isFeatureOn = FeatureFlagUtils + .isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI); + return isFeatureOn ? CONDITIONALLY_UNAVAILABLE : AVAILABLE; } @Override diff --git a/src/com/android/settings/language/LanguageAndInputSettings.java b/src/com/android/settings/language/LanguageAndInputSettings.java index 2d80da59447..f40473c9565 100644 --- a/src/com/android/settings/language/LanguageAndInputSettings.java +++ b/src/com/android/settings/language/LanguageAndInputSettings.java @@ -24,6 +24,7 @@ import android.app.Activity; import android.app.settings.SettingsEnums; import android.content.Context; import android.os.Bundle; +import android.util.FeatureFlagUtils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -104,8 +105,6 @@ public class LanguageAndInputSettings extends DashboardFragment { private static List buildPreferenceControllers( @NonNull Context context, @Nullable Lifecycle lifecycle) { final List controllers = new ArrayList<>(); - // Language - controllers.add(new PhoneLanguagePreferenceController(context)); // Input final VirtualKeyboardPreferenceController virtualKeyboardPreferenceController = @@ -154,11 +153,16 @@ public class LanguageAndInputSettings extends DashboardFragment { public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider(R.xml.language_and_input) { - @Override public List createPreferenceControllers( Context context) { return buildPreferenceControllers(context, null); } + + @Override + protected boolean isPageSearchEnabled(Context context) { + return !FeatureFlagUtils + .isEnabled(context, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI); + } }; } diff --git a/src/com/android/settings/language/LanguagePreferenceController.java b/src/com/android/settings/language/LanguagePreferenceController.java new file mode 100644 index 00000000000..08033cd0863 --- /dev/null +++ b/src/com/android/settings/language/LanguagePreferenceController.java @@ -0,0 +1,36 @@ +/* + * 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.settings.language; + +import android.content.Context; +import android.util.FeatureFlagUtils; + +import com.android.settings.core.BasePreferenceController; + +public class LanguagePreferenceController extends BasePreferenceController { + + public LanguagePreferenceController(Context context, String key) { + super(context, key); + } + + @Override + public int getAvailabilityStatus() { + boolean isFeatureOn = FeatureFlagUtils + .isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI); + return isFeatureOn ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; + } +} \ No newline at end of file diff --git a/src/com/android/settings/language/LanguageSettings.java b/src/com/android/settings/language/LanguageSettings.java new file mode 100644 index 00000000000..f814e36f275 --- /dev/null +++ b/src/com/android/settings/language/LanguageSettings.java @@ -0,0 +1,70 @@ +/* + * 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.settings.language; + +import android.app.Activity; +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.util.FeatureFlagUtils; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settingslib.search.SearchIndexable; + +@SearchIndexable +public class LanguageSettings extends DashboardFragment { + + private static final String TAG = "LanguageSettings"; + + @Override + public int getMetricsCategory() { + return SettingsEnums.SETTINGS_LANGUAGES_CATEGORY; + } + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + public void onResume() { + super.onResume(); + // Hack to update action bar title. It's necessary to refresh title because this page user + // can change locale from here and fragment won't relaunch. Once language changes, title + // must display in the new language. + final Activity activity = getActivity(); + if (activity == null) { + return; + } + activity.setTitle(R.string.languages_settings); + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.language_settings; + } + + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider(R.xml.language_settings) { + @Override + protected boolean isPageSearchEnabled(Context context) { + return FeatureFlagUtils + .isEnabled(context, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI); + } + }; +} \ No newline at end of file diff --git a/src/com/android/settings/language/PhoneLanguagePreferenceController.java b/src/com/android/settings/language/PhoneLanguagePreferenceController.java index 4f67a8ff6eb..0d5aa379ac7 100644 --- a/src/com/android/settings/language/PhoneLanguagePreferenceController.java +++ b/src/com/android/settings/language/PhoneLanguagePreferenceController.java @@ -16,33 +16,32 @@ package com.android.settings.language; -import android.app.settings.SettingsEnums; import android.content.Context; import androidx.preference.Preference; import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; import com.android.settings.core.PreferenceControllerMixin; -import com.android.settings.core.SubSettingLauncher; -import com.android.settings.localepicker.LocaleListEditor; import com.android.settings.overlay.FeatureFactory; -import com.android.settingslib.core.AbstractPreferenceController; import java.util.List; -public class PhoneLanguagePreferenceController extends AbstractPreferenceController +public class PhoneLanguagePreferenceController extends BasePreferenceController implements PreferenceControllerMixin { - private static final String KEY_PHONE_LANGUAGE = "phone_language"; - - public PhoneLanguagePreferenceController(Context context) { - super(context); + public PhoneLanguagePreferenceController(Context context, String key) { + super(context, key); } @Override - public boolean isAvailable() { - return mContext.getResources().getBoolean(R.bool.config_show_phone_language) - && mContext.getAssets().getLocales().length > 1; + public int getAvailabilityStatus() { + if (mContext.getResources().getBoolean(R.bool.config_show_phone_language) + && mContext.getAssets().getLocales().length > 1) { + return AVAILABLE; + } else { + return CONDITIONALLY_UNAVAILABLE; + } } @Override @@ -61,23 +60,4 @@ public class PhoneLanguagePreferenceController extends AbstractPreferenceControl // make search page look like there are duplicate result, creating confusion. keys.add(getPreferenceKey()); } - - @Override - public String getPreferenceKey() { - return KEY_PHONE_LANGUAGE; - } - - @Override - public boolean handlePreferenceTreeClick(Preference preference) { - if (!KEY_PHONE_LANGUAGE.equals(preference.getKey())) { - return false; - } - new SubSettingLauncher(mContext) - .setDestination(LocaleListEditor.class.getName()) - .setSourceMetricsCategory(SettingsEnums.SETTINGS_LANGUAGE_CATEGORY) - .setTitleRes(R.string.language_picker_title) - .launch(); - return true; - } - } diff --git a/tests/robotests/src/com/android/settings/language/PhoneLanguagePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/language/PhoneLanguagePreferenceControllerTest.java index b67cc5dbc1c..aa4c32ee28c 100644 --- a/tests/robotests/src/com/android/settings/language/PhoneLanguagePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/language/PhoneLanguagePreferenceControllerTest.java @@ -27,6 +27,7 @@ import android.content.res.AssetManager; import androidx.preference.Preference; +import com.android.settings.core.BasePreferenceController; import com.android.settings.testutils.FakeFeatureFactory; import org.junit.Before; @@ -59,7 +60,7 @@ public class PhoneLanguagePreferenceControllerTest { mContext = spy(RuntimeEnvironment.application); when(mContext.getAssets()).thenReturn(mAssets); mFeatureFactory = FakeFeatureFactory.setupForTest(); - mController = new PhoneLanguagePreferenceController(mContext); + mController = new PhoneLanguagePreferenceController(mContext, "key"); } @Test @@ -76,6 +77,22 @@ public class PhoneLanguagePreferenceControllerTest { assertThat(mController.isAvailable()).isFalse(); } + @Test + public void testGetAvailabilityStatus_hasMultipleLocales_returnAvailable() { + when(mAssets.getLocales()).thenReturn(new String[] {"en", "de"}); + + assertThat(mController.getAvailabilityStatus()) + .isEqualTo(BasePreferenceController.AVAILABLE); + } + + @Test + public void testGetAvailabilityStatus_hasSingleLocales_returnConditionallyUnavailable() { + when(mAssets.getLocales()).thenReturn(new String[] {"en"}); + + assertThat(mController.getAvailabilityStatus()) + .isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE); + } + @Test @Config(qualifiers = "mcc999") public void testIsAvailable_ifDisabled_shouldReturnFalse() {