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:
25
packages/SystemUI/res/layout/custom_key.xml
Normal file
25
packages/SystemUI/res/layout/custom_key.xml
Normal 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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user