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; 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.content.DialogInterface; 33 import android.content.DialogInterface.OnClickListener; 34 import android.content.res.Resources; 35 import android.graphics.Color; 36 import android.graphics.Paint; 37 import android.graphics.Paint.FontMetricsInt; 38 import android.graphics.Rect; 39 import android.graphics.drawable.Drawable; 40 import android.graphics.drawable.Icon; 41 import android.os.Bundle; 42 import android.os.Handler; 43 import android.support.v14.preference.PreferenceFragment; 44 import android.support.v7.preference.DropDownPreference; 45 import android.support.v7.preference.ListPreference; 46 import android.support.v7.preference.Preference; 47 import android.support.v7.preference.Preference.OnPreferenceChangeListener; 48 import android.support.v7.preference.Preference.OnPreferenceClickListener; 49 import android.support.v7.preference.PreferenceCategory; 50 import android.text.SpannableStringBuilder; 51 import android.text.style.ImageSpan; 52 import android.util.Log; 53 import android.util.TypedValue; 54 import android.view.KeyEvent; 55 import android.widget.EditText; 56 57 import com.android.systemui.Dependency; 58 import com.android.systemui.R; 59 import com.android.systemui.statusbar.phone.NavigationBarInflaterView; 60 import com.android.systemui.tuner.TunerService.Tunable; 61 62 import java.util.ArrayList; 63 64 public class NavBarTuner extends TunerPreferenceFragment { 65 66 private static final String LAYOUT = "layout"; 67 private static final String LEFT = "left"; 68 private static final String RIGHT = "right"; 69 70 private static final String TYPE = "type"; 71 private static final String KEYCODE = "keycode"; 72 private static final String ICON = "icon"; 73 74 private static final int[][] ICONS = new int[][]{ 75 {R.drawable.ic_qs_circle, R.string.tuner_circle}, 76 {R.drawable.ic_add, R.string.tuner_plus}, 77 {R.drawable.ic_remove, R.string.tuner_minus}, 78 {R.drawable.ic_left, R.string.tuner_left}, 79 {R.drawable.ic_right, R.string.tuner_right}, 80 {R.drawable.ic_menu, R.string.tuner_menu}, 81 }; 82 83 private final ArrayList<Tunable> mTunables = new ArrayList<>(); 84 private Handler mHandler; 85 86 @Override onCreate(@ullable Bundle savedInstanceState)87 public void onCreate(@Nullable Bundle savedInstanceState) { 88 mHandler = new Handler(); 89 super.onCreate(savedInstanceState); 90 } 91 92 @Override onActivityCreated(@ullable Bundle savedInstanceState)93 public void onActivityCreated(@Nullable Bundle savedInstanceState) { 94 super.onActivityCreated(savedInstanceState); 95 getActivity().getActionBar().setDisplayHomeAsUpEnabled(true); 96 } 97 98 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)99 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 100 addPreferencesFromResource(R.xml.nav_bar_tuner); 101 bindLayout((ListPreference) findPreference(LAYOUT)); 102 bindButton(NAV_BAR_LEFT, NAVSPACE, LEFT); 103 bindButton(NAV_BAR_RIGHT, MENU_IME, RIGHT); 104 } 105 106 @Override onDestroy()107 public void onDestroy() { 108 super.onDestroy(); 109 mTunables.forEach(t -> Dependency.get(TunerService.class).removeTunable(t)); 110 } 111 addTunable(Tunable tunable, String... keys)112 private void addTunable(Tunable tunable, String... keys) { 113 mTunables.add(tunable); 114 Dependency.get(TunerService.class).addTunable(tunable, keys); 115 } 116 bindLayout(ListPreference preference)117 private void bindLayout(ListPreference preference) { 118 addTunable((key, newValue) -> mHandler.post(() -> { 119 String val = newValue; 120 if (val == null) { 121 val = "default"; 122 } 123 preference.setValue(val); 124 }), NAV_BAR_VIEWS); 125 preference.setOnPreferenceChangeListener((preference1, newValue) -> { 126 String val = (String) newValue; 127 if ("default".equals(val)) val = null; 128 Dependency.get(TunerService.class).setValue(NAV_BAR_VIEWS, val); 129 return true; 130 }); 131 } 132 bindButton(String setting, String def, String k)133 private void bindButton(String setting, String def, String k) { 134 ListPreference type = (ListPreference) findPreference(TYPE + "_" + k); 135 Preference keycode = findPreference(KEYCODE + "_" + k); 136 ListPreference icon = (ListPreference) findPreference(ICON + "_" + k); 137 setupIcons(icon); 138 addTunable((key, newValue) -> mHandler.post(() -> { 139 String val = newValue; 140 if (val == null) { 141 val = def; 142 } 143 String button = extractButton(val); 144 if (button.startsWith(KEY)) { 145 type.setValue(KEY); 146 String uri = extractImage(button); 147 int code = extractKeycode(button); 148 icon.setValue(uri); 149 updateSummary(icon); 150 keycode.setSummary(code + ""); 151 keycode.setVisible(true); 152 icon.setVisible(true); 153 } else { 154 type.setValue(button); 155 keycode.setVisible(false); 156 icon.setVisible(false); 157 } 158 }), setting); 159 OnPreferenceChangeListener listener = (preference, newValue) -> { 160 mHandler.post(() -> { 161 setValue(setting, type, keycode, icon); 162 updateSummary(icon); 163 }); 164 return true; 165 }; 166 type.setOnPreferenceChangeListener(listener); 167 icon.setOnPreferenceChangeListener(listener); 168 keycode.setOnPreferenceClickListener(preference -> { 169 EditText editText = new EditText(getContext()); 170 new AlertDialog.Builder(getContext()) 171 .setTitle(preference.getTitle()) 172 .setView(editText) 173 .setNegativeButton(android.R.string.cancel, null) 174 .setPositiveButton(android.R.string.ok, (dialog, which) -> { 175 int code = KeyEvent.KEYCODE_ENTER; 176 try { 177 code = Integer.parseInt(editText.getText().toString()); 178 } catch (Exception e) { 179 } 180 keycode.setSummary(code + ""); 181 setValue(setting, type, keycode, icon); 182 }).show(); 183 return true; 184 }); 185 } 186 updateSummary(ListPreference icon)187 private void updateSummary(ListPreference icon) { 188 try { 189 int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14, 190 getContext().getResources().getDisplayMetrics()); 191 String pkg = icon.getValue().split("/")[0]; 192 int id = Integer.parseInt(icon.getValue().split("/")[1]); 193 SpannableStringBuilder builder = new SpannableStringBuilder(); 194 Drawable d = Icon.createWithResource(pkg, id) 195 .loadDrawable(getContext()); 196 d.setTint(Color.BLACK); 197 d.setBounds(0, 0, size, size); 198 ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BASELINE); 199 builder.append(" ", span, 0); 200 builder.append(" "); 201 for (int i = 0; i < ICONS.length; i++) { 202 if (ICONS[i][0] == id) { 203 builder.append(getString(ICONS[i][1])); 204 } 205 } 206 icon.setSummary(builder); 207 } catch (Exception e) { 208 Log.d("NavButton", "Problem with summary", e); 209 icon.setSummary(null); 210 } 211 } 212 setValue(String setting, ListPreference type, Preference keycode, ListPreference icon)213 private void setValue(String setting, ListPreference type, Preference keycode, 214 ListPreference icon) { 215 String button = type.getValue(); 216 if (KEY.equals(button)) { 217 String uri = icon.getValue(); 218 int code = KeyEvent.KEYCODE_ENTER; 219 try { 220 code = Integer.parseInt(keycode.getSummary().toString()); 221 } catch (Exception e) { 222 } 223 button = button + KEY_CODE_START + code + KEY_IMAGE_DELIM + uri + KEY_CODE_END; 224 } 225 Dependency.get(TunerService.class).setValue(setting, button); 226 } 227 setupIcons(ListPreference icon)228 private void setupIcons(ListPreference icon) { 229 CharSequence[] labels = new CharSequence[ICONS.length]; 230 CharSequence[] values = new CharSequence[ICONS.length]; 231 int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14, 232 getContext().getResources().getDisplayMetrics()); 233 for (int i = 0; i < ICONS.length; i++) { 234 SpannableStringBuilder builder = new SpannableStringBuilder(); 235 Drawable d = Icon.createWithResource(getContext().getPackageName(), ICONS[i][0]) 236 .loadDrawable(getContext()); 237 d.setTint(Color.BLACK); 238 d.setBounds(0, 0, size, size); 239 ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BASELINE); 240 builder.append(" ", span, 0); 241 builder.append(" "); 242 builder.append(getString(ICONS[i][1])); 243 labels[i] = builder; 244 values[i] = getContext().getPackageName() + "/" + ICONS[i][0]; 245 } 246 icon.setEntries(labels); 247 icon.setEntryValues(values); 248 } 249 } 250