Add keycode buttons to nav bar tuner

Allow a keycode (list generated from KeyEvent.java) and a image to
be selected as a new KeyButtonView to be shown in the nav bar.

Change-Id: I26fcad5b74a96090f2c5574fd214b4afbcadbb30
This commit is contained in:
Jason Monk
2016-01-24 10:15:55 -05:00
parent 3b58714344
commit 8457ad89a8
6 changed files with 256 additions and 10 deletions

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->
<com.android.systemui.statusbar.policy.KeyButtonView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:layout_width="@dimen/navigation_side_padding"
android:layout_height="match_parent"
android:layout_weight="0"
android:scaleType="center"
android:tint="@android:color/white"
android:contentDescription="@string/accessibility_key" />

View File

@@ -1387,4 +1387,19 @@
<!-- SysUI Tuner: Description of nav bar button that holds the clipboard [CHAR LIMIT=NONE] -->
<string name="clipboard_description">The Clipboard allows items to be dragged directly to the clipboard. Items can also be dragged directly out of the clipboard when present.</string>
<!-- SysUI Tuner: Accessibility description for custom nav key [CHAR LIMIT=NONE] -->
<string name="accessibility_key">Custom navigation button</string>
<!-- SysUI Tuner: Nav bar button that emulates a keycode [CHAR LIMIT=30] -->
<string name="keycode">Keycode</string>
<!-- SysUI Tuner: Description of nav bar button that emulates a keycode [CHAR LIMIT=NONE] -->
<string name="keycode_description">Keycode buttons allow keyboard keys to
be added to the Navigation Bar. When pressed they emulate the selected
keyboard key. First the key must be selected for the button, followed
by an image to be shown on the button.</string>
<!-- SysUI Tuner: Title of dialog to select which key to emulate [CHAR LIMIT=60] -->
<string name="select_keycode">Select Keyboard Button</string>
</resources>

View File

@@ -18,7 +18,10 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
@@ -28,8 +31,10 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Space;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.KeyButtonView;
import com.android.systemui.tuner.TunerService;
import java.io.File;
import java.util.Objects;
public class NavigationBarInflaterView extends FrameLayout implements TunerService.Tunable {
@@ -44,18 +49,24 @@ public class NavigationBarInflaterView extends FrameLayout implements TunerServi
public static final String RECENT = "recent";
public static final String NAVSPACE = "space";
public static final String CLIPBOARD = "clipboard";
public static final String KEY = "key";
public static final String GRAVITY_SEPARATOR = ";";
public static final String BUTTON_SEPARATOR = ",";
protected final LayoutInflater mLayoutInflater;
protected final LayoutInflater mLandscapeInflater;
public static final String SIZE_MOD_START = "[";
public static final String SIZE_MOD_END = "]";
public static final String KEY_CODE_START = "(";
public static final String KEY_IMAGE_DELIM = ":";
public static final String KEY_CODE_END = ")";
protected final LayoutInflater mLayoutInflater;
protected final LayoutInflater mLandscapeInflater;
protected FrameLayout mRot0;
protected FrameLayout mRot90;
private SparseArray<ButtonDispatcher> mButtonDispatchers;
private String mCurrentLayout;
@@ -236,6 +247,14 @@ public class NavigationBarInflaterView extends FrameLayout implements TunerServi
v = inflater.inflate(R.layout.nav_key_space, parent, false);
} else if (CLIPBOARD.equals(button)) {
v = inflater.inflate(R.layout.clipboard, parent, false);
} else if (button.startsWith(KEY)) {
String uri = extractImage(button);
int code = extractKeycode(button);
v = inflater.inflate(R.layout.custom_key, parent, false);
((KeyButtonView) v).setCode(code);
if (uri != null) {
((KeyButtonView) v).loadAsync(uri);
}
} else {
return null;
}
@@ -249,6 +268,24 @@ public class NavigationBarInflaterView extends FrameLayout implements TunerServi
return v;
}
public static String extractImage(String buttonSpec) {
if (!buttonSpec.contains(KEY_IMAGE_DELIM)) {
return null;
}
final int start = buttonSpec.indexOf(KEY_IMAGE_DELIM);
String subStr = buttonSpec.substring(start + 1, buttonSpec.indexOf(KEY_CODE_END));
return subStr;
}
public static int extractKeycode(String buttonSpec) {
if (!buttonSpec.contains(KEY_CODE_START)) {
return 1;
}
final int start = buttonSpec.indexOf(KEY_CODE_START);
String subStr = buttonSpec.substring(start + 1, buttonSpec.indexOf(KEY_IMAGE_DELIM));
return Integer.parseInt(subStr);
}
public static float extractSize(String buttonSpec) {
if (!buttonSpec.contains(SIZE_MOD_START)) {
return 1;

View File

@@ -20,8 +20,12 @@ import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.hardware.input.InputManager;
import android.media.AudioManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.AttributeSet;
@@ -99,6 +103,24 @@ public class KeyButtonView extends ImageView {
setBackground(new KeyButtonRipple(context, this));
}
public void setCode(int code) {
mCode = code;
}
public void loadAsync(String uri) {
new AsyncTask<String, Void, Drawable>() {
@Override
protected Drawable doInBackground(String... params) {
return Icon.createWithContentUri(params[0]).loadDrawable(mContext);
}
@Override
protected void onPostExecute(Drawable drawable) {
setImageDrawable(drawable);
}
}.execute(uri);
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2016 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.systemui.tuner;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.util.Pair;
import android.view.KeyEvent;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import com.android.systemui.R;
public class KeycodeSelectionHelper {
private static final ArrayList<String> mKeycodeStrings = new ArrayList<>();
private static final ArrayList<Integer> mKeycodes = new ArrayList<>();
private static final String KEYCODE_STRING = "KEYCODE_";
static {
Class<KeyEvent> cls = KeyEvent.class;
for (Field field : cls.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers())
&& field.getName().startsWith(KEYCODE_STRING)
&& field.getType().equals(int.class)) {
try {
mKeycodeStrings.add(formatString(field.getName()));
mKeycodes.add((Integer) field.get(null));
} catch (IllegalAccessException e) {
}
}
}
}
// Force the string into something somewhat readable.
private static String formatString(String name) {
StringBuilder str = new StringBuilder(name.replace(KEYCODE_STRING, "").replace("_", " ")
.toLowerCase());
for (int i = 0; i < str.length(); i++) {
if (i == 0 || str.charAt(i - 1) == ' ') {
str.setCharAt(i, Character.toUpperCase(str.charAt(i)));
}
}
return str.toString();
}
public static void showKeycodeSelect(Context context, final OnSelectionComplete listener) {
new AlertDialog.Builder(context)
.setTitle(R.string.select_keycode)
.setItems(mKeycodeStrings.toArray(new String[0]),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
listener.onSelectionComplete(mKeycodes.get(which));
}
}).show();
}
public static Intent getSelectImageIntent() {
return new Intent(Intent.ACTION_OPEN_DOCUMENT).addCategory(Intent.CATEGORY_OPENABLE)
.setType("image/*");
}
public interface OnSelectionComplete {
void onSelectionComplete(int code);
}
}

View File

@@ -17,14 +17,17 @@ package com.android.systemui.tuner;
import com.android.systemui.R;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v7.widget.LinearLayoutManager;
@@ -46,6 +49,10 @@ import java.util.ArrayList;
import java.util.List;
import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.CLIPBOARD;
import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY;
import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_CODE_END;
import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_CODE_START;
import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_IMAGE_DELIM;
import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.SIZE_MOD_END;
import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.SIZE_MOD_START;
import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.extractButton;
@@ -63,6 +70,7 @@ public class NavBarTuner extends Fragment implements TunerService.Tunable {
private static final int SAVE = Menu.FIRST + 1;
private static final int RESET = Menu.FIRST + 2;
private static final int READ_REQUEST = 42;
private NavBarAdapter mNavBarAdapter;
@@ -161,6 +169,8 @@ public class NavBarTuner extends Fragment implements TunerService.Tunable {
return context.getString(R.string.menu_ime);
} else if (button.startsWith(CLIPBOARD)) {
return context.getString(R.string.clipboard);
} else if (button.startsWith(KEY)) {
return context.getString(R.string.keycode);
}
return button;
}
@@ -202,7 +212,23 @@ public class NavBarTuner extends Fragment implements TunerService.Tunable {
}
}
private static class NavBarAdapter extends RecyclerView.Adapter<Holder>
private void selectImage() {
startActivityForResult(KeycodeSelectionHelper.getSelectImageIntent(), READ_REQUEST);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == READ_REQUEST && resultCode == Activity.RESULT_OK && data != null) {
final Uri uri = data.getData();
final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION);
getContext().getContentResolver().takePersistableUriPermission(uri, takeFlags);
mNavBarAdapter.onImageSelected(uri);
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
private class NavBarAdapter extends RecyclerView.Adapter<Holder>
implements View.OnClickListener {
private static final String START = "start";
@@ -220,6 +246,9 @@ public class NavBarTuner extends Fragment implements TunerService.Tunable {
private int mButtonLayout;
private ItemTouchHelper mTouchHelper;
// Stored keycode while we wait for image selection on a KEY.
private int mKeycode;
public NavBarAdapter(Context context) {
TypedArray attrs = context.getTheme().obtainStyledAttributes(null,
android.R.styleable.Preference, android.R.attr.preferenceStyle, 0);
@@ -353,7 +382,7 @@ public class NavBarTuner extends Fragment implements TunerService.Tunable {
private void showAddDialog(final Context context) {
final String[] options = new String[] {
BACK, HOME, RECENT, MENU_IME, NAVSPACE, CLIPBOARD,
BACK, HOME, RECENT, MENU_IME, NAVSPACE, CLIPBOARD, KEY,
};
final CharSequence[] labels = new CharSequence[options.length];
for (int i = 0; i < options.length; i++) {
@@ -364,16 +393,50 @@ public class NavBarTuner extends Fragment implements TunerService.Tunable {
.setItems(labels, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int index = mButtons.size() - 1;
showAddedMessage(context, options[which]);
mButtons.add(index, options[which]);
mLabels.add(index, labels[which]);
notifyItemInserted(index);
if (KEY.equals(options[which])) {
showKeyDialogs(context);
} else {
int index = mButtons.size() - 1;
showAddedMessage(context, options[which]);
mButtons.add(index, options[which]);
mLabels.add(index, labels[which]);
notifyItemInserted(index);
}
}
}).setNegativeButton(android.R.string.cancel, null)
.show();
}
private void onImageSelected(Uri uri) {
int index = mButtons.size() - 1;
mButtons.add(index, KEY + KEY_CODE_START + mKeycode + KEY_IMAGE_DELIM + uri.toString()
+ KEY_CODE_END);
mLabels.add(index, getLabel(KEY, getContext()));
notifyItemInserted(index);
}
private void showKeyDialogs(final Context context) {
final KeycodeSelectionHelper.OnSelectionComplete listener =
new KeycodeSelectionHelper.OnSelectionComplete() {
@Override
public void onSelectionComplete(int code) {
mKeycode = code;
selectImage();
}
};
new AlertDialog.Builder(context)
.setTitle(R.string.keycode)
.setMessage(R.string.keycode_description)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
KeycodeSelectionHelper.showKeycodeSelect(context, listener);
}
}).show();
}
private void showAddedMessage(Context context, String button) {
if (CLIPBOARD.equals(button)) {
new AlertDialog.Builder(context)