1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tv.settings.inputmethod; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.Context; 22 import android.hardware.input.InputDeviceIdentifier; 23 import android.hardware.input.InputManager; 24 import android.hardware.input.KeyboardLayout; 25 import android.os.Bundle; 26 27 import androidx.preference.Preference; 28 import androidx.preference.PreferenceScreen; 29 30 import com.android.tv.settings.RadioPreference; 31 import com.android.tv.settings.SettingsPreferenceFragment; 32 import com.android.tv.twopanelsettings.TwoPanelSettingsFragment; 33 import com.android.tv.twopanelsettings.slices.SliceFragment; 34 35 import java.util.Arrays; 36 import java.util.HashMap; 37 import java.util.Map; 38 import java.util.Objects; 39 40 /** 41 * Presents the user a list of all available keyboard layouts / languages and switches to 42 * the selected layout. 43 */ 44 public class KeyboardLayoutSelectionFragment extends SettingsPreferenceFragment implements 45 InputManager.InputDeviceListener { 46 private static final String LAYOUT_RADIO_GROUP = "layout"; 47 private static final String ARG_DEVICE_NAME = "deviceName"; 48 private static final String ARG_LAYOUT_IDENTIFIER = "layoutIdentifier"; 49 private static final String ARG_DEVICE_ID = "deviceId"; 50 private static final String ARG_INPUT_DEVICE_IDENTIFIER = "inputDeviceIdentifier"; 51 52 private InputDeviceIdentifier mInputDeviceIdentifier; 53 private int mDeviceId; 54 private InputManager mIm; 55 private final Map<String, KeyboardLayout> mKeyboardLayoutMap = new HashMap<>(); 56 57 /** 58 * Prepares the args with the provided parameters. 59 */ prepareArgs(@onNull Bundle args, @NonNull InputDeviceIdentifier inputDeviceIdentifier, @NonNull String deviceName, int deviceId, @Nullable String currentLayoutIdentifier)60 public static void prepareArgs(@NonNull Bundle args, 61 @NonNull InputDeviceIdentifier inputDeviceIdentifier, 62 @NonNull String deviceName, int deviceId, @Nullable String currentLayoutIdentifier) { 63 args.putObject(ARG_INPUT_DEVICE_IDENTIFIER, inputDeviceIdentifier); 64 args.putString(ARG_DEVICE_NAME, deviceName); 65 args.putInt(ARG_DEVICE_ID, deviceId); 66 if (currentLayoutIdentifier != null) { 67 args.putString(ARG_LAYOUT_IDENTIFIER, currentLayoutIdentifier); 68 } 69 } 70 71 @Override onCreatePreferences(Bundle bundle, String s)72 public void onCreatePreferences(Bundle bundle, String s) { 73 final Context themedContext = getPreferenceManager().getContext(); 74 mInputDeviceIdentifier = getArguments().getParcelable(ARG_INPUT_DEVICE_IDENTIFIER); 75 String mDeviceName = getArguments().getString(ARG_DEVICE_NAME); 76 mDeviceId = getArguments().getInt(ARG_DEVICE_ID); 77 String currentLayoutIdentifier = getArguments().getString(ARG_LAYOUT_IDENTIFIER); 78 final PreferenceScreen screen = 79 getPreferenceManager().createPreferenceScreen(themedContext); 80 screen.setTitle(themedContext.getString( 81 com.android.settingslib.R.string.keyboard_layout_dialog_title)); 82 screen.setSummary(mDeviceName); 83 mIm = Objects.requireNonNull(themedContext.getSystemService(InputManager.class)); 84 mIm.registerInputDeviceListener(this, null); 85 86 KeyboardLayout[] keyboardLayouts = mIm.getKeyboardLayoutsForInputDevice( 87 mInputDeviceIdentifier); 88 Arrays.sort(keyboardLayouts); 89 RadioPreference activePreference = null; 90 for (KeyboardLayout kl : keyboardLayouts) { 91 final RadioPreference radioPreference = new RadioPreference(themedContext); 92 radioPreference.setKey(kl.getDescriptor()); 93 radioPreference.setPersistent(false); 94 radioPreference.setTitle(kl.getLabel()); 95 radioPreference.setRadioGroup(LAYOUT_RADIO_GROUP); 96 radioPreference.setLayoutResource( 97 com.android.tv.settings.R.layout.preference_reversed_widget); 98 if (kl.getDescriptor().equals(currentLayoutIdentifier)) { 99 radioPreference.setChecked(true); 100 activePreference = radioPreference; 101 } 102 screen.addPreference(radioPreference); 103 mKeyboardLayoutMap.put(kl.getDescriptor(), kl); 104 } 105 106 if (activePreference != null) { 107 scrollToPreference(activePreference); 108 } 109 110 setPreferenceScreen(screen); 111 } 112 113 @Override onPreferenceTreeClick(Preference preference)114 public boolean onPreferenceTreeClick(Preference preference) { 115 if (preference instanceof RadioPreference) { 116 final RadioPreference radioPreference = (RadioPreference) preference; 117 radioPreference.clearOtherRadioPreferences(getPreferenceScreen()); 118 if (radioPreference.isChecked()) { 119 mIm.setCurrentKeyboardLayoutForInputDevice(mInputDeviceIdentifier, 120 mKeyboardLayoutMap.get(radioPreference.getKey()).getDescriptor()); 121 } else { 122 radioPreference.setChecked(true); 123 } 124 } 125 return super.onPreferenceTreeClick(preference); 126 } 127 128 @Override getPageId()129 protected int getPageId() { 130 return super.getPageId(); 131 } 132 133 @Override onInputDeviceAdded(int deviceId)134 public void onInputDeviceAdded(int deviceId) { 135 // ignore 136 } 137 138 @Override onInputDeviceRemoved(int deviceId)139 public void onInputDeviceRemoved(int deviceId) { 140 if (deviceId == mDeviceId) { 141 back(); 142 } 143 } 144 145 @Override onInputDeviceChanged(int deviceId)146 public void onInputDeviceChanged(int deviceId) { 147 // ignore 148 } 149 back()150 private void back() { 151 if (getCallbackFragment() instanceof TwoPanelSettingsFragment) { 152 TwoPanelSettingsFragment parentFragment = 153 (TwoPanelSettingsFragment) getCallbackFragment(); 154 if (parentFragment.isFragmentInTheMainPanel(this)) { 155 parentFragment.navigateBack(); 156 } 157 } else if (getCallbackFragment() instanceof SliceFragment.OnePanelSliceFragmentContainer) { 158 ((SliceFragment.OnePanelSliceFragmentContainer) getCallbackFragment()).navigateBack(); 159 } 160 } 161 } 162