OpenPgpListPreference.java

  1/*
  2 * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
  3 *
  4 * Licensed under the Apache License, Version 2.0 (the "License");
  5 * you may not use this file except in compliance with the License.
  6 * You may obtain a copy of the License at
  7 *
  8 *      http://www.apache.org/licenses/LICENSE-2.0
  9 *
 10 * Unless required by applicable law or agreed to in writing, software
 11 * distributed under the License is distributed on an "AS IS" BASIS,
 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 * See the License for the specific language governing permissions and
 14 * limitations under the License.
 15 */
 16
 17package org.openintents.openpgp.util;
 18
 19import android.app.AlertDialog.Builder;
 20import android.content.Context;
 21import android.content.DialogInterface;
 22import android.content.Intent;
 23import android.content.pm.ResolveInfo;
 24import android.content.res.TypedArray;
 25import android.graphics.drawable.Drawable;
 26import android.net.Uri;
 27import android.preference.DialogPreference;
 28import android.util.AttributeSet;
 29import android.view.View;
 30import android.view.ViewGroup;
 31import android.widget.ArrayAdapter;
 32import android.widget.ListAdapter;
 33import android.widget.TextView;
 34import org.openintents.openpgp.R;
 35
 36import java.util.ArrayList;
 37import java.util.List;
 38
 39/**
 40 * Does not extend ListPreference, but is very similar to it!
 41 * http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/preference/ListPreference.java/?v=source
 42 */
 43public class OpenPgpListPreference extends DialogPreference {
 44    private static final String OPENKEYCHAIN_PACKAGE = "org.sufficientlysecure.keychain";
 45    private static final String MARKET_INTENT_URI_BASE = "market://details?id=%s";
 46    private static final Intent MARKET_INTENT = new Intent(Intent.ACTION_VIEW, Uri.parse(
 47            String.format(MARKET_INTENT_URI_BASE, OPENKEYCHAIN_PACKAGE)));
 48
 49    private ArrayList<OpenPgpProviderEntry> mLegacyList = new ArrayList<OpenPgpProviderEntry>();
 50    private ArrayList<OpenPgpProviderEntry> mList = new ArrayList<OpenPgpProviderEntry>();
 51
 52    private String mSelectedPackage;
 53
 54    public OpenPgpListPreference(Context context, AttributeSet attrs) {
 55        super(context, attrs);
 56    }
 57
 58    public OpenPgpListPreference(Context context) {
 59        this(context, null);
 60    }
 61
 62    /**
 63     * Public method to add new entries for legacy applications
 64     *
 65     * @param packageName
 66     * @param simpleName
 67     * @param icon
 68     */
 69    public void addLegacyProvider(int position, String packageName, String simpleName, Drawable icon) {
 70        mLegacyList.add(position, new OpenPgpProviderEntry(packageName, simpleName, icon));
 71    }
 72
 73    @Override
 74    protected void onPrepareDialogBuilder(Builder builder) {
 75        mList.clear();
 76        
 77        // add "none"-entry
 78        mList.add(0, new OpenPgpProviderEntry("",
 79                getContext().getString(R.string.openpgp_list_preference_none),
 80                getContext().getResources().getDrawable(R.drawable.ic_action_cancel_launchersize)));
 81        
 82        // add all additional (legacy) providers
 83        mList.addAll(mLegacyList);
 84        
 85        // search for OpenPGP providers...
 86        ArrayList<OpenPgpProviderEntry> providerList = new ArrayList<OpenPgpProviderEntry>();
 87        Intent intent = new Intent(OpenPgpApi.SERVICE_INTENT);
 88        List<ResolveInfo> resInfo = getContext().getPackageManager().queryIntentServices(intent, 0);
 89        if (!resInfo.isEmpty()) {
 90            for (ResolveInfo resolveInfo : resInfo) {
 91                if (resolveInfo.serviceInfo == null)
 92                    continue;
 93
 94                String packageName = resolveInfo.serviceInfo.packageName;
 95                String simpleName = String.valueOf(resolveInfo.serviceInfo.loadLabel(getContext()
 96                        .getPackageManager()));
 97                Drawable icon = resolveInfo.serviceInfo.loadIcon(getContext().getPackageManager());
 98
 99                providerList.add(new OpenPgpProviderEntry(packageName, simpleName, icon));
100            }
101        }
102
103        if (providerList.isEmpty()) {
104            // add install links if provider list is empty
105            resInfo = getContext().getPackageManager().queryIntentActivities
106                    (MARKET_INTENT, 0);
107            for (ResolveInfo resolveInfo : resInfo) {
108                Intent marketIntent = new Intent(MARKET_INTENT);
109                marketIntent.setPackage(resolveInfo.activityInfo.packageName);
110                Drawable icon = resolveInfo.activityInfo.loadIcon(getContext().getPackageManager());
111                String marketName = String.valueOf(resolveInfo.activityInfo.applicationInfo
112                        .loadLabel(getContext().getPackageManager()));
113                String simpleName = String.format(getContext().getString(R.string
114                        .openpgp_install_openkeychain_via), marketName);
115                mList.add(new OpenPgpProviderEntry(OPENKEYCHAIN_PACKAGE, simpleName,
116                        icon, marketIntent));
117            }
118        } else {
119            // add provider
120            mList.addAll(providerList);
121        }
122
123        // Init ArrayAdapter with OpenPGP Providers
124        ListAdapter adapter = new ArrayAdapter<OpenPgpProviderEntry>(getContext(),
125                android.R.layout.select_dialog_singlechoice, android.R.id.text1, mList) {
126            public View getView(int position, View convertView, ViewGroup parent) {
127                // User super class to create the View
128                View v = super.getView(position, convertView, parent);
129                TextView tv = (TextView) v.findViewById(android.R.id.text1);
130
131                // Put the image on the TextView
132                tv.setCompoundDrawablesWithIntrinsicBounds(mList.get(position).icon, null,
133                        null, null);
134
135                // Add margin between image and text (support various screen densities)
136                int dp10 = (int) (10 * getContext().getResources().getDisplayMetrics().density + 0.5f);
137                tv.setCompoundDrawablePadding(dp10);
138
139                return v;
140            }
141        };
142
143        builder.setSingleChoiceItems(adapter, getIndexOfProviderList(getValue()),
144                new DialogInterface.OnClickListener() {
145
146                    @Override
147                    public void onClick(DialogInterface dialog, int which) {
148                        OpenPgpProviderEntry entry = mList.get(which);
149
150                        if (entry.intent != null) {
151                            /*
152                             * Intents are called as activity
153                             *
154                             * Current approach is to assume the user installed the app.
155                             * If he does not, the selected package is not valid.
156                             *
157                             * However  applications should always consider this could happen,
158                             * as the user might remove the currently used OpenPGP app.
159                             */
160                            getContext().startActivity(entry.intent);
161                        }
162
163                        mSelectedPackage = entry.packageName;
164
165                        /*
166                         * Clicking on an item simulates the positive button click, and dismisses
167                         * the dialog.
168                         */
169                        OpenPgpListPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
170                        dialog.dismiss();
171                    }
172                });
173
174        /*
175         * The typical interaction for list-based dialogs is to have click-on-an-item dismiss the
176         * dialog instead of the user having to press 'Ok'.
177         */
178        builder.setPositiveButton(null, null);
179    }
180
181    @Override
182    protected void onDialogClosed(boolean positiveResult) {
183        super.onDialogClosed(positiveResult);
184
185        if (positiveResult && (mSelectedPackage != null)) {
186            if (callChangeListener(mSelectedPackage)) {
187                setValue(mSelectedPackage);
188            }
189        }
190    }
191
192    private int getIndexOfProviderList(String packageName) {
193        for (OpenPgpProviderEntry app : mList) {
194            if (app.packageName.equals(packageName)) {
195                return mList.indexOf(app);
196            }
197        }
198
199        return -1;
200    }
201
202    public void setValue(String packageName) {
203        mSelectedPackage = packageName;
204        persistString(packageName);
205    }
206
207    public String getValue() {
208        return mSelectedPackage;
209    }
210
211    public String getEntry() {
212        return getEntryByValue(mSelectedPackage);
213    }
214
215    @Override
216    protected Object onGetDefaultValue(TypedArray a, int index) {
217        return a.getString(index);
218    }
219
220    @Override
221    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
222        setValue(restoreValue ? getPersistedString(mSelectedPackage) : (String) defaultValue);
223    }
224
225    public String getEntryByValue(String packageName) {
226        for (OpenPgpProviderEntry app : mList) {
227            if (app.packageName.equals(packageName)) {
228                return app.simpleName;
229            }
230        }
231
232        return null;
233    }
234
235    private static class OpenPgpProviderEntry {
236        private String packageName;
237        private String simpleName;
238        private Drawable icon;
239        private Intent intent;
240
241        public OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon) {
242            this.packageName = packageName;
243            this.simpleName = simpleName;
244            this.icon = icon;
245        }
246
247        public OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon, Intent intent) {
248            this(packageName, simpleName, icon);
249            this.intent = intent;
250        }
251
252        @Override
253        public String toString() {
254            return simpleName;
255        }
256    }
257}