OmniGears: add single and multi app select preference
Change-Id: I634ff9c937a19ffb2f341aa05fc6bf8d34039cf1
diff --git a/res/layout/app_select_item.xml b/res/layout/app_select_item.xml
new file mode 100644
index 0000000..57a48f9
--- /dev/null
+++ b/res/layout/app_select_item.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The OmniROM Project
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingTop="8dip"
+ android:paddingBottom="8dip"
+ android:orientation="horizontal" >
+
+ <ImageView
+ android:id="@+id/app_icon"
+ android:layout_width="@android:dimen/app_icon_size"
+ android:layout_height="@android:dimen/app_icon_size"
+ android:layout_marginEnd="8dip"
+ android:layout_gravity="center_vertical"
+ android:scaleType="centerInside"
+ android:contentDescription="@null" />
+
+ <TextView
+ android:id="@+id/app_name"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:focusable="false"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:textAlignment="viewStart" />
+
+ <CheckBox android:id="@android:id/checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="4dip"
+ android:layout_gravity="center_vertical"
+ android:clickable="false"
+ android:focusable="false" />
+
+</LinearLayout>
+
diff --git a/res/layout/preference_app_list.xml b/res/layout/preference_app_list.xml
new file mode 100644
index 0000000..449dcf5
--- /dev/null
+++ b/res/layout/preference_app_list.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The OmniROM Project
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingStart="@dimen/alert_dialog_padding_material"
+ android:paddingEnd="@dimen/alert_dialog_padding_material" >
+ <ListView
+ android:id="@+id/app_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="6dip"
+ android:layout_marginTop="6dip" />
+</LinearLayout>
diff --git a/res/layout/preference_font_picker.xml b/res/layout/preference_font_picker.xml
index d41365f..030c57b 100644
--- a/res/layout/preference_font_picker.xml
+++ b/res/layout/preference_font_picker.xml
@@ -1,3 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The OmniROM Project
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/src/org/omnirom/omnigears/preference/AppMultiSelectListPreference.java b/src/org/omnirom/omnigears/preference/AppMultiSelectListPreference.java
new file mode 100644
index 0000000..90efb74
--- /dev/null
+++ b/src/org/omnirom/omnigears/preference/AppMultiSelectListPreference.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2016 The OmniROM Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+package org.omnirom.omnigears.preference;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.preference.DialogPreference;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class AppMultiSelectListPreference extends DialogPreference {
+ private final List<MyApplicationInfo> mPackageInfoList = new ArrayList<MyApplicationInfo>();
+ private AppListAdapter mAdapter;
+ private Set<String> mValues = new HashSet<String>();
+
+ public AppMultiSelectListPreference(Context context) {
+ this(context, null);
+ }
+
+ public AppMultiSelectListPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ setDialogLayoutResource(R.layout.preference_app_list);
+
+ final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+ mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ List<ResolveInfo> installedAppsInfo = getContext().getPackageManager().queryIntentActivities(
+ mainIntent, 0);
+
+ for (ResolveInfo info : installedAppsInfo) {
+ MyApplicationInfo myInfo = new MyApplicationInfo();
+ myInfo.resolveInfo = info;
+ myInfo.label = getResolveInfoTitle(info);
+ mPackageInfoList.add(myInfo);
+ }
+ Collections.sort(mPackageInfoList, sDisplayNameComparator);
+ }
+
+ public void setValues(Set<String> values) {
+ mValues.clear();
+ mValues.addAll(values);
+ }
+
+ public Set<String> getValues() {
+ return mValues;
+ }
+
+ @Override
+ protected void onBindDialogView(View view) {
+ super.onBindDialogView(view);
+
+ mAdapter = new AppListAdapter(getContext());
+ final ListView listView = (ListView) view.findViewById(R.id.app_list);
+ listView.setAdapter(mAdapter);
+ listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ final AppViewHolder holder = (AppViewHolder) view.getTag();
+ final boolean isChecked = !holder.checkBox.isChecked();
+ holder.checkBox.setChecked(isChecked);
+
+ MyApplicationInfo myInfo = mAdapter.getItem(position);
+ ResolveInfo info = myInfo.resolveInfo;
+ Intent intent = getIntentForResolveInfo(info, Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+ String value = intent.toUri(0).toString();
+ if (isChecked) {
+ mValues.add(value);
+ } else {
+ mValues.remove(value);
+ }
+ }
+ });
+ }
+
+ @Override
+ protected void onDialogClosed(boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+ callChangeListener(null);
+ }
+
+ private String getResolveInfoTitle(ResolveInfo info) {
+ CharSequence label = info.loadLabel(getContext().getPackageManager());
+ if (label == null) label = info.activityInfo.name;
+ return label != null ? label.toString() : null;
+ }
+
+ private Intent getIntentForResolveInfo(ResolveInfo info, String action) {
+ Intent intent = new Intent(action);
+ ActivityInfo ai = info.activityInfo;
+ intent.setClassName(ai.packageName, ai.name);
+ return intent;
+ }
+
+ class MyApplicationInfo {
+ ApplicationInfo info;
+ CharSequence label;
+ ResolveInfo resolveInfo;
+ }
+
+ public class AppListAdapter extends ArrayAdapter<MyApplicationInfo> {
+ private final LayoutInflater mInflater;
+
+ public AppListAdapter(Context context) {
+ super(context, 0);
+ mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ addAll(mPackageInfoList);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // A ViewHolder keeps references to children views to avoid unnecessary calls
+ // to findViewById() on each row.
+ AppViewHolder holder = AppViewHolder.createOrRecycle(mInflater, convertView);
+ convertView = holder.rootView;
+ MyApplicationInfo info = getItem(position);
+ holder.appName.setText(info.label);
+ Drawable icon = info.resolveInfo.loadIcon(getContext().getPackageManager());
+ if (icon != null) {
+ holder.appIcon.setImageDrawable(icon);
+ } else {
+ holder.appIcon.setImageDrawable(null);
+ }
+
+ Intent intent = getIntentForResolveInfo(info.resolveInfo, Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ String value = intent.toUri(0).toString();
+
+ holder.checkBox.setChecked(mValues.contains(value));
+ return convertView;
+ }
+
+ @Override
+ public MyApplicationInfo getItem(int position) {
+ return mPackageInfoList.get(position);
+ }
+ }
+
+ public static class AppViewHolder {
+ public View rootView;
+ public TextView appName;
+ public ImageView appIcon;
+ public CheckBox checkBox;
+
+ public static AppViewHolder createOrRecycle(LayoutInflater inflater, View convertView) {
+ if (convertView == null) {
+ convertView = inflater.inflate(R.layout.app_select_item, null);
+
+ // Creates a ViewHolder and store references to the two children views
+ // we want to bind data to.
+ AppViewHolder holder = new AppViewHolder();
+ holder.rootView = convertView;
+ holder.appName = (TextView) convertView.findViewById(R.id.app_name);
+ holder.appIcon = (ImageView) convertView.findViewById(R.id.app_icon);
+ holder.checkBox = (CheckBox) convertView.findViewById(android.R.id.checkbox);
+ convertView.setTag(holder);
+ return holder;
+ } else {
+ // Get the ViewHolder back to get fast access to the TextView
+ // and the ImageView.
+ return (AppViewHolder)convertView.getTag();
+ }
+ }
+ }
+
+ private final static Comparator<MyApplicationInfo> sDisplayNameComparator
+ = new Comparator<MyApplicationInfo>() {
+
+ private final Collator collator = Collator.getInstance();
+
+ public final int compare(MyApplicationInfo a, MyApplicationInfo b) {
+ return collator.compare(a.label, b.label);
+ }
+ };
+}
+
diff --git a/src/org/omnirom/omnigears/preference/AppSelectListPreference.java b/src/org/omnirom/omnigears/preference/AppSelectListPreference.java
new file mode 100644
index 0000000..ff2ce2f
--- /dev/null
+++ b/src/org/omnirom/omnigears/preference/AppSelectListPreference.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2016 The OmniROM Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+package org.omnirom.omnigears.preference;
+
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.preference.DialogPreference;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class AppSelectListPreference extends DialogPreference {
+ private final List<MyApplicationInfo> mPackageInfoList = new ArrayList<MyApplicationInfo>();
+ private AppListAdapter mAdapter;
+ private String mReturnValue;
+
+ public AppSelectListPreference(Context context) {
+ this(context, null);
+ }
+
+ public AppSelectListPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ setDialogLayoutResource(R.layout.preference_app_list);
+
+ final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+ mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ List<ResolveInfo> installedAppsInfo = getContext().getPackageManager().queryIntentActivities(
+ mainIntent, 0);
+
+ for (ResolveInfo info : installedAppsInfo) {
+ MyApplicationInfo myInfo = new MyApplicationInfo();
+ myInfo.resolveInfo = info;
+ myInfo.label = getResolveInfoTitle(info);
+ mPackageInfoList.add(myInfo);
+ }
+ Collections.sort(mPackageInfoList, sDisplayNameComparator);
+ }
+
+ public String getValue() {
+ return mReturnValue;
+ }
+
+ @Override
+ protected void onBindDialogView(View view) {
+ super.onBindDialogView(view);
+
+ mAdapter = new AppListAdapter(getContext());
+ final ListView listView = (ListView) view.findViewById(R.id.app_list);
+ listView.setAdapter(mAdapter);
+ listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ final AppViewHolder holder = (AppViewHolder) view.getTag();
+
+ MyApplicationInfo myInfo = mAdapter.getItem(position);
+ ResolveInfo info = myInfo.resolveInfo;
+ Intent intent = getIntentForResolveInfo(info, Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+ mReturnValue = intent.toUri(0).toString();
+ AppSelectListPreference.this.onClick(getDialog(), DialogInterface.BUTTON_POSITIVE);
+ getDialog().dismiss();
+ }
+ });
+ }
+
+ @Override
+ protected void onPrepareDialogBuilder(Builder builder) {
+ super.onPrepareDialogBuilder(builder);
+ builder.setPositiveButton(null, null);
+ }
+
+ @Override
+ protected void onDialogClosed(boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+ callChangeListener(mReturnValue);
+ }
+
+ private String getResolveInfoTitle(ResolveInfo info) {
+ CharSequence label = info.loadLabel(getContext().getPackageManager());
+ if (label == null) label = info.activityInfo.name;
+ return label != null ? label.toString() : null;
+ }
+
+ private Intent getIntentForResolveInfo(ResolveInfo info, String action) {
+ Intent intent = new Intent(action);
+ ActivityInfo ai = info.activityInfo;
+ intent.setClassName(ai.packageName, ai.name);
+ return intent;
+ }
+
+ class MyApplicationInfo {
+ ApplicationInfo info;
+ CharSequence label;
+ ResolveInfo resolveInfo;
+ }
+
+ public class AppListAdapter extends ArrayAdapter<MyApplicationInfo> {
+ private final LayoutInflater mInflater;
+
+ public AppListAdapter(Context context) {
+ super(context, 0);
+ mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ addAll(mPackageInfoList);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // A ViewHolder keeps references to children views to avoid unnecessary calls
+ // to findViewById() on each row.
+ AppViewHolder holder = AppViewHolder.createOrRecycle(mInflater, convertView);
+ convertView = holder.rootView;
+ MyApplicationInfo info = getItem(position);
+ holder.appName.setText(info.label);
+ Drawable icon = info.resolveInfo.loadIcon(getContext().getPackageManager());
+ if (icon != null) {
+ holder.appIcon.setImageDrawable(icon);
+ } else {
+ holder.appIcon.setImageDrawable(null);
+ }
+ return convertView;
+ }
+
+ @Override
+ public MyApplicationInfo getItem(int position) {
+ return mPackageInfoList.get(position);
+ }
+ }
+
+ public static class AppViewHolder {
+ public View rootView;
+ public TextView appName;
+ public ImageView appIcon;
+ public CheckBox checkBox;
+
+ public static AppViewHolder createOrRecycle(LayoutInflater inflater, View convertView) {
+ if (convertView == null) {
+ convertView = inflater.inflate(R.layout.app_select_item, null);
+
+ // Creates a ViewHolder and store references to the two children views
+ // we want to bind data to.
+ AppViewHolder holder = new AppViewHolder();
+ holder.rootView = convertView;
+ holder.appName = (TextView) convertView.findViewById(R.id.app_name);
+ holder.appIcon = (ImageView) convertView.findViewById(R.id.app_icon);
+ holder.checkBox = (CheckBox) convertView.findViewById(android.R.id.checkbox);
+ holder.checkBox.setVisibility(View.GONE);
+ convertView.setTag(holder);
+ return holder;
+ } else {
+ // Get the ViewHolder back to get fast access to the TextView
+ // and the ImageView.
+ return (AppViewHolder)convertView.getTag();
+ }
+ }
+ }
+
+ private final static Comparator<MyApplicationInfo> sDisplayNameComparator
+ = new Comparator<MyApplicationInfo>() {
+
+ private final Collator collator = Collator.getInstance();
+
+ public final int compare(MyApplicationInfo a, MyApplicationInfo b) {
+ return collator.compare(a.label, b.label);
+ }
+ };
+}
+