1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.systemui.tuner; 16 17 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY; 18 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_CODE_END; 19 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_CODE_START; 20 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_IMAGE_DELIM; 21 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.MENU_IME_ROTATE; 22 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAVSPACE; 23 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_LEFT; 24 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_RIGHT; 25 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_VIEWS; 26 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.extractButton; 27 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.extractImage; 28 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.extractKeycode; 29 30 import android.annotation.Nullable; 31 import android.app.AlertDialog; 32 import android.graphics.Color; 33 import android.graphics.drawable.Drawable; 34 import android.graphics.drawable.Icon; 35 import android.os.Bundle; 36 import android.os.Handler; 37 import android.support.v7.preference.ListPreference; 38 import android.support.v7.preference.Preference; 39 import android.support.v7.preference.Preference.OnPreferenceChangeListener; 40 import android.text.SpannableStringBuilder; 41 import android.text.style.ImageSpan; 42 import android.util.Log; 43 import android.util.TypedValue; 44 import android.view.KeyEvent; 45 import android.widget.EditText; 46 47 import com.android.systemui.Dependency; 48 import com.android.systemui.R; 49 import com.android.systemui.tuner.TunerService.Tunable; 50 51 import java.util.ArrayList; 52 53 public class NavBarTuner extends TunerPreferenceFragment { 54 55 private static final String LAYOUT = "layout"; 56 private static final String LEFT = "left"; 57 private static final String RIGHT = "right"; 58 59 private static final String TYPE = "type"; 60 private static final String KEYCODE = "keycode"; 61 private static final String ICON = "icon"; 62 63 private static final int[][] ICONS = new int[][]{ 64 {R.drawable.ic_qs_circle, R.string.tuner_circle}, 65 {R.drawable.ic_add, R.string.tuner_plus}, 66 {R.drawable.ic_remove, R.string.tuner_minus}, 67 {R.drawable.ic_left, R.string.tuner_left}, 68 {R.drawable.ic_right, R.string.tuner_right}, 69 {R.drawable.ic_menu, R.string.tuner_menu}, 70 }; 71 72 private final ArrayList<Tunable> mTunables = new ArrayList<>(); 73 private Handler mHandler; 74 75 @Override 76 public void onCreate(@Nullable Bundle savedInstanceState) { 77 mHandler = new Handler(); 78 super.onCreate(savedInstanceState); 79 } 80 81 @Override 82 public void onActivityCreated(@Nullable Bundle savedInstanceState) { 83 super.onActivityCreated(savedInstanceState); 84 getActivity().getActionBar().setDisplayHomeAsUpEnabled(true); 85 } 86 87 @Override 88 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 89 addPreferencesFromResource(R.xml.nav_bar_tuner); 90 bindLayout((ListPreference) findPreference(LAYOUT)); 91 bindButton(NAV_BAR_LEFT, NAVSPACE, LEFT); 92 bindButton(NAV_BAR_RIGHT, MENU_IME_ROTATE, RIGHT); 93 } 94 95 @Override 96 public void onDestroy() { 97 super.onDestroy(); 98 mTunables.forEach(t -> Dependency.get(TunerService.class).removeTunable(t)); 99 } 100 101 private void addTunable(Tunable tunable, String... keys) { 102 mTunables.add(tunable); 103 Dependency.get(TunerService.class).addTunable(tunable, keys); 104 } 105 106 private void bindLayout(ListPreference preference) { 107 addTunable((key, newValue) -> mHandler.post(() -> { 108 String val = newValue; 109 if (val == null) { 110 val = "default"; 111 } 112 preference.setValue(val); 113 }), NAV_BAR_VIEWS); 114 preference.setOnPreferenceChangeListener((preference1, newValue) -> { 115 String val = (String) newValue; 116 if ("default".equals(val)) val = null; 117 Dependency.get(TunerService.class).setValue(NAV_BAR_VIEWS, val); 118 return true; 119 }); 120 } 121 122 private void bindButton(String setting, String def, String k) { 123 ListPreference type = (ListPreference) findPreference(TYPE + "_" + k); 124 Preference keycode = findPreference(KEYCODE + "_" + k); 125 ListPreference icon = (ListPreference) findPreference(ICON + "_" + k); 126 setupIcons(icon); 127 addTunable((key, newValue) -> mHandler.post(() -> { 128 String val = newValue; 129 if (val == null) { 130 val = def; 131 } 132 String button = extractButton(val); 133 if (button.startsWith(KEY)) { 134 type.setValue(KEY); 135 String uri = extractImage(button); 136 int code = extractKeycode(button); 137 icon.setValue(uri); 138 updateSummary(icon); 139 keycode.setSummary(code + ""); 140 keycode.setVisible(true); 141 icon.setVisible(true); 142 } else { 143 type.setValue(button); 144 keycode.setVisible(false); 145 icon.setVisible(false); 146 } 147 }), setting); 148 OnPreferenceChangeListener listener = (preference, newValue) -> { 149 mHandler.post(() -> { 150 setValue(setting, type, keycode, icon); 151 updateSummary(icon); 152 }); 153 return true; 154 }; 155 type.setOnPreferenceChangeListener(listener); 156 icon.setOnPreferenceChangeListener(listener); 157 keycode.setOnPreferenceClickListener(preference -> { 158 EditText editText = new EditText(getContext()); 159 new AlertDialog.Builder(getContext()) 160 .setTitle(preference.getTitle()) 161 .setView(editText) 162 .setNegativeButton(android.R.string.cancel, null) 163 .setPositiveButton(android.R.string.ok, (dialog, which) -> { 164 int code = KeyEvent.KEYCODE_ENTER; 165 try { 166 code = Integer.parseInt(editText.getText().toString()); 167 } catch (Exception e) { 168 } 169 keycode.setSummary(code + ""); 170 setValue(setting, type, keycode, icon); 171 }).show(); 172 return true; 173 }); 174 } 175 176 private void updateSummary(ListPreference icon) { 177 try { 178 int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14, 179 getContext().getResources().getDisplayMetrics()); 180 String pkg = icon.getValue().split("/")[0]; 181 int id = Integer.parseInt(icon.getValue().split("/")[1]); 182 SpannableStringBuilder builder = new SpannableStringBuilder(); 183 Drawable d = Icon.createWithResource(pkg, id) 184 .loadDrawable(getContext()); 185 d.setTint(Color.BLACK); 186 d.setBounds(0, 0, size, size); 187 ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BASELINE); 188 builder.append(" ", span, 0); 189 builder.append(" "); 190 for (int i = 0; i < ICONS.length; i++) { 191 if (ICONS[i][0] == id) { 192 builder.append(getString(ICONS[i][1])); 193 } 194 } 195 icon.setSummary(builder); 196 } catch (Exception e) { 197 Log.d("NavButton", "Problem with summary", e); 198 icon.setSummary(null); 199 } 200 } 201 202 private void setValue(String setting, ListPreference type, Preference keycode, 203 ListPreference icon) { 204 String button = type.getValue(); 205 if (KEY.equals(button)) { 206 String uri = icon.getValue(); 207 int code = KeyEvent.KEYCODE_ENTER; 208 try { 209 code = Integer.parseInt(keycode.getSummary().toString()); 210 } catch (Exception e) { 211 } 212 button = button + KEY_CODE_START + code + KEY_IMAGE_DELIM + uri + KEY_CODE_END; 213 } 214 Dependency.get(TunerService.class).setValue(setting, button); 215 } 216 217 private void setupIcons(ListPreference icon) { 218 CharSequence[] labels = new CharSequence[ICONS.length]; 219 CharSequence[] values = new CharSequence[ICONS.length]; 220 int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14, 221 getContext().getResources().getDisplayMetrics()); 222 for (int i = 0; i < ICONS.length; i++) { 223 SpannableStringBuilder builder = new SpannableStringBuilder(); 224 Drawable d = Icon.createWithResource(getContext().getPackageName(), ICONS[i][0]) 225 .loadDrawable(getContext()); 226 d.setTint(Color.BLACK); 227 d.setBounds(0, 0, size, size); 228 ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BASELINE); 229 builder.append(" ", span, 0); 230 builder.append(" "); 231 builder.append(getString(ICONS[i][1])); 232 labels[i] = builder; 233 values[i] = getContext().getPackageName() + "/" + ICONS[i][0]; 234 } 235 icon.setEntries(labels); 236 icon.setEntryValues(values); 237 } 238 } 239