1 /* 2 * KeymapPreference.java - Even simpler preference for mapping keys 3 * 4 * Copyright (C) 2010 Kostas Nakos 5 * Copyright (C) 2010 Atari800 development team (see DOC/CREDITS) 6 * 7 * This file is part of the Atari800 emulator project which emulates 8 * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers. 9 * 10 * Atari800 is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * Atari800 is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with Atari800; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 */ 24 25 package name.nick.jubanka.colleen; 26 27 import android.preference.DialogPreference; 28 import android.content.Context; 29 import android.view.KeyCharacterMap; 30 import android.view.View; 31 import android.view.ViewGroup; 32 import android.view.KeyEvent; 33 import android.content.res.TypedArray; 34 import android.util.AttributeSet; 35 import android.util.Log; 36 import android.content.DialogInterface; 37 import android.os.Bundle; 38 import android.app.Dialog; 39 import android.app.AlertDialog; 40 import android.util.SparseArray; 41 import android.widget.TextView; 42 import android.widget.EditText; 43 import android.R.style; 44 import android.util.TypedValue; 45 import android.view.LayoutInflater; 46 import static android.view.KeyEvent.*; 47 import static name.nick.jubanka.colleen.A800view.*; 48 49 50 public final class KeymapPreference extends DialogPreference 51 { 52 private static final String TAG = "KeyPreference"; 53 54 private static final int DEFKEY = 'a'; 55 private static final String DEFKEYEXT = "-1,-1"; 56 private static final int EXTSTR_ACTION = 0; 57 private static final int EXTSTR_KEY = 1; 58 59 private KeyCharacterMap _keymap; 60 private int _def; 61 private String _defext = null; 62 private boolean _extended = false; 63 KeymapPreference(Context c, AttributeSet a)64 public KeymapPreference(Context c, AttributeSet a) { 65 super(c, a); 66 _keymap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD); 67 68 _extended = Boolean.parseBoolean( c.obtainStyledAttributes(a, R.styleable.KeymapPreference) 69 .getString(R.styleable.KeymapPreference_ext) ); 70 71 setNegativeButtonText(R.string.cancel); 72 setDialogTitle(getTitle()); 73 } 74 75 @Override onGetDefaultValue(TypedArray a, int i)76 protected Object onGetDefaultValue(TypedArray a, int i) { 77 try { 78 _def = a.getInt(i, DEFKEY); 79 return _def; 80 } catch (NumberFormatException e) { 81 _defext = a.getString(i); 82 if (_defext == null) 83 _defext = DEFKEYEXT; 84 return _defext; 85 } 86 } 87 88 @Override onSetInitialValue(boolean restore, Object def)89 protected void onSetInitialValue(boolean restore, Object def) { 90 if (!restore) 91 if (!_extended) 92 persistInt((Integer) def); 93 else 94 persistString((String) def); 95 } 96 97 @Override showDialog(Bundle state)98 protected void showDialog(Bundle state) { 99 super.showDialog(state); 100 Dialog d = getDialog(); 101 d.takeKeyEvents(true); 102 ((AlertDialog) d).getButton(DialogInterface.BUTTON_POSITIVE).setVisibility(View.GONE); 103 } 104 105 @Override onCreateDialogView()106 protected View onCreateDialogView() { 107 View v = new SnoopTextView(getContext()); 108 return v; 109 } 110 111 private final class SnoopTextView extends EditText 112 { SnoopTextView(Context c)113 public SnoopTextView(Context c) { 114 super(c); 115 setText( (!_extended) ? R.string.pref_keymapmsg : R.string.pref_keymapmsg1); 116 setCursorVisible(false); 117 int pad = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 118 (float) 10, 119 c.getResources().getDisplayMetrics()); 120 setPadding(pad, pad, pad, pad); 121 setFocusable(true); 122 setFocusableInTouchMode(true); 123 requestFocus(); 124 } 125 126 @Override onKeyDown(int kc, KeyEvent ev)127 public boolean onKeyDown(int kc, KeyEvent ev) { 128 Log.d(TAG, "key " + kc); 129 130 for (int res: RESKEYS) 131 if (res == kc) 132 return false; 133 134 final int k = xlatKey(kc); 135 136 if (k == 0) 137 return false; 138 139 if (k == getKeymap() && !_extended) { 140 getDialog().dismiss(); 141 return true; 142 } 143 144 if (callChangeListener(new Integer(k)) == false) { 145 new AlertDialog.Builder(getContext()) 146 .setTitle(R.string.warning) 147 .setIcon(android.R.drawable.ic_dialog_alert) 148 .setMessage(R.string.pref_keymapdupmsg) 149 .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { 150 public void onClick(DialogInterface d, int i) { 151 callChangeListener(new Integer(-k)); 152 setKeymap(k); 153 d.dismiss(); 154 if (!_extended) { 155 Dialog d1 = getDialog(); 156 if (d1 != null) 157 d1.dismiss(); 158 } else 159 showExtDialog(); 160 } 161 }) 162 .setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { 163 public void onClick(DialogInterface d, int i) { 164 Dialog d1 = getDialog(); 165 if (d1 != null) 166 d1.dismiss(); 167 } 168 }) 169 .show(); 170 return true; 171 } 172 173 setKeymap(k); 174 if (!_extended) 175 getDialog().dismiss(); 176 else 177 showExtDialog(); 178 return true; 179 } 180 } 181 showExtDialog()182 private void showExtDialog() 183 { 184 LayoutInflater inf = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 185 new AlertDialog.Builder(getContext()) 186 .setTitle(getTitle()) 187 .setView(inf.inflate(R.layout.extended_keymap, null)) 188 .setCancelable(false) 189 .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { 190 public void onClick(DialogInterface d, int i) { 191 CharSequence txt = ((TextView) ((Dialog) d).findViewById(R.id.keyinput)).getText(); 192 if (txt == null || txt.length() != 1) { 193 getDialog().dismiss(); 194 return; 195 } 196 persistString( buildExtPref(parseExtPref(EXTSTR_ACTION), (int) txt.charAt(0)) ); 197 updateSum(); 198 d.dismiss(); 199 getDialog().dismiss(); 200 } 201 }) 202 .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { 203 public void onClick(DialogInterface d, int i) { 204 getDialog().dismiss(); 205 } 206 }) 207 .show(); 208 } 209 updateSum()210 public void updateSum() { 211 StringBuilder str = new StringBuilder(); 212 str.append( getContext() 213 .getString(_extended ? R.string.pref_keymap_controller : R.string.pref_keymap_current) ); 214 str.append(" "); 215 str.append( getKeyname(getKeymap()) ); 216 if (_extended) { 217 str.append(" "); 218 str.append( getContext().getString(R.string.pref_keymap_mappedto) ); 219 str.append(" "); 220 str.append( getKeyname(parseExtPref(EXTSTR_KEY)) ); 221 } 222 setSummary(str); 223 } 224 xlatKey(int kc)225 private int xlatKey(int kc) { 226 int k; 227 228 Integer xlat = A800view.XLATKEYS.get(kc); 229 if (xlat != null) 230 k = xlat.intValue(); 231 else 232 k = _keymap.get(kc, 0); 233 return k; 234 } 235 setKeymap(int k)236 public void setKeymap(int k) { 237 if (!_extended) 238 persistInt(k); 239 else 240 persistString( buildExtPref(k, parseExtPref(EXTSTR_KEY)) ); 241 updateSum(); 242 } 243 setDefaultKeymap()244 public void setDefaultKeymap() { 245 if (!_extended) return; 246 persistString(DEFKEYEXT); 247 updateSum(); 248 } 249 getKeymap()250 public int getKeymap() { 251 if (!_extended) 252 return getPersistedInt(-128) == -128 ? _def : getPersistedInt(-1); 253 else 254 return parseExtPref(EXTSTR_ACTION); 255 } 256 parseExtPref(int part)257 private int parseExtPref(int part) { 258 String str = getPersistedString(null); 259 return Integer.parseInt( ((str != null) ? str : _defext).split(",")[part] ); 260 } 261 buildExtPref(int k1, int k2)262 private String buildExtPref(int k1, int k2) { 263 return Integer.toString(k1) + "," + Integer.toString(k2); 264 } 265 getKeyname(int k)266 private String getKeyname(int k) { 267 String name = null; 268 name = KEYNAMES.get(k); 269 if (name != null) return name; 270 if (k > 31 && k < 127) return Character.toString((char) k); 271 return "ASCII " + k; 272 } 273 274 // Real programmers *hate* data entry ;-) 275 private static final SparseArray<String> KEYNAMES = new SparseArray<String>(13); 276 static { 277 KEYNAMES.put(-1, "None"); 278 KEYNAMES.put(' ', "Space"); KEYNAMES.put(KEY_DOWN, R)279 KEYNAMES.put(KEY_DOWN, "Down arrow"); KEYNAMES.put(KEY_LEFT, R)280 KEYNAMES.put(KEY_LEFT, "Left arrow"); KEYNAMES.put(KEY_RIGHT, R)281 KEYNAMES.put(KEY_RIGHT, "Right arrow"); KEYNAMES.put(KEY_UP, R)282 KEYNAMES.put(KEY_UP, "Up arrow"); KEYNAMES.put(KEY_ENTER, R)283 KEYNAMES.put(KEY_ENTER, "Enter"); KEYNAMES.put(KEY_BACKSPACE, R)284 KEYNAMES.put(KEY_BACKSPACE, "Del"); KEYNAMES.put(KEY_BT_X, R)285 KEYNAMES.put(KEY_BT_X, "Button X"); KEYNAMES.put(KEY_BT_Y, R)286 KEYNAMES.put(KEY_BT_Y, "Button Y"); KEYNAMES.put(KEY_BT_L1, R)287 KEYNAMES.put(KEY_BT_L1, "Button L1"); KEYNAMES.put(KEY_BT_R1, R)288 KEYNAMES.put(KEY_BT_R1, "Button R1"); KEYNAMES.put(KEY_BREAK, R)289 KEYNAMES.put(KEY_BREAK, "DPAD Enter"); 290 } 291 private static final int[] RESKEYS = { 292 KEYCODE_SHIFT_LEFT, KEYCODE_SHIFT_RIGHT, KEYCODE_VOLUME_UP, KEYCODE_VOLUME_DOWN, 293 KEYCODE_MENU, KEYCODE_SEARCH, KEYCODE_BACK, KEYCODE_HOME, KEYCODE_POWER, 294 KEYCODE_CALL, KEYCODE_ENDCALL 295 }; 296 } 297