package com.fsck.k9.activity.setup;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.TextView;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.R;
import com.fsck.k9.preferences.StorageEditor;
import com.fsck.k9.ui.dialog.ApgDeprecationWarningDialog;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpAppPreference;
public class OpenPgpAppSelectDialog extends Activity {
private static final String OPENKEYCHAIN_PACKAGE = "org.sufficientlysecure.keychain";
private static final String APG_PROVIDER_PLACEHOLDER = "apg-placeholder";
private static final String PACKAGE_NAME_APG = "org.thialfihar.android.apg";
public static final String FRAG_OPENPGP_SELECT = "openpgp_select";
public static final String FRAG_APG_DEPRECATE = "apg_deprecate";
private static final String MARKET_INTENT_URI_BASE = "market://details?id=%s";
private static final Intent MARKET_INTENT = new Intent(Intent.ACTION_VIEW, Uri.parse(
String.format(MARKET_INTENT_URI_BASE, OPENKEYCHAIN_PACKAGE)));
private static final ArrayList<String> PROVIDER_BLACKLIST = new ArrayList<>();
static {
// Unfortunately, the current released version of APG includes a broken version of the API
PROVIDER_BLACKLIST.add(PACKAGE_NAME_APG);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(K9.getK9Theme() == K9.Theme.LIGHT ?
R.style.Theme_K9_Dialog_Translucent_Light : R.style.Theme_K9_Dialog_Translucent_Dark);
if (savedInstanceState == null) {
showOpenPgpSelectDialogFragment();
}
}
private void showOpenPgpSelectDialogFragment() {
OpenPgpAppSelectFragment fragment = new OpenPgpAppSelectFragment();
fragment.show(getFragmentManager(), FRAG_OPENPGP_SELECT);
}
private void showApgDeprecationDialogFragment() {
ApgDeprecationDialogFragment fragment = new ApgDeprecationDialogFragment();
fragment.show(getFragmentManager(), FRAG_APG_DEPRECATE);
}
public static class OpenPgpAppSelectFragment extends DialogFragment {
private ArrayList<OpenPgpProviderEntry> openPgpProviderList = new ArrayList<>();
private String selectedPackage;
private void populateAppList() {
openPgpProviderList.clear();
Context context = getActivity();
OpenPgpProviderEntry noneEntry = new OpenPgpProviderEntry("",
context.getString(R.string.openpgp_list_preference_none),
getResources().getDrawable(R.drawable.ic_action_cancel_launchersize));
openPgpProviderList.add(0, noneEntry);
if (OpenPgpAppPreference.isApgInstalled(getActivity())) {
Drawable icon = getResources().getDrawable(R.drawable.ic_apg_small);
openPgpProviderList.add(new OpenPgpProviderEntry(
APG_PROVIDER_PLACEHOLDER, getString(R.string.apg), icon));
}
// search for OpenPGP providers...
Intent intent = new Intent(OpenPgpApi.SERVICE_INTENT_2);
List<ResolveInfo> resInfo = getActivity().getPackageManager().queryIntentServices(intent, 0);
boolean hasNonBlacklistedChoices = false;
if (resInfo != null) {
for (ResolveInfo resolveInfo : resInfo) {
if (resolveInfo.serviceInfo == null) {
continue;
}
String packageName = resolveInfo.serviceInfo.packageName;
String simpleName = String.valueOf(resolveInfo.serviceInfo.loadLabel(context.getPackageManager()));
Drawable icon = resolveInfo.serviceInfo.loadIcon(context.getPackageManager());
if (!PROVIDER_BLACKLIST.contains(packageName)) {
openPgpProviderList.add(new OpenPgpProviderEntry(packageName, simpleName, icon));
hasNonBlacklistedChoices = true;
}
}
}
if (!hasNonBlacklistedChoices) {
// add install links if provider list is empty
resInfo = context.getPackageManager().queryIntentActivities(MARKET_INTENT, 0);
for (ResolveInfo resolveInfo : resInfo) {
Intent marketIntent = new Intent(MARKET_INTENT);
marketIntent.setPackage(resolveInfo.activityInfo.packageName);
Drawable icon = resolveInfo.activityInfo.loadIcon(context.getPackageManager());
String marketName = String.valueOf(resolveInfo.activityInfo.applicationInfo
.loadLabel(context.getPackageManager()));
String simpleName = String.format(context.getString(R.string
.openpgp_install_openkeychain_via), marketName);
openPgpProviderList.add(new OpenPgpProviderEntry(OPENKEYCHAIN_PACKAGE, simpleName,
icon, marketIntent));
}
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
selectedPackage = K9.getOpenPgpProvider();
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.account_settings_crypto_app);
// do again, maybe an app has now been installed
populateAppList();
// Init ArrayAdapter with OpenPGP Providers
ListAdapter adapter = new ArrayAdapter<OpenPgpProviderEntry>(getActivity(),
android.R.layout.select_dialog_singlechoice, android.R.id.text1, openPgpProviderList) {
public View getView(int position, View convertView, ViewGroup parent) {
// User super class to create the View
View v = super.getView(position, convertView, parent);
TextView tv = (TextView) v.findViewById(android.R.id.text1);
// Put the image on the TextView
tv.setCompoundDrawablesWithIntrinsicBounds(openPgpProviderList.get(position).icon, null,
null, null);
// Add margin between image and text (support various screen densities)
int dp10 = (int) (10 * getContext().getResources().getDisplayMetrics().density + 0.5f);
tv.setCompoundDrawablePadding(dp10);
return v;
}
};
builder.setSingleChoiceItems(adapter, getIndexOfProviderList(selectedPackage),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
OpenPgpProviderEntry entry = openPgpProviderList.get(which);
if (entry.intent != null) {
/*
* Intents are called as activity
*
* Current approach is to assume the user installed the app.
* If he does not, the selected package is not valid.
*
* However applications should always consider this could happen,
* as the user might remove the currently used OpenPGP app.
*/
getActivity().startActivity(entry.intent);
return;
}
selectedPackage = entry.packageName;
dialog.dismiss();
}
});
return builder.create();
}
private int getIndexOfProviderList(String packageName) {
for (OpenPgpProviderEntry app : openPgpProviderList) {
if (app.packageName.equals(packageName)) {
return openPgpProviderList.indexOf(app);
}
}
// default is "none"
return 0;
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
((OpenPgpAppSelectDialog) getActivity()).onSelectProvider(selectedPackage);
}
}
public static class ApgDeprecationDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new ApgDeprecationWarningDialog(getActivity());
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
((OpenPgpAppSelectDialog) getActivity()).onDismissApgDialog();
}
}
public void onSelectProvider(String selectedPackage) {
if (APG_PROVIDER_PLACEHOLDER.equals(selectedPackage)) {
showApgDeprecationDialogFragment();
return;
}
persistOpenPgpProviderSetting(selectedPackage);
finish();
}
private void persistOpenPgpProviderSetting(String selectedPackage) {
K9.setOpenPgpProvider(selectedPackage);
StorageEditor editor = Preferences.getPreferences(this).getStorage().edit();
K9.save(editor);
editor.commit();
}
public void onDismissApgDialog() {
showOpenPgpSelectDialogFragment();
}
private static class OpenPgpProviderEntry {
private String packageName;
private String simpleName;
private Drawable icon;
private Intent intent;
OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon) {
this.packageName = packageName;
this.simpleName = simpleName;
this.icon = icon;
}
OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon, Intent intent) {
this(packageName, simpleName, icon);
this.intent = intent;
}
@Override
public String toString() {
return simpleName;
}
}
}