Hardware key: Customization support [2/2]

Based on DUI

jhenrique09 edits: make it DUI independent

Original commit message:
Same robust action library as used on software navigation. Supports
single tap, double tap, and long press. As a precautionary measure,
single tap back and single tap home are fixed and can not be changed.
Camera button actions are not supported at this time. We will bring
in wake key support at a later time.

Also includes:

Change-Id: I29ff38678821ca80db36d49d3b10d8ac29a6b4de
DUI: Initial checkin for Oreo [4/7]
Signed-off-by: Shubham Singh <coolsks94@gmail.com>
Signed-off-by: mhkjahromi <m.h.k.jahromi@gmail.com>
Signed-off-by: SagarMakhar <sagarmakhar@gmail.com>
Signed-off-by: Hưng Phan <phandinhhungvp2001@gmail.com>
This commit is contained in:
bigrushdog
2017-12-06 10:00:00 +01:00
committed by Hưng Phan
parent 7a11dc7e80
commit 54a5962b6e
8 changed files with 1072 additions and 1 deletions

View File

@@ -447,4 +447,25 @@
<item>2</item>
<item>3</item>
</string-array>
<!-- HW Keys customization -->
<string-array name="action_dialog_entries">
<item>@string/action_entry_default_action</item>
<item>@string/action_entry_custom_action</item>
<item>@string/action_entry_select_app</item>
</string-array>
<string-array name="action_dialog_values" translatable="false">
<item>@string/action_value_default_action</item>
<item>@string/action_value_custom_action</item>
<item>@string/action_value_select_app</item>
</string-array>
<string-array name="action_dialog_no_default_entries">
<item>@string/action_entry_custom_action</item>
<item>@string/action_entry_select_app</item>
</string-array>
<string-array name="action_dialog_no_default_values" translatable="false">
<item>@string/action_value_custom_action</item>
<item>@string/action_value_select_app</item>
</string-array>
</resources>

View File

@@ -176,7 +176,38 @@
<string name="torch_power_button_gesture_dt_toast">Jump to camera gesture is now disabled</string>
<string name="torch_power_button_gesture_lp">Long press power button</string>
<!-- 4G icon -->
<!-- Hardware button navigation -->
<string name="action_value_default_action" translatable="false">default</string>
<string name="action_value_select_app" translatable="false">app</string>
<string name="action_value_custom_action" translatable="false">custom</string>
<string name="action_value_back" translatable="false">task_back</string>
<string name="action_value_home" translatable="false">task_home</string>
<string name="action_value_search_assist" translatable="false">task_assist</string>
<string name="action_value_recent_apps" translatable="false">task_recents</string>
<string name="action_value_voice_search" translatable="false">task_voice_search</string>
<string name="action_value_menu" translatable="false">task_menu</string>
<string name="picker_activities">Activities</string>
<string name="select_custom_app_title">Select custom app</string>
<string name="select_custom_activity_title">Select custom activity</string>
<string name="profile_applist_title">Applications</string>
<string name="choose_action_title">Choose action</string>
<string name="action_entry_default_action">Default settings</string>
<string name="action_entry_select_app">Select application</string>
<string name="action_entry_custom_action">Select custom action</string>
<string name="hardware_keys_volume_keys_title">Volume rocker</string>
<string name="hardware_keys_home_key_title">Home button</string>
<string name="hardware_keys_menu_key_title">Menu button</string>
<string name="hardware_keys_assist_key_title">Search button</string>
<string name="hardware_keys_appswitch_key_title">Overview button</string>
<string name="hardware_keys_back_key_title">Back button</string>
<string name="hardware_keys_double_tap_summary">Set double tap action</string>
<string name="hardware_keys_single_tap_summary">Set single tap action</string>
<string name="hardware_keys_long_press_summary">Set long press action</string>
<string name="hardware_keys_single_tap_title">Single tap action</string>
<string name="hardware_keys_long_press_title">Long press action</string>
<string name="hardware_keys_double_tap_title">Double tap action</string>
<!-- 4G icon -->
<string name="show_fourg_icon_title">4G icon</string>
<string name="show_fourg_icon_summary">Display 4G icon in signal icon instead LTE</string>

View File

@@ -39,6 +39,114 @@
android:title="@string/volume_steps_fragment_title"
android:summary="@string/volume_steps_summary"
android:fragment="com.cherish.settings.fragments.VolumeStepsFragment" />
</PreferenceCategory>
<PreferenceCategory
android:key="back_key"
android:title="@string/hardware_keys_back_key_title" >
<com.cherish.settings.preferences.ActionPreference
android:key="hwkeys_button_back_long_press"
android:summary="@string/hardware_keys_long_press_summary"
android:title="@string/hardware_keys_long_press_title" >
</com.cherish.settings.preferences.ActionPreference>
<com.cherish.settings.preferences.ActionPreference
android:key="hwkeys_button_back_double_tap"
android:summary="@string/hardware_keys_double_tap_summary"
android:title="@string/hardware_keys_double_tap_title" >
</com.cherish.settings.preferences.ActionPreference>
</PreferenceCategory>
<PreferenceCategory
android:key="home_key"
android:title="@string/hardware_keys_home_key_title" >
<com.cherish.settings.preferences.ActionPreference
android:key="hwkeys_button_home_long_press"
android:summary="@string/hardware_keys_long_press_summary"
android:title="@string/hardware_keys_long_press_title" >
</com.cherish.settings.preferences.ActionPreference>
<com.cherish.settings.preferences.ActionPreference
android:key="hwkeys_button_home_double_tap"
android:summary="@string/hardware_keys_double_tap_summary"
android:title="@string/hardware_keys_double_tap_title" >
</com.cherish.settings.preferences.ActionPreference>
</PreferenceCategory>
<PreferenceCategory
android:key="app_switch_key"
android:title="@string/hardware_keys_appswitch_key_title" >
<com.cherish.settings.preferences.ActionPreference
android:key="hwkeys_button_overview_single_tap"
android:summary="@string/hardware_keys_single_tap_summary"
android:title="@string/hardware_keys_single_tap_title" >
</com.cherish.settings.preferences.ActionPreference>
<com.cherish.settings.preferences.ActionPreference
android:key="hwkeys_button_overview_long_press"
android:summary="@string/hardware_keys_long_press_summary"
android:title="@string/hardware_keys_long_press_title" >
</com.cherish.settings.preferences.ActionPreference>
<com.cherish.settings.preferences.ActionPreference
android:key="hwkeys_button_overview_double_tap"
android:summary="@string/hardware_keys_double_tap_summary"
android:title="@string/hardware_keys_double_tap_title" >
</com.cherish.settings.preferences.ActionPreference>
</PreferenceCategory>
<PreferenceCategory
android:key="menu_key"
android:title="@string/hardware_keys_menu_key_title" >
<com.cherish.settings.preferences.ActionPreference
android:key="hwkeys_button_menu_single_tap"
android:summary="@string/hardware_keys_single_tap_summary"
android:title="@string/hardware_keys_single_tap_title" >
</com.cherish.settings.preferences.ActionPreference>
<com.cherish.settings.preferences.ActionPreference
android:key="hwkeys_button_menu_long_press"
android:summary="@string/hardware_keys_long_press_summary"
android:title="@string/hardware_keys_long_press_title" >
</com.cherish.settings.preferences.ActionPreference>
<com.cherish.settings.preferences.ActionPreference
android:key="hwkeys_button_menu_double_tap"
android:summary="@string/hardware_keys_double_tap_summary"
android:title="@string/hardware_keys_double_tap_title" >
</com.cherish.settings.preferences.ActionPreference>
</PreferenceCategory>
<PreferenceCategory
android:key="assist_key"
android:title="@string/hardware_keys_assist_key_title" >
<com.cherish.settings.preferences.ActionPreference
android:key="hwkeys_button_assist_single_tap"
android:summary="@string/hardware_keys_single_tap_summary"
android:title="@string/hardware_keys_single_tap_title" >
</com.cherish.settings.preferences.ActionPreference>
<com.cherish.settings.preferences.ActionPreference
android:key="hwkeys_button_assist_long_press"
android:summary="@string/hardware_keys_long_press_summary"
android:title="@string/hardware_keys_long_press_title" >
</com.cherish.settings.preferences.ActionPreference>
<com.cherish.settings.preferences.ActionPreference
android:key="hwkeys_button_assist_double_tap"
android:summary="@string/hardware_keys_double_tap_summary"
android:title="@string/hardware_keys_double_tap_title" >
</com.cherish.settings.preferences.ActionPreference>
</PreferenceCategory>
<ListPreference

View File

@@ -33,15 +33,20 @@ import androidx.preference.SwitchPreference;
import android.provider.Settings;
import android.widget.Toast;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.cherish.settings.preferences.SystemSettingSwitchPreference;
import com.cherish.settings.preferences.SecureSettingSwitchPreference;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.util.hwkeys.ActionConstants;
import com.android.internal.util.hwkeys.ActionUtils;
import com.cherish.settings.preferences.CustomSeekBarPreference;
import com.cherish.settings.preferences.ActionFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
import android.provider.SearchIndexableResource;
import java.util.ArrayList;
import java.util.List;
@@ -49,6 +54,24 @@ import java.util.List;
public class ButtonSettings extends SettingsPreferenceFragment implements
Preference.OnPreferenceChangeListener {
// category keys
private static final String CATEGORY_BACK = "back_key";
private static final String CATEGORY_HOME = "home_key";
private static final String CATEGORY_MENU = "menu_key";
private static final String CATEGORY_ASSIST = "assist_key";
private static final String CATEGORY_APPSWITCH = "app_switch_key";
private static final String CATEGORY_VOLUME = "volume_keys";
private static final String CATEGORY_POWER = "power_key";
// Masks for checking presence of hardware keys.
// Must match values in frameworks/base/core/res/res/values/config.xml
public static final int KEY_MASK_HOME = 0x01;
public static final int KEY_MASK_BACK = 0x02;
public static final int KEY_MASK_MENU = 0x04;
public static final int KEY_MASK_ASSIST = 0x08;
public static final int KEY_MASK_APP_SWITCH = 0x10;
public static final int KEY_MASK_CAMERA = 0x20;
public static final int KEY_MASK_VOLUME = 0x40;
private static final String TORCH_POWER_BUTTON_GESTURE = "torch_power_button_gesture";
private ListPreference mTorchPowerButton;
@@ -61,6 +84,49 @@ public class ButtonSettings extends SettingsPreferenceFragment implements
final Resources res = getResources();
final ContentResolver resolver = getActivity().getContentResolver();
final PreferenceScreen prefScreen = getPreferenceScreen();
// bits for hardware keys present on device
final int deviceKeys = getResources().getInteger(
com.android.internal.R.integer.config_deviceHardwareKeys);
// read bits for present hardware keys
final boolean hasHomeKey = (deviceKeys & KEY_MASK_HOME) != 0;
final boolean hasBackKey = (deviceKeys & KEY_MASK_BACK) != 0;
final boolean hasMenuKey = (deviceKeys & KEY_MASK_MENU) != 0;
final boolean hasAssistKey = (deviceKeys & KEY_MASK_ASSIST) != 0;
final boolean hasAppSwitchKey = (deviceKeys & KEY_MASK_APP_SWITCH) != 0;
// load categories and init/remove preferences based on device
// configuration
final PreferenceCategory backCategory = (PreferenceCategory) prefScreen
.findPreference(CATEGORY_BACK);
final PreferenceCategory homeCategory = (PreferenceCategory) prefScreen
.findPreference(CATEGORY_HOME);
final PreferenceCategory menuCategory = (PreferenceCategory) prefScreen
.findPreference(CATEGORY_MENU);
final PreferenceCategory assistCategory = (PreferenceCategory) prefScreen
.findPreference(CATEGORY_ASSIST);
final PreferenceCategory appSwitchCategory = (PreferenceCategory) prefScreen
.findPreference(CATEGORY_APPSWITCH);
// back key
if (!hasBackKey) {
prefScreen.removePreference(backCategory);
}
// home key
if (!hasHomeKey) {
prefScreen.removePreference(homeCategory);
}
// App switch key (recents)
if (!hasAppSwitchKey) {
prefScreen.removePreference(appSwitchCategory);
}
// menu key
if (!hasMenuKey) {
prefScreen.removePreference(menuCategory);
}
// search/assist key
if (!hasAssistKey) {
prefScreen.removePreference(assistCategory);
}
// let super know we can load ActionPreferences
onPreferenceScreenLoaded(ActionConstants.getDefaults(ActionConstants.HWKEYS));
// screen off torch
mTorchPowerButton = (ListPreference) findPreference(TORCH_POWER_BUTTON_GESTURE);

View File

@@ -0,0 +1,261 @@
/*
* Copyright (C) 2015 TeamEos 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.
*
* Handle assignable action dialogs and instances of the ActionPreference
* class that holds target widget state
*/
package com.cherish.settings.preferences;
import java.util.ArrayList;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.util.hwkeys.ActionConstants.Defaults;
import com.android.internal.util.hwkeys.ActionHandler;
import com.android.internal.util.hwkeys.Config;
import com.android.internal.util.hwkeys.Config.ActionConfig;
import com.android.internal.util.hwkeys.Config.ButtonConfig;
import com.cherish.settings.preferences.ShortcutPickHelper;
import com.cherish.settings.preferences.ActionPreference;
import com.cherish.settings.preferences.CustomActionListAdapter;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
public class ActionFragment extends SettingsPreferenceFragment implements
ShortcutPickHelper.OnPickListener {
private static final int DIALOG_CATEGORY = 69;
private static final int DIALOG_CUSTOM_ACTIONS = 70;
private static final String KEY_FOCUSED_PREFERENCE = "key_focused_preference";
private ShortcutPickHelper mPicker;
protected ArrayList<ActionPreference> mPrefHolder;
private String mHolderTag;
private Defaults mDefaults;
private ArrayList<ButtonConfig> mButtons;
private ArrayList<ButtonConfig> mDefaultButtons;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
if (icicle != null) {
String holderTag = icicle.getString(KEY_FOCUSED_PREFERENCE);
if (holderTag != null) {
mHolderTag = holderTag;
}
}
mPicker = new ShortcutPickHelper(getActivity(), this);
mPrefHolder = new ArrayList<ActionPreference>();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
mPicker.onActivityResult(requestCode, resultCode, data);
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void shortcutPicked(String uri, String friendlyName, boolean isApplication) {
// activity dialogs pass null here if they are dismissed
// if null, do nothing, no harm
if (uri == null) {
return;
}
findAndUpdatePreference(new ActionConfig(getActivity(), uri), mHolderTag);
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (preference instanceof ActionPreference) {
mHolderTag = ((ActionPreference)preference).getTag();
showDialog(DIALOG_CATEGORY);
return true;
}
return super.onPreferenceTreeClick(preference);
}
@Override
public void onStart() {
super.onStart();
if (mDefaults != null) {
loadAndSetConfigs();
onActionPolicyEnforced(mPrefHolder);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mHolderTag != null) {
outState.putString(KEY_FOCUSED_PREFERENCE, mHolderTag);
}
}
@Override
public Dialog onCreateDialog(int dialogId) {
switch (dialogId) {
case DIALOG_CATEGORY: {
Dialog dialog;
final DialogInterface.OnClickListener categoryClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int item) {
onTargetChange(getResources().getStringArray(R.array.action_dialog_values)[item]);
dialog.dismiss();
}
};
dialog = new AlertDialog.Builder(getActivity())
.setTitle(R.string.choose_action_title)
.setItems(getResources().getStringArray(R.array.action_dialog_entries),
categoryClickListener)
.setNegativeButton(getString(android.R.string.cancel), null)
.create();
return dialog;
}
case DIALOG_CUSTOM_ACTIONS: {
Dialog dialog;
final CustomActionListAdapter adapter = new CustomActionListAdapter(getActivity());
if (!usesExtendedActionsList()) {
adapter.removeAction(ActionHandler.SYSTEMUI_TASK_HOME);
adapter.removeAction(ActionHandler.SYSTEMUI_TASK_BACK);
}
final DialogInterface.OnClickListener customActionClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int item) {
findAndUpdatePreference(adapter.getItem(item), mHolderTag);
dialog.dismiss();
}
};
dialog = new AlertDialog.Builder(getActivity())
.setTitle(getString(R.string.action_entry_custom_action))
.setAdapter(adapter, customActionClickListener)
.setNegativeButton(getString(android.R.string.cancel), null)
.create();
return dialog;
}
}
return super.onCreateDialog(dialogId);
}
@Override
public int getDialogMetricsCategory(int dialogId) {
switch (dialogId) {
case DIALOG_CATEGORY:
case DIALOG_CUSTOM_ACTIONS:
return MetricsEvent.CHERISH_SETTINGS;
default:
return 0;
}
}
// subclass overrides to include back and home actions
protected boolean usesExtendedActionsList() {
return false;
}
protected void onActionPolicyEnforced(ArrayList<ActionPreference> prefs) {
}
protected void setActionPreferencesEnabled(boolean enabled) {
for (ActionPreference pref : mPrefHolder) {
pref.setEnabled(enabled);
}
}
/**
* load our button lists and ActionPreferences map button action targets from preference keys
* and defaults config maps subclass is required to set desired Defaults interface int
* ActionContants
*/
protected void onPreferenceScreenLoaded(Defaults defaults) {
mDefaults = defaults;
final PreferenceScreen prefScreen = getPreferenceScreen();
for (int i = 0; i < prefScreen.getPreferenceCount(); i++) {
Preference pref = prefScreen.getPreference(i);
if (pref instanceof PreferenceCategory) {
PreferenceCategory cat = (PreferenceCategory) pref;
for (int j = 0; j < cat.getPreferenceCount(); j++) {
Preference child = cat.getPreference(j);
if (child instanceof ActionPreference) {
mPrefHolder.add((ActionPreference) child);
}
}
} else if (pref instanceof ActionPreference) {
mPrefHolder.add((ActionPreference) pref);
}
}
loadAndSetConfigs();
}
protected void loadAndSetConfigs() {
mButtons = Config.getConfig(getActivity(), mDefaults);
mDefaultButtons = Config.getDefaultConfig(getActivity(), mDefaults);
for (ActionPreference pref : mPrefHolder) {
pref.setDefaults(mDefaults);
ButtonConfig button = mButtons.get(pref.getConfigMap().button);
ActionConfig action = button.getActionConfig(pref.getConfigMap().action);
pref.setActionConfig(action);
ButtonConfig defButton = mDefaultButtons.get(pref.getConfigMap().button);
ActionConfig defAction = defButton.getActionConfig(pref.getConfigMap().action);
pref.setDefaultActionConfig(defAction);
}
}
private void onTargetChange(String uri) {
if (uri == null) {
return;
} else if (uri.equals(getString(R.string.action_value_default_action))) {
findAndUpdatePreference(null, mHolderTag);
} else if (uri.equals(getString(R.string.action_value_select_app))) {
mPicker.pickShortcut(null, null, getId());
} else if (uri.equals(getString(R.string.action_value_custom_action))) {
showDialog(DIALOG_CUSTOM_ACTIONS);
}
}
protected void findAndUpdatePreference(ActionConfig action, String tag) {
for (ActionPreference pref : mPrefHolder) {
if (pref.getTag().equals(mHolderTag)) {
if (action == null) {
action = pref.getDefaultActionConfig();
}
pref.setActionConfig(action);
ButtonConfig button = mButtons.get(pref.getConfigMap().button);
ActionConfig newAction = pref.getActionConfig();
button.setActionConfig(newAction, pref.getConfigMap().action);
mButtons = Config.replaceButtonAtPosition(mButtons, button, pref.getConfigMap());
Config.setConfig(getActivity(), mDefaults, mButtons);
onActionPolicyEnforced(mPrefHolder);
break;
}
}
}
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.CHERISH_SETTINGS;
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (C) 2015 TeamEos 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.
*
* Simple preference class implementing ActionHolder interface to assign
* actions to buttons. It is ABSOLUTELY IMPERITIVE that the preference
* key is identical to the target ConfigMap tag in ActionConstants
*/
package com.cherish.settings.preferences;
import java.util.Map;
import com.android.internal.util.hwkeys.ActionConstants.ConfigMap;
import com.android.internal.util.hwkeys.ActionConstants.Defaults;
import com.android.internal.util.hwkeys.ActionHolder;
import com.android.internal.util.hwkeys.Config.ActionConfig;
import com.android.internal.util.hwkeys.Config.ButtonConfig;
import android.content.Context;
import androidx.preference.Preference;
import android.util.AttributeSet;
public class ActionPreference extends Preference implements ActionHolder {
private Defaults mDefaults;
private ConfigMap mMap;
private ActionConfig mAction;
private ActionConfig mDefaultAction;
public ActionPreference(Context context) {
this(context, null);
}
public ActionPreference(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ActionPreference(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public ActionPreference(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs);
}
@Override
public String getTag() {
return this.getKey();
}
@Override
public void setTag(String tag) {
this.setKey(tag);
}
@Override
public Defaults getDefaults() {
return mDefaults;
}
@Override
public void setDefaults(Defaults defaults) {
mDefaults = defaults;
final String tag = this.getKey();
for (Map.Entry<String, ConfigMap> entry : defaults.getActionMap().entrySet()) {
if (((String) entry.getKey()).equals(tag)) {
mMap = entry.getValue();
break;
}
}
}
@Override
public ConfigMap getConfigMap() {
return mMap;
}
@Override
public void setConfigMap(ConfigMap map) {
mMap = map;
}
@Override
public ButtonConfig getButtonConfig() {
return null;
}
@Override
public void setButtonConfig(ButtonConfig button) {
}
@Override
public ActionConfig getActionConfig() {
return mAction;
}
@Override
public void setActionConfig(ActionConfig action) {
mAction = action;
this.setSummary(action.getLabel());
}
@Override
public ButtonConfig getDefaultButtonConfig() {
return null;
}
@Override
public void setDefaultButtonConfig(ButtonConfig button) {
}
@Override
public ActionConfig getDefaultActionConfig() {
return mDefaultAction;
}
@Override
public void setDefaultActionConfig(ActionConfig action) {
mDefaultAction = action;
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) 2015 TeamEos 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.
*
* Adapter for displaying custom actions in a list
*/
package com.cherish.settings.preferences;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.internal.util.hwkeys.ActionHandler;
import com.android.internal.util.hwkeys.Config.ActionConfig;
import com.android.settings.R;
public class CustomActionListAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private Context mContext;
private List<ActionConfig> mCustomActions = new ArrayList<ActionConfig>();
public CustomActionListAdapter(Context context) {
mContext = context;
mInflater = LayoutInflater.from(context);
reloadActions();
}
private void reloadActions() {
mCustomActions.clear();
mCustomActions.addAll(ActionHandler.getSystemActions(mContext));
notifyDataSetChanged();
}
public void removeAction(String action) {
int index = -1;
for (int i = 0; i < mCustomActions.size(); i++) {
if (TextUtils.equals(mCustomActions.get(i).getAction(), action)) {
index = i;
break;
}
}
if (index > -1) {
mCustomActions.remove(index);
notifyDataSetChanged();
}
}
@Override
public int getCount() {
return mCustomActions.size();
}
@Override
public ActionConfig getItem(int position) {
return mCustomActions.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
final Context ctx = mContext;
if (convertView != null) {
holder = (ViewHolder) convertView.getTag();
} else {
convertView = mInflater.inflate(R.layout.custom_action_item, null, false);
holder = new ViewHolder();
convertView.setTag(holder);
holder.title = (TextView) convertView.findViewById(com.android.internal.R.id.title);
holder.summary = (TextView) convertView
.findViewById(com.android.internal.R.id.summary);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) holder.icon.getLayoutParams();
params.width = holder.icon.getMaxWidth();
params.height = holder.icon.getMaxHeight();
holder.icon.setLayoutParams(params);
holder.icon.setScaleType(ScaleType.CENTER);
holder.icon.setCropToPadding(true);
}
ActionConfig config = getItem(position);
holder.title.setText(config.getLabel());
holder.icon.setBackgroundResource(R.drawable.fab_accent);
holder.icon.setImageDrawable(config.getDefaultIcon(ctx));
holder.summary.setVisibility(View.GONE);
return convertView;
}
private static class ViewHolder {
TextView title;
TextView summary;
ImageView icon;
}
}

View File

@@ -0,0 +1,327 @@
/*
* Copyright (C) 2011 The CyanogenMod 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.cherish.settings.preferences;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Intent.ShortcutIconResource;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.TextView;
import com.android.settings.R;
import com.cherish.settings.preferences.ShortcutPickHelper.AppExpandableAdapter.GroupInfo;
public class ShortcutPickHelper {
private FragmentActivity mParent;
private AlertDialog mAlertDialog;
private OnPickListener mListener;
private PackageManager mPackageManager;
private static final int REQUEST_PICK_SHORTCUT = 100;
private static final int REQUEST_PICK_APPLICATION = 101;
private static final int REQUEST_CREATE_SHORTCUT = 102;
private int lastFragmentId;
public interface OnPickListener {
void shortcutPicked(String uri, String friendlyName, boolean isApplication);
}
public ShortcutPickHelper(FragmentActivity parent, OnPickListener listener) {
mParent = parent;
mPackageManager = mParent.getPackageManager();
mListener = listener;
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == FragmentActivity.RESULT_OK) {
switch (requestCode) {
case REQUEST_PICK_APPLICATION:
completeSetCustomApp(data);
break;
case REQUEST_CREATE_SHORTCUT:
completeSetCustomShortcut(data);
break;
case REQUEST_PICK_SHORTCUT:
processShortcut(data, REQUEST_PICK_APPLICATION, REQUEST_CREATE_SHORTCUT);
break;
}
}
}
public void pickShortcut(String[] names, ShortcutIconResource[] icons, int fragmentId) {
Bundle bundle = new Bundle();
ArrayList<String> shortcutNames = new ArrayList<String>();
if (names != null) {
for (String s : names) {
shortcutNames.add(s);
}
}
shortcutNames.add(mParent.getString(R.string.profile_applist_title));
shortcutNames.add(mParent.getString(R.string.picker_activities));
bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames);
ArrayList<ShortcutIconResource> shortcutIcons = new ArrayList<ShortcutIconResource>();
if (icons != null) {
for (ShortcutIconResource s : icons) {
shortcutIcons.add(s);
}
}
shortcutIcons.add(ShortcutIconResource.fromContext(mParent, android.R.drawable.sym_def_app_icon));
shortcutIcons.add(ShortcutIconResource.fromContext(mParent, R.drawable.activities_icon));
bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons);
Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
pickIntent.putExtra(Intent.EXTRA_INTENT, new Intent(Intent.ACTION_CREATE_SHORTCUT));
pickIntent.putExtra(Intent.EXTRA_TITLE, mParent.getText(R.string.select_custom_app_title));
pickIntent.putExtras(bundle);
lastFragmentId = fragmentId;
startFragmentOrActivity(pickIntent, REQUEST_PICK_SHORTCUT);
}
private void startFragmentOrActivity(Intent pickIntent, int requestCode) {
if (lastFragmentId == 0) {
mParent.startActivityForResult(pickIntent, requestCode);
} else {
Fragment cFrag = mParent.getSupportFragmentManager().findFragmentById(lastFragmentId);
if (cFrag != null) {
mParent.startActivityFromFragment(cFrag, pickIntent, requestCode);
}
}
}
private void processShortcut(final Intent intent, int requestCodeApplication, int requestCodeShortcut) {
// Handle case where user selected "Applications"
String applicationName = mParent.getString(R.string.profile_applist_title);
String application2name = mParent.getString(R.string.picker_activities);
String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
if (applicationName != null && applicationName.equals(shortcutName)) {
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
startFragmentOrActivity(pickIntent, requestCodeApplication);
} else if (application2name != null && application2name.equals(shortcutName)){
final List<PackageInfo> pInfos = mPackageManager.getInstalledPackages(PackageManager.GET_ACTIVITIES);
ExpandableListView appListView = new ExpandableListView(mParent);
AppExpandableAdapter appAdapter = new AppExpandableAdapter(pInfos, mParent);
appListView.setAdapter(appAdapter);
appListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id) {
Intent shortIntent = new Intent(Intent.ACTION_MAIN);
String pkgName = ((GroupInfo)parent.getExpandableListAdapter().getGroup(groupPosition))
.info.packageName;
String actName = ((GroupInfo)parent.getExpandableListAdapter().getGroup(groupPosition))
.info.activities[childPosition].name;
shortIntent.setClassName(pkgName, actName);
completeSetCustomApp(shortIntent);
mAlertDialog.dismiss();
return true;
}
});
Builder builder = new Builder(mParent);
builder.setView(appListView);
mAlertDialog = builder.create();
mAlertDialog.setTitle(mParent.getString(R.string.select_custom_activity_title));
mAlertDialog.show();
mAlertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
mListener.shortcutPicked(null, null, false);
}
});
} else {
startFragmentOrActivity(intent, requestCodeShortcut);
}
}
public class AppExpandableAdapter extends BaseExpandableListAdapter {
ArrayList<GroupInfo> allList = new ArrayList<GroupInfo>();
final int groupPadding;
public class LabelCompare implements Comparator<GroupInfo>{
@Override
public int compare(GroupInfo item1, GroupInfo item2) {
String rank1 = item1.label.toLowerCase();
String rank2 = item2.label.toLowerCase();
int result = rank1.compareTo(rank2);
if(result == 0) {
return 0;
} else if(result < 0) {
return -1;
} else {
return +1;
}
}
}
class GroupInfo {
String label;
PackageInfo info;
GroupInfo (String l, PackageInfo p) {
label = l;
info = p;
}
}
public AppExpandableAdapter(List<PackageInfo> pInfos, Context context) {
for (PackageInfo i : pInfos) {
allList.add(new GroupInfo(i.applicationInfo.loadLabel(mPackageManager).toString(), i));
}
Collections.sort(allList, new LabelCompare());
groupPadding = context.getResources().getDimensionPixelSize(R.dimen.shortcut_picker_left_padding);
}
public String getChild(int groupPosition, int childPosition) {
return allList.get(groupPosition).info.activities[childPosition].name;
}
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
public int getChildrenCount(int groupPosition) {
if (allList.get(groupPosition).info.activities != null) {
return allList.get(groupPosition).info.activities.length;
} else {
return 0;
}
}
public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = View.inflate(mParent, android.R.layout.simple_list_item_1, null);
convertView.setPadding(groupPadding, 0, 0, 0);
}
TextView textView = (TextView)convertView.findViewById(android.R.id.text1);
textView.setText(getChild(groupPosition, childPosition).replaceFirst(allList.get(groupPosition).info.packageName + ".", ""));
return convertView;
}
public GroupInfo getGroup(int groupPosition) {
return allList.get(groupPosition);
}
public int getGroupCount() {
return allList.size();
}
public long getGroupId(int groupPosition) {
return groupPosition;
}
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) {
if (convertView == null) {
convertView = View.inflate(mParent, android.R.layout.simple_list_item_1, null);
convertView.setPadding(70, 0, 0, 0);
}
TextView textView = (TextView)convertView.findViewById(android.R.id.text1);
textView.setText(getGroup(groupPosition).label.toString());
return convertView;
}
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
public boolean hasStableIds() {
return true;
}
}
private void completeSetCustomApp(Intent data) {
mListener.shortcutPicked(data.toUri(0), getFriendlyActivityName(data, false), true);
}
private void completeSetCustomShortcut(Intent data) {
Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
if (intent == null) return;
// preserve shortcut name, we want to restore it later
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME));
// standardize the uri scheme to be sure we can launch it
// TODO: add compatibility for apps that use ACTION_CONFIRM_PIN_SHORTCUT (drag widget on the home screen)
String appUri = intent.toUri(Intent.URI_INTENT_SCHEME);
appUri = appUri.replaceAll("com.android.contacts.action.QUICK_CONTACT", "android.intent.action.VIEW");
mListener.shortcutPicked(appUri, getFriendlyShortcutName(intent), false);
}
private String getFriendlyActivityName(Intent intent, boolean labelOnly) {
ActivityInfo ai = intent.resolveActivityInfo(mPackageManager, PackageManager.GET_ACTIVITIES);
String friendlyName = null;
if (ai != null) {
friendlyName = ai.loadLabel(mPackageManager).toString();
if (friendlyName == null && !labelOnly) {
friendlyName = ai.name;
}
}
return friendlyName != null || labelOnly ? friendlyName : intent.toUri(0);
}
private String getFriendlyShortcutName(Intent intent) {
String activityName = getFriendlyActivityName(intent, true);
String name = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
if (activityName != null && name != null) {
return activityName + ": " + name;
}
return name != null ? name : intent.toUri(0);
}
public String getFriendlyNameForUri(String uri) {
if (uri == null) {
return null;
}
try {
Intent intent = Intent.parseUri(uri, 0);
if (Intent.ACTION_MAIN.equals(intent.getAction())) {
return getFriendlyActivityName(intent, false);
}
return getFriendlyShortcutName(intent);
} catch (URISyntaxException e) {
}
return uri;
}
}