From 54a5962b6e48f254fe03862367f6febf5fa86a3b Mon Sep 17 00:00:00 2001 From: bigrushdog Date: Wed, 6 Dec 2017 10:00:00 +0100 Subject: [PATCH] Hardware key: Customization support [2/2] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: mhkjahromi Signed-off-by: SagarMakhar Signed-off-by: Hưng Phan --- res/values/cherish_arrays.xml | 21 ++ res/values/cherish_strings.xml | 33 +- res/xml/cherish_settings_button.xml | 108 ++++++ .../settings/fragments/ButtonSettings.java | 66 ++++ .../settings/preferences/ActionFragment.java | 261 ++++++++++++++ .../preferences/ActionPreference.java | 134 +++++++ .../preferences/CustomActionListAdapter.java | 123 +++++++ .../preferences/ShortcutPickHelper.java | 327 ++++++++++++++++++ 8 files changed, 1072 insertions(+), 1 deletion(-) create mode 100644 src/com/cherish/settings/preferences/ActionFragment.java create mode 100644 src/com/cherish/settings/preferences/ActionPreference.java create mode 100644 src/com/cherish/settings/preferences/CustomActionListAdapter.java create mode 100644 src/com/cherish/settings/preferences/ShortcutPickHelper.java diff --git a/res/values/cherish_arrays.xml b/res/values/cherish_arrays.xml index 7ac789a..f915c77 100644 --- a/res/values/cherish_arrays.xml +++ b/res/values/cherish_arrays.xml @@ -447,4 +447,25 @@ 2 3 + + + + @string/action_entry_default_action + @string/action_entry_custom_action + @string/action_entry_select_app + + + @string/action_value_default_action + @string/action_value_custom_action + @string/action_value_select_app + + + @string/action_entry_custom_action + @string/action_entry_select_app + + + @string/action_value_custom_action + @string/action_value_select_app + + diff --git a/res/values/cherish_strings.xml b/res/values/cherish_strings.xml index ff04c22..7180327 100644 --- a/res/values/cherish_strings.xml +++ b/res/values/cherish_strings.xml @@ -176,7 +176,38 @@ Jump to camera gesture is now disabled Long press power button - + + default + app + custom + task_back + task_home + task_assist + task_recents + task_voice_search + task_menu + Activities + Select custom app + Select custom activity + Applications + Choose action + Default settings + Select application + Select custom action + Volume rocker + Home button + Menu button + Search button + Overview button + Back button + Set double tap action + Set single tap action + Set long press action + Single tap action + Long press action + Double tap action + + 4G icon Display 4G icon in signal icon instead LTE diff --git a/res/xml/cherish_settings_button.xml b/res/xml/cherish_settings_button.xml index e14911d..2092b0b 100644 --- a/res/xml/cherish_settings_button.xml +++ b/res/xml/cherish_settings_button.xml @@ -39,6 +39,114 @@ android:title="@string/volume_steps_fragment_title" android:summary="@string/volume_steps_summary" android:fragment="com.cherish.settings.fragments.VolumeStepsFragment" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + mPrefHolder; + private String mHolderTag; + private Defaults mDefaults; + private ArrayList mButtons; + private ArrayList 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(); + } + + @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 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; + } +} \ No newline at end of file diff --git a/src/com/cherish/settings/preferences/ActionPreference.java b/src/com/cherish/settings/preferences/ActionPreference.java new file mode 100644 index 0000000..67c0a81 --- /dev/null +++ b/src/com/cherish/settings/preferences/ActionPreference.java @@ -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 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; + } +} diff --git a/src/com/cherish/settings/preferences/CustomActionListAdapter.java b/src/com/cherish/settings/preferences/CustomActionListAdapter.java new file mode 100644 index 0000000..dff16e1 --- /dev/null +++ b/src/com/cherish/settings/preferences/CustomActionListAdapter.java @@ -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 mCustomActions = new ArrayList(); + + 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; + } +} diff --git a/src/com/cherish/settings/preferences/ShortcutPickHelper.java b/src/com/cherish/settings/preferences/ShortcutPickHelper.java new file mode 100644 index 0000000..44eaf3e --- /dev/null +++ b/src/com/cherish/settings/preferences/ShortcutPickHelper.java @@ -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 shortcutNames = new ArrayList(); + 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 shortcutIcons = new ArrayList(); + 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 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 allList = new ArrayList(); + final int groupPadding; + + public class LabelCompare implements Comparator{ + @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 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; + } +}