diff options
author | Tim Bray <timbray@gmail.com> | 2014-09-12 08:34:51 -0700 |
---|---|---|
committer | Tim Bray <timbray@gmail.com> | 2014-09-13 21:41:03 -0700 |
commit | 1c32d1df8801968ea6423ee4f1a9160e0c4ff0c3 (patch) | |
tree | 6571fc5b6aca0de6bd23e9acda38caf16d81cc30 | |
parent | 36ef8a9a62f32f0843c9e317d493a7f136624dce (diff) | |
download | open-keychain-1c32d1df8801968ea6423ee4f1a9160e0c4ff0c3.tar.gz open-keychain-1c32d1df8801968ea6423ee4f1a9160e0c4ff0c3.tar.bz2 open-keychain-1c32d1df8801968ea6423ee4f1a9160e0c4ff0c3.zip |
Add cloud search tab, lose Keybase/Keyserver tabs, re-organize prefs
26 files changed, 501 insertions, 527 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index db78a36c1..8ee769fdf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -81,6 +81,8 @@ public final class Constants { public static final String CACHED_CONSOLIDATE = "cachedConsolidate"; public static final String CACHED_CONSOLIDATE_SECRETS = "cachedConsolidateSecrets"; public static final String CACHED_CONSOLIDATE_PUBLICS = "cachedConsolidatePublics"; + public static final String SEARCH_KEYSERVER = "search_keyserver_pref"; + public static final String SEARCH_KEYBASE = "search_keybase_pref"; public static final String USE_DEFAULT_YUBIKEY_PIN = "useDefaultYubikeyPin"; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/EmailKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/EmailKeyHelper.java index d8efdc480..91325ca1f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/EmailKeyHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/EmailKeyHelper.java @@ -61,9 +61,9 @@ public class EmailKeyHelper { if (keys.isEmpty()) { // Most users don't have the SRV record, so ask a default server as well - String[] servers = Preferences.getPreferences(context).getKeyServers(); - if (servers != null && servers.length != 0) { - HkpKeyserver hkp = new HkpKeyserver(servers[0]); + String server = Preferences.getPreferences(context).getPreferredKeyserver(); + if (server != null) { + HkpKeyserver hkp = new HkpKeyserver(server); keys.addAll(getEmailKeys(mail, hkp)); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java index aa4621893..bf09997a0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java @@ -220,6 +220,9 @@ public class Preferences { } return servers.toArray(chunks); } + public String getPreferredKeyserver() { + return getKeyServers()[0]; + } public void setKeyServers(String[] value) { SharedPreferences.Editor editor = mSharedPreferences.edit(); @@ -248,6 +251,35 @@ public class Preferences { return mSharedPreferences.getBoolean(Constants.Pref.WRITE_VERSION_HEADER, false); } + public void setSearchKeyserver(boolean searchKeyserver) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putBoolean(Pref.SEARCH_KEYSERVER, searchKeyserver); + editor.commit(); + } + public void setSearchKeybase(boolean searchKeybase) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putBoolean(Pref.SEARCH_KEYBASE, searchKeybase); + editor.commit(); + } + + public CloudSearchPrefs getCloudSearchPrefs() { + return new CloudSearchPrefs(mSharedPreferences.getBoolean(Pref.SEARCH_KEYSERVER, true), + mSharedPreferences.getBoolean(Pref.SEARCH_KEYBASE, true), + getPreferredKeyserver()); + } + + public static class CloudSearchPrefs { + public final boolean searchKeyserver; + public final boolean searchKeybase; + public final String keyserver; + + public CloudSearchPrefs(boolean searchKeyserver, boolean searchKeybase, String keyserver) { + this.searchKeyserver = searchKeyserver; + this.searchKeybase = searchKeybase; + this.keyserver = keyserver; + } + } + public void updatePreferences() { if (mSharedPreferences.getInt(Constants.Pref.PREF_DEFAULT_VERSION, 0) != Constants.Defaults.PREF_VERSION) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java new file mode 100644 index 000000000..27d826c59 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2014 Tim Bray <tbray@textuality.com> + * + * 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 3 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.sufficientlysecure.keychain.keyimport; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.util.Log; + +import java.util.ArrayList; +import java.util.Vector; + +/** + * Search two or more types of server for online keys. + */ +public class CloudSearch { + + private final static long SECONDS = 1000; + + public static ArrayList<ImportKeysListEntry> search(final String query, Preferences.CloudSearchPrefs cloudPrefs) + throws Keyserver.CloudSearchFailureException { + final ArrayList<Keyserver> servers = new ArrayList<Keyserver>(); + + // it's a Vector for sync, multiple threads might report problems + final Vector<Keyserver.CloudSearchFailureException> problems = new Vector<Keyserver.CloudSearchFailureException>(); + + if (cloudPrefs.searchKeyserver) { + servers.add(new HkpKeyserver(cloudPrefs.keyserver)); + } + if (cloudPrefs.searchKeybase) { + servers.add(new KeybaseKeyserver()); + } + final ImportKeysList results = new ImportKeysList(servers.size()); + + for (final Keyserver keyserver : servers) { + Runnable r = new Runnable() { + @Override + public void run() { + try { + results.addAll(keyserver.search(query)); + } catch (Keyserver.CloudSearchFailureException e) { + problems.add(e); + } + results.finishedAdding(); // notifies if all searchers done + } + }; + new Thread(r).start(); + } + + // wait for either all the searches to come back, or 10 seconds + synchronized(results) { + try { + results.wait(10 * SECONDS); + } catch (InterruptedException e) { + } + } + + if (results.outstandingSuppliers() > 0) { + String message = "Launched " + servers.size() + " cloud searchers, but" + + results.outstandingSuppliers() + "failed to complete."; + problems.add(new Keyserver.QueryFailedException(message)); + } + + if (!problems.isEmpty()) { + for (Keyserver.CloudSearchFailureException e : problems) { + Log.d(Constants.TAG, "Cloud search exception: " + e.getLocalizedMessage()); + } + + // only throw exception if we didn’t get any results + if (results.isEmpty()) { + throw problems.get(0); + } + } + + return results; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java new file mode 100644 index 000000000..15de8cb8f --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2014 Tim Bray <tbray@textuality.com> + * + * 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 3 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.sufficientlysecure.keychain.keyimport; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * Just an ArrayList, only with a synchronized dupe-merging add/addAll, and a sign-off method + */ +public class ImportKeysList extends ArrayList<ImportKeysListEntry> { + + private int mSupplierCount; + + public ImportKeysList(int supplierCount) { + mSupplierCount = supplierCount; + } + + @Override + public boolean add(ImportKeysListEntry toAdd) { + addOrMerge(toAdd); + return true; // that’s what the ArrayList#add contract says + } + + @Override + public boolean addAll(Collection<? extends ImportKeysListEntry> addThese) { + boolean modified = false; + for (ImportKeysListEntry toAdd : addThese) { + modified = addOrMerge(toAdd) || modified; + } + return modified; + } + + // NOTE: side-effects + // NOTE: synchronized + private synchronized boolean addOrMerge(ImportKeysListEntry toAdd) { + for (ImportKeysListEntry existing : this) { + if (toAdd.hasSameKeyAs(existing)) { + return mergeIDs(toAdd, existing); + } + } + return super.add(toAdd); + } + + // being a little anal about the ArrayList#addAll contract here + private boolean mergeIDs(ImportKeysListEntry incoming, ImportKeysListEntry existing) { + boolean modified = false; + ArrayList<String> incomingIDs = incoming.getUserIds(); + ArrayList<String> existingIDs = existing.getUserIds(); + for (String incomingID : incomingIDs) { + if (!existingIDs.contains(incomingID)) { + existingIDs.add(incomingID); + modified = true; + } + } + return modified; + } + + // NOTE: synchronized + public synchronized void finishedAdding() { + mSupplierCount--; + if (mSupplierCount == 0) { + this.notify(); + } + } + + public int outstandingSuppliers() { + return mSupplierCount; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java index da70f1505..c10cd2e70 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java @@ -107,6 +107,13 @@ public class ImportKeysListEntry implements Serializable, Parcelable { return super.hashCode(); } + public boolean hasSameKeyAs(Object o) { + if (mFingerprintHex == null || o == null || !(o instanceof ImportKeysListEntry)) { + return false; + } + return mFingerprintHex.equals(((ImportKeysListEntry) o).mFingerprintHex); + } + public String getKeyIdHex() { return mKeyIdHex; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java index b726529f8..5e4bd0b70 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java @@ -24,7 +24,19 @@ import java.io.InputStream; import java.util.List; public abstract class Keyserver { - public static class QueryFailedException extends Exception { + + public static class CloudSearchFailureException extends Exception { + private static final long serialVersionUID = 2703768928624654515L; + + public CloudSearchFailureException(String message) { + super(message); + } + public CloudSearchFailureException() { + super(); + } + } + + public static class QueryFailedException extends CloudSearchFailureException { private static final long serialVersionUID = 2703768928624654512L; public QueryFailedException(String message) { @@ -32,7 +44,7 @@ public abstract class Keyserver { } } - public static class QueryNeedsRepairException extends Exception { + public static class QueryNeedsRepairException extends CloudSearchFailureException { private static final long serialVersionUID = 2693768928624654512L; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java index dc9fa78e5..c6edc47a1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -204,7 +204,7 @@ public class CreateKeyFinalFragment extends Fragment { Bundle data = new Bundle(); // upload to favorite keyserver - String keyserver = Preferences.getPreferences(getActivity()).getKeyServers()[0]; + String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver(); data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, keyserver); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index d644dabc2..518a8046a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -68,8 +68,6 @@ public class ImportKeysActivity extends ActionBarActivity { + "IMPORT_KEY_FROM_KEY_SERVER_AND_RETURN"; public static final String ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN = Constants.INTENT_PREFIX + "IMPORT_KEY_FROM_FILE_AND_RETURN"; - public static final String ACTION_IMPORT_KEY_FROM_KEYBASE = Constants.INTENT_PREFIX - + "IMPORT_KEY_FROM_KEYBASE"; // Actions for internal use only: public static final String ACTION_IMPORT_KEY_FROM_FILE = Constants.INTENT_PREFIX @@ -101,12 +99,11 @@ public class ImportKeysActivity extends ActionBarActivity { public static final int VIEW_PAGER_HEIGHT = 64; // dp private static final int ALL_TABS = -1; - private static final int TAB_KEYSERVER = 0; + private static final int TAB_CLOUD = 0; private static final int TAB_QR_CODE = 1; private static final int TAB_FILE = 2; - private static final int TAB_KEYBASE = 3; - private int mSwitchToTab = TAB_KEYSERVER; + private int mSwitchToTab = TAB_CLOUD; @Override protected void onCreate(Bundle savedInstanceState) { @@ -193,8 +190,8 @@ public class ImportKeysActivity extends ActionBarActivity { if (query != null && query.length() > 0) { // display keyserver fragment with query serverBundle = new Bundle(); - serverBundle.putString(ImportKeysServerFragment.ARG_QUERY, query); - mSwitchToTab = TAB_KEYSERVER; + serverBundle.putString(ImportKeysCloudFragment.ARG_QUERY, query); + mSwitchToTab = TAB_CLOUD; // action: search immediately startListFragment(savedInstanceState, null, null, query); @@ -214,11 +211,11 @@ public class ImportKeysActivity extends ActionBarActivity { // display keyserver fragment with query serverBundle = new Bundle(); - serverBundle.putString(ImportKeysServerFragment.ARG_QUERY, query); - serverBundle.putBoolean(ImportKeysServerFragment.ARG_DISABLE_QUERY_EDIT, true); + serverBundle.putString(ImportKeysCloudFragment.ARG_QUERY, query); + serverBundle.putBoolean(ImportKeysCloudFragment.ARG_DISABLE_QUERY_EDIT, true); // display server tab only - showTabOnly = TAB_KEYSERVER; - mSwitchToTab = TAB_KEYSERVER; + showTabOnly = TAB_CLOUD; + mSwitchToTab = TAB_CLOUD; // action: search immediately startListFragment(savedInstanceState, null, null, query); @@ -259,12 +256,6 @@ public class ImportKeysActivity extends ActionBarActivity { // no immediate actions! startListFragment(savedInstanceState, null, null, null); - } else if (ACTION_IMPORT_KEY_FROM_KEYBASE.equals(action)) { - // NOTE: this only displays the appropriate fragment, no actions are taken - mSwitchToTab = TAB_KEYBASE; - - // no immediate actions! - startListFragment(savedInstanceState, null, null, null); } else { startListFragment(savedInstanceState, null, null, null); } @@ -299,19 +290,17 @@ public class ImportKeysActivity extends ActionBarActivity { switch (showTabOnly) { case ALL_TABS: - // show all tabs - mTabsAdapter.addTab(ImportKeysServerFragment.class, - serverBundle, getString(R.string.import_tab_keyserver)); + // show default tabs + mTabsAdapter.addTab(ImportKeysCloudFragment.class, + serverBundle, getString(R.string.import_tab_cloud)); mTabsAdapter.addTab(ImportKeysQrCodeFragment.class, null, getString(R.string.import_tab_qr_code)); mTabsAdapter.addTab(ImportKeysFileFragment.class, null, getString(R.string.import_tab_direct)); - mTabsAdapter.addTab(ImportKeysKeybaseFragment.class, - null, getString(R.string.import_tab_keybase)); break; - case TAB_KEYSERVER: - mTabsAdapter.addTab(ImportKeysServerFragment.class, - serverBundle, getString(R.string.import_tab_keyserver)); + case TAB_CLOUD: + mTabsAdapter.addTab(ImportKeysCloudFragment.class, + serverBundle, getString(R.string.import_tab_cloud)); break; case TAB_QR_CODE: mTabsAdapter.addTab(ImportKeysQrCodeFragment.class, @@ -321,10 +310,6 @@ public class ImportKeysActivity extends ActionBarActivity { mTabsAdapter.addTab(ImportKeysFileFragment.class, null, getString(R.string.import_tab_direct)); break; - case TAB_KEYBASE: - mTabsAdapter.addTab(ImportKeysKeybaseFragment.class, - null, getString(R.string.import_tab_keybase)); - break; } // update layout after operations @@ -380,18 +365,17 @@ public class ImportKeysActivity extends ActionBarActivity { if (mViewPager.getAdapter() != null) mViewPager.setAdapter(null); mViewPager.setAdapter(mTabsAdapter); - mViewPager.setCurrentItem(TAB_KEYSERVER); + mViewPager.setCurrentItem(TAB_CLOUD); - ImportKeysServerFragment f = (ImportKeysServerFragment) - getActiveFragment(mViewPager, TAB_KEYSERVER); + ImportKeysCloudFragment f = (ImportKeysCloudFragment) + getActiveFragment(mViewPager, TAB_CLOUD); - // ask favorite keyserver - String keyserver = Preferences.getPreferences(ImportKeysActivity.this).getKeyServers()[0]; + // search config + Preferences prefs = Preferences.getPreferences(ImportKeysActivity.this); + Preferences.CloudSearchPrefs cloudPrefs = new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver()); - // set fields of ImportKeysServerFragment - f.setQueryAndKeyserver(query, keyserver); // search directly - loadCallback(new ImportKeysListFragment.KeyserverLoaderState(query, keyserver)); + loadCallback(new ImportKeysListFragment.CloudLoaderState(query, cloudPrefs)); } // http://stackoverflow.com/a/9293207 @@ -522,8 +506,8 @@ public class ImportKeysActivity extends ActionBarActivity { Log.e(Constants.TAG, "Problem writing cache file", e); Notify.showNotify(this, "Problem writing cache file!", Notify.Style.ERROR); } - } else if (ls instanceof ImportKeysListFragment.KeyserverLoaderState) { - ImportKeysListFragment.KeyserverLoaderState sls = (ImportKeysListFragment.KeyserverLoaderState) ls; + } else if (ls instanceof ImportKeysListFragment.CloudLoaderState) { + ImportKeysListFragment.CloudLoaderState sls = (ImportKeysListFragment.CloudLoaderState) ls; // Send all information needed to service to query keys in other thread Intent intent = new Intent(this, KeychainIntentService.class); @@ -533,31 +517,7 @@ public class ImportKeysActivity extends ActionBarActivity { // fill values for this action Bundle data = new Bundle(); - data.putString(KeychainIntentService.DOWNLOAD_KEY_SERVER, sls.keyserver); - - // get selected key entries - ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedEntries(); - data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries); - - intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } else if (ls instanceof ImportKeysListFragment.KeybaseLoaderState) { - // Send all information needed to service to query keys in other thread - Intent intent = new Intent(this, KeychainIntentService.class); - - intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYBASE_KEYS); - - // fill values for this action - Bundle data = new Bundle(); + data.putString(KeychainIntentService.DOWNLOAD_KEY_SERVER, sls.mCloudPrefs.keyserver); // get selected key entries ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedEntries(); @@ -574,7 +534,6 @@ public class ImportKeysActivity extends ActionBarActivity { // start service with intent startService(intent); - } else { Notify.showNotify(this, R.string.error_nothing_import, Notify.Style.ERROR); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java index 88caebc32..6b4b833c6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui; import android.app.Activity; import android.content.Context; +import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.KeyEvent; @@ -30,8 +31,6 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; -import android.widget.EditText; -import android.widget.Spinner; import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; @@ -42,9 +41,8 @@ import org.sufficientlysecure.keychain.util.Log; import java.util.List; -public class ImportKeysServerFragment extends Fragment { +public class ImportKeysCloudFragment extends Fragment { public static final String ARG_QUERY = "query"; - public static final String ARG_KEYSERVER = "keyserver"; public static final String ARG_DISABLE_QUERY_EDIT = "disable_query_edit"; private ImportKeysActivity mImportActivity; @@ -52,19 +50,16 @@ public class ImportKeysServerFragment extends Fragment { private View mSearchButton; private AutoCompleteTextView mQueryEditText; private View mConfigButton; - private View mConfigLayout; - private Spinner mServerSpinner; private ArrayAdapter<String> mServerAdapter; /** * Creates new instance of this fragment */ - public static ImportKeysServerFragment newInstance(String query, String keyserver) { - ImportKeysServerFragment frag = new ImportKeysServerFragment(); + public static ImportKeysCloudFragment newInstance(String query, String keyserver, boolean doKeyserver, boolean doKeybase) { + ImportKeysCloudFragment frag = new ImportKeysCloudFragment(); Bundle args = new Bundle(); args.putString(ARG_QUERY, query); - args.putString(ARG_KEYSERVER, keyserver); frag.setArguments(args); @@ -76,26 +71,11 @@ public class ImportKeysServerFragment extends Fragment { */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.import_keys_server_fragment, container, false); - - mSearchButton = view.findViewById(R.id.import_server_search); - mQueryEditText = (AutoCompleteTextView) view.findViewById(R.id.import_server_query); - mConfigButton = view.findViewById(R.id.import_server_config_button); - mConfigLayout = view.findViewById(R.id.import_server_config); - mServerSpinner = (Spinner) view.findViewById(R.id.import_server_spinner); - - // add keyservers to spinner - mServerAdapter = new ArrayAdapter<String>(getActivity(), - android.R.layout.simple_spinner_item, Preferences.getPreferences(getActivity()) - .getKeyServers() - ); - mServerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - mServerSpinner.setAdapter(mServerAdapter); - if (mServerAdapter.getCount() > 0) { - mServerSpinner.setSelection(0); - } else { - mSearchButton.setEnabled(false); - } + View view = inflater.inflate(R.layout.import_keys_cloud_fragment, container, false); + + mSearchButton = view.findViewById(R.id.cloud_import_server_search); + mQueryEditText = (AutoCompleteTextView) view.findViewById(R.id.cloud_import_server_query); + mConfigButton = view.findViewById(R.id.cloud_import_server_config_button); List<String> namesAndEmails = ContactHelper.getContactNames(getActivity()); namesAndEmails.addAll(ContactHelper.getContactMails(getActivity())); @@ -110,9 +90,7 @@ public class ImportKeysServerFragment extends Fragment { mSearchButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - String query = mQueryEditText.getText().toString(); - String keyServer = (String) mServerSpinner.getSelectedItem(); - search(query, keyServer); + search(mQueryEditText.getText().toString()); // close keyboard after pressing search InputMethodManager imm = @@ -125,9 +103,7 @@ public class ImportKeysServerFragment extends Fragment { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (actionId == EditorInfo.IME_ACTION_SEARCH) { - String query = mQueryEditText.getText().toString(); - String keyServer = (String) mServerSpinner.getSelectedItem(); - search(query, keyServer); + search(mQueryEditText.getText().toString()); // Don't return true to let the keyboard close itself after pressing search return false; @@ -139,11 +115,11 @@ public class ImportKeysServerFragment extends Fragment { mConfigButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - if (mImportActivity.getViewPagerHeight() > ImportKeysActivity.VIEW_PAGER_HEIGHT) { - mImportActivity.resizeViewPager(ImportKeysActivity.VIEW_PAGER_HEIGHT); - } else { - mImportActivity.resizeViewPager(ImportKeysActivity.VIEW_PAGER_HEIGHT + 41); - } + Intent i = new Intent(mImportActivity, PreferencesActivity.class); + // GRR, for some reason I can’t set the Action or I get an incomprehensible + // exception about “modern two-pane layouts” + // i.setAction(PreferencesActivity.ACTION_PREFS_CLOUD); + startActivity(i); } }); @@ -163,14 +139,6 @@ public class ImportKeysServerFragment extends Fragment { Log.d(Constants.TAG, "query: " + query); } - if (getArguments().containsKey(ARG_KEYSERVER)) { - String keyserver = getArguments().getString(ARG_KEYSERVER); - int keyserverPos = mServerAdapter.getPosition(keyserver); - mServerSpinner.setSelection(keyserverPos); - - Log.d(Constants.TAG, "keyserver: " + keyserver); - } - if (getArguments().getBoolean(ARG_DISABLE_QUERY_EDIT, false)) { mQueryEditText.setEnabled(false); } @@ -184,14 +152,9 @@ public class ImportKeysServerFragment extends Fragment { mImportActivity = (ImportKeysActivity) activity; } - private void search(String query, String keyserver) { - mImportActivity.loadCallback(new ImportKeysListFragment.KeyserverLoaderState(query, keyserver)); - } - - public void setQueryAndKeyserver(String query, String keyserver) { - mQueryEditText.setText(query, TextView.BufferType.EDITABLE); - int keyServerPos = mServerAdapter.getPosition(keyserver); - mServerSpinner.setSelection(keyServerPos); + private void search(String query) { + Preferences prefs = Preferences.getPreferences(getActivity()); + mImportActivity.loadCallback(new ImportKeysListFragment.CloudLoaderState(query, prefs.getCloudSearchPrefs())); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java deleted file mode 100644 index 3a82bdc32..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> - * - * 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 3 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.sufficientlysecure.keychain.ui; - -import android.app.Activity; -import android.content.Context; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; -import android.widget.TextView; - -import org.sufficientlysecure.keychain.R; - -/** - * Import public keys from the Keybase.io directory. First cut: just raw search. - * TODO: make a pick list of the people you’re following on keybase - */ -public class ImportKeysKeybaseFragment extends Fragment { - - private ImportKeysActivity mImportActivity; - private View mSearchButton; - private EditText mQueryEditText; - - public static final String ARG_QUERY = "query"; - - /** - * Creates new instance of this fragment - */ - public static ImportKeysKeybaseFragment newInstance() { - ImportKeysKeybaseFragment frag = new ImportKeysKeybaseFragment(); - - Bundle args = new Bundle(); - frag.setArguments(args); - - return frag; - } - - /** - * Inflate the layout for this fragment - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.import_keys_keybase_fragment, container, false); - - mQueryEditText = (EditText) view.findViewById(R.id.import_keybase_query); - - mSearchButton = view.findViewById(R.id.import_keybase_search); - mSearchButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - String query = mQueryEditText.getText().toString(); - search(query); - - // close keyboard after pressing search - InputMethodManager imm = - (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(mQueryEditText.getWindowToken(), 0); - } - }); - - mQueryEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (actionId == EditorInfo.IME_ACTION_SEARCH) { - String query = mQueryEditText.getText().toString(); - search(query); - - // Don't return true to let the keyboard close itself after pressing search - return false; - } - return false; - } - }); - - return view; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - // set displayed values - if (getArguments() != null) { - if (getArguments().containsKey(ARG_QUERY)) { - String query = getArguments().getString(ARG_QUERY); - mQueryEditText.setText(query, TextView.BufferType.EDITABLE); - } - } - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - - mImportActivity = (ImportKeysActivity) activity; - } - - private void search(String query) { - mImportActivity.loadCallback(new ImportKeysListFragment.KeybaseLoaderState(query)); - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java index fde0f5f23..f1f234461 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -37,9 +37,8 @@ import org.sufficientlysecure.keychain.keyimport.Keyserver; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter; -import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListKeybaseLoader; +import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListCloudLoader; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader; -import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListServerLoader; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Notify; @@ -63,8 +62,7 @@ public class ImportKeysListFragment extends ListFragment implements private LoaderState mLoaderState; private static final int LOADER_ID_BYTES = 0; - private static final int LOADER_ID_SERVER_QUERY = 1; - private static final int LOADER_ID_KEYBASE = 2; + private static final int LOADER_ID_CLOUD = 1; private LongSparseArray<ParcelableKeyRing> mCachedKeyData; @@ -118,21 +116,13 @@ public class ImportKeysListFragment extends ListFragment implements } } - static public class KeyserverLoaderState extends LoaderState { - String serverQuery; - String keyserver; + static public class CloudLoaderState extends LoaderState { + Preferences.CloudSearchPrefs mCloudPrefs; + String mServerQuery; - KeyserverLoaderState(String serverQuery, String keyserver) { - this.serverQuery = serverQuery; - this.keyserver = keyserver; - } - } - - static public class KeybaseLoaderState extends LoaderState { - String keybaseQuery; - - KeybaseLoaderState(String keybaseQuery) { - this.keybaseQuery = keybaseQuery; + CloudLoaderState(String serverQuery, Preferences.CloudSearchPrefs cloudPrefs) { + mServerQuery = serverQuery; + mCloudPrefs = cloudPrefs; } } @@ -161,9 +151,8 @@ public class ImportKeysListFragment extends ListFragment implements } else if (query != null) { // TODO: this is used when updating a key. // Currently it simply uses keyserver nr 0 - String keyserver = Preferences.getPreferences(getActivity()) - .getKeyServers()[0]; - mLoaderState = new KeyserverLoaderState(query, keyserver); + Preferences prefs = Preferences.getPreferences(getActivity()); + mLoaderState = new CloudLoaderState(query, prefs.getCloudSearchPrefs()); } getListView().setOnTouchListener(new OnTouchListener() { @@ -202,11 +191,8 @@ public class ImportKeysListFragment extends ListFragment implements if (getLoaderManager().getLoader(LOADER_ID_BYTES) != null) { getLoaderManager().destroyLoader(LOADER_ID_BYTES); } - if (getLoaderManager().getLoader(LOADER_ID_SERVER_QUERY) != null) { - getLoaderManager().destroyLoader(LOADER_ID_SERVER_QUERY); - } - if (getLoaderManager().getLoader(LOADER_ID_KEYBASE) != null) { - getLoaderManager().destroyLoader(LOADER_ID_KEYBASE); + if (getLoaderManager().getLoader(LOADER_ID_CLOUD) != null) { + getLoaderManager().destroyLoader(LOADER_ID_CLOUD); } if (getView() != null) { setListShown(true); @@ -219,16 +205,11 @@ public class ImportKeysListFragment extends ListFragment implements setListShown(false); getLoaderManager().restartLoader(LOADER_ID_BYTES, null, this); - } else if (mLoaderState instanceof KeyserverLoaderState) { - // Start out with a progress indicator. - setListShown(false); - - getLoaderManager().restartLoader(LOADER_ID_SERVER_QUERY, null, this); - } else if (mLoaderState instanceof KeybaseLoaderState) { + } else if (mLoaderState instanceof CloudLoaderState) { // Start out with a progress indicator. setListShown(false); - getLoaderManager().restartLoader(LOADER_ID_KEYBASE, null, this); + getLoaderManager().restartLoader(LOADER_ID_CLOUD, null, this); } } @@ -241,13 +222,9 @@ public class ImportKeysListFragment extends ListFragment implements InputData inputData = getInputData(ls.keyBytes, ls.dataUri); return new ImportKeysListLoader(mActivity, inputData); } - case LOADER_ID_SERVER_QUERY: { - KeyserverLoaderState ls = (KeyserverLoaderState) mLoaderState; - return new ImportKeysListServerLoader(getActivity(), ls.serverQuery, ls.keyserver); - } - case LOADER_ID_KEYBASE: { - KeybaseLoaderState ls = (KeybaseLoaderState) mLoaderState; - return new ImportKeysListKeybaseLoader(getActivity(), ls.keybaseQuery); + case LOADER_ID_CLOUD: { + CloudLoaderState ls = (CloudLoaderState) mLoaderState; + return new ImportKeysListCloudLoader(getActivity(), ls.mServerQuery, ls.mCloudPrefs); } default: @@ -301,8 +278,7 @@ public class ImportKeysListFragment extends ListFragment implements } break; - case LOADER_ID_SERVER_QUERY: - case LOADER_ID_KEYBASE: + case LOADER_ID_CLOUD: if (error == null) { // No error @@ -333,11 +309,7 @@ public class ImportKeysListFragment extends ListFragment implements // Clear the data in the adapter. mAdapter.clear(); break; - case LOADER_ID_SERVER_QUERY: - // Clear the data in the adapter. - mAdapter.clear(); - break; - case LOADER_ID_KEYBASE: + case LOADER_ID_CLOUD: // Clear the data in the adapter. mAdapter.clear(); break; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java index 1221966e1..dd4fedf9d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java @@ -18,6 +18,7 @@ package org.sufficientlysecure.keychain.ui; import android.annotation.SuppressLint; +import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Bundle; @@ -40,7 +41,7 @@ import java.util.List; @SuppressLint("NewApi") public class PreferencesActivity extends PreferenceActivity { - public static final String ACTION_PREFS_GEN = "org.sufficientlysecure.keychain.ui.PREFS_GEN"; + public static final String ACTION_PREFS_CLOUD = "org.sufficientlysecure.keychain.ui.PREFS_CLOUD"; public static final String ACTION_PREFS_ADV = "org.sufficientlysecure.keychain.ui.PREFS_ADV"; public static final int REQUEST_CODE_KEYSERVER_PREF = 0x00007005; @@ -55,16 +56,11 @@ public class PreferencesActivity extends PreferenceActivity { String action = getIntent().getAction(); - if (action != null && action.equals(ACTION_PREFS_GEN)) { - addPreferencesFromResource(R.xml.gen_preferences); - - initializePassPassphraceCacheTtl( - (IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL)); + if (action != null && action.equals(ACTION_PREFS_CLOUD)) { + addPreferencesFromResource(R.xml.cloud_search_prefs); mKeyServerPreference = (PreferenceScreen) findPreference(Constants.Pref.KEY_SERVERS); - String servers[] = sPreferences.getKeyServers(); - mKeyServerPreference.setSummary(getResources().getQuantityString(R.plurals.n_keyservers, - servers.length, servers.length)); + mKeyServerPreference.setSummary(keyserverSummary(this)); mKeyServerPreference .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { public boolean onPreferenceClick(Preference preference) { @@ -76,10 +72,19 @@ public class PreferencesActivity extends PreferenceActivity { return false; } }); + initializeSearchKeyserver( + (CheckBoxPreference) findPreference(Constants.Pref.SEARCH_KEYSERVER) + ); + initializeSearchKeybase( + (CheckBoxPreference) findPreference(Constants.Pref.SEARCH_KEYBASE) + ); } else if (action != null && action.equals(ACTION_PREFS_ADV)) { addPreferencesFromResource(R.xml.adv_preferences); + initializePassPassphraceCacheTtl( + (IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL)); + initializeEncryptionAlgorithm( (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM)); @@ -136,8 +141,7 @@ public class PreferencesActivity extends PreferenceActivity { String servers[] = data .getStringArrayExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS); sPreferences.setKeyServers(servers); - mKeyServerPreference.setSummary(getResources().getQuantityString( - R.plurals.n_keyservers, servers.length, servers.length)); + mKeyServerPreference.setSummary(keyserverSummary(this)); break; } @@ -156,9 +160,9 @@ public class PreferencesActivity extends PreferenceActivity { } /** - * This fragment shows the general preferences in android 3.0+ + * This fragment shows the Cloud Search preferences in android 3.0+ */ - public static class GeneralPrefsFragment extends PreferenceFragment { + public static class CloudSearchPrefsFragment extends PreferenceFragment { private PreferenceScreen mKeyServerPreference = null; @@ -167,15 +171,11 @@ public class PreferencesActivity extends PreferenceActivity { super.onCreate(savedInstanceState); // Load the preferences from an XML resource - addPreferencesFromResource(R.xml.gen_preferences); - - initializePassPassphraceCacheTtl( - (IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL)); + addPreferencesFromResource(R.xml.cloud_search_prefs); mKeyServerPreference = (PreferenceScreen) findPreference(Constants.Pref.KEY_SERVERS); - String servers[] = sPreferences.getKeyServers(); - mKeyServerPreference.setSummary(getResources().getQuantityString(R.plurals.n_keyservers, - servers.length, servers.length)); + mKeyServerPreference.setSummary(keyserverSummary(getActivity())); + mKeyServerPreference .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { public boolean onPreferenceClick(Preference preference) { @@ -187,6 +187,12 @@ public class PreferencesActivity extends PreferenceActivity { return false; } }); + initializeSearchKeyserver( + (CheckBoxPreference) findPreference(Constants.Pref.SEARCH_KEYSERVER) + ); + initializeSearchKeybase( + (CheckBoxPreference) findPreference(Constants.Pref.SEARCH_KEYBASE) + ); } @Override @@ -199,8 +205,7 @@ public class PreferencesActivity extends PreferenceActivity { String servers[] = data .getStringArrayExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS); sPreferences.setKeyServers(servers); - mKeyServerPreference.setSummary(getResources().getQuantityString( - R.plurals.n_keyservers, servers.length, servers.length)); + mKeyServerPreference.setSummary(keyserverSummary(getActivity())); break; } @@ -224,6 +229,9 @@ public class PreferencesActivity extends PreferenceActivity { // Load the preferences from an XML resource addPreferencesFromResource(R.xml.adv_preferences); + initializePassPassphraceCacheTtl( + (IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL)); + initializeEncryptionAlgorithm( (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM)); @@ -271,7 +279,7 @@ public class PreferencesActivity extends PreferenceActivity { protected boolean isValidFragment(String fragmentName) { return AdvancedPrefsFragment.class.getName().equals(fragmentName) - || GeneralPrefsFragment.class.getName().equals(fragmentName) + || CloudSearchPrefsFragment.class.getName().equals(fragmentName) || super.isValidFragment(fragmentName); } @@ -398,6 +406,39 @@ public class PreferencesActivity extends PreferenceActivity { }); } + private static void initializeSearchKeyserver(final CheckBoxPreference mSearchKeyserver) { + Preferences.CloudSearchPrefs prefs = sPreferences.getCloudSearchPrefs(); + mSearchKeyserver.setChecked(prefs.searchKeyserver); + mSearchKeyserver.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + mSearchKeyserver.setChecked((Boolean) newValue); + sPreferences.setSearchKeyserver((Boolean) newValue); + return false; + } + }); + } + + private static void initializeSearchKeybase(final CheckBoxPreference mSearchKeybase) { + Preferences.CloudSearchPrefs prefs = sPreferences.getCloudSearchPrefs(); + mSearchKeybase.setChecked(prefs.searchKeybase); + mSearchKeybase.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + mSearchKeybase.setChecked((Boolean) newValue); + sPreferences.setSearchKeybase((Boolean) newValue); + return false; + } + }); + } + + public static String keyserverSummary(Context context) { + String[] servers = sPreferences.getKeyServers(); + String serverSummary = context.getResources().getQuantityString( + R.plurals.n_keyservers, servers.length, servers.length); + return serverSummary + "; " + context.getString(R.string.label_preferred) + ": " + sPreferences.getPreferredKeyserver(); + } + private static void initializeUseDefaultYubikeyPin(final CheckBoxPreference mUseDefaultYubikeyPin) { mUseDefaultYubikeyPin.setChecked(sPreferences.useDefaultYubikeyPin()); mUseDefaultYubikeyPin.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @@ -408,4 +449,5 @@ public class PreferencesActivity extends PreferenceActivity { } }); } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java index 0de675bab..b5e0e2ab5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java @@ -43,6 +43,7 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O private LayoutInflater mInflater; private ViewGroup mEditors; private View mAdd; + private View mShuffle; private TextView mTitle; private TextView mSummary; @@ -73,6 +74,7 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O mTitle = (TextView) findViewById(R.id.title); mSummary = (TextView) findViewById(R.id.summary); + mSummary.setText(getText(R.string.label_first_keyserver_is_used)); mTitle.setText(R.string.label_keyservers); @@ -80,9 +82,29 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O mAdd = findViewById(R.id.add); mAdd.setOnClickListener(this); + mShuffle = findViewById(R.id.shuffle); + mShuffle.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + Vector<String> servers = serverList(); + String first = servers.get(0); + if (first != null) { + servers.remove(0); + servers.add(first); + String[] dummy = {}; + makeServerList(servers.toArray(dummy)); + } + } + }); + Intent intent = getIntent(); String servers[] = intent.getStringArrayExtra(EXTRA_KEY_SERVERS); + makeServerList(servers); + } + + private void makeServerList(String[] servers) { if (servers != null) { + mEditors.removeAllViews(); for (String serv : servers) { KeyServerEditor view = (KeyServerEditor) mInflater.inflate( R.layout.key_server_editor, mEditors, false); @@ -114,6 +136,18 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O finish(); } + private Vector<String> serverList() { + Vector<String> servers = new Vector<String>(); + for (int i = 0; i < mEditors.getChildCount(); ++i) { + KeyServerEditor editor = (KeyServerEditor) mEditors.getChildAt(i); + String tmp = editor.getValue(); + if (tmp.length() > 0) { + servers.add(tmp); + } + } + return servers; + } + private void okClicked() { Intent data = new Intent(); Vector<String> servers = new Vector<String>(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java index d62c2db43..d39feda76 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java @@ -21,28 +21,29 @@ import android.content.Context; import android.support.v4.content.AsyncTaskLoader; import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.keyimport.CloudSearch; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; -import org.sufficientlysecure.keychain.keyimport.Keyserver; import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; -public class ImportKeysListServerLoader +public class ImportKeysListCloudLoader extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> { Context mContext; + + Preferences.CloudSearchPrefs mCloudPrefs; String mServerQuery; - String mKeyServer; private ArrayList<ImportKeysListEntry> mEntryList = new ArrayList<ImportKeysListEntry>(); private AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper; - public ImportKeysListServerLoader(Context context, String serverQuery, String keyServer) { + public ImportKeysListCloudLoader(Context context, String serverQuery, Preferences.CloudSearchPrefs cloudPrefs) { super(context); mContext = context; mServerQuery = serverQuery; - mKeyServer = keyServer; + mCloudPrefs = cloudPrefs; } @Override @@ -56,9 +57,9 @@ public class ImportKeysListServerLoader if (mServerQuery.startsWith("0x") && mServerQuery.length() == 42) { Log.d(Constants.TAG, "This search is based on a unique fingerprint. Enforce a fingerprint check!"); - queryServer(mServerQuery, mKeyServer, true); + queryServer(true); } else { - queryServer(mServerQuery, mKeyServer, false); + queryServer(false); } return mEntryListWrapper; @@ -90,15 +91,14 @@ public class ImportKeysListServerLoader /** * Query keyserver */ - private void queryServer(String query, String keyServer, boolean enforceFingerprint) { - HkpKeyserver server = new HkpKeyserver(keyServer); + private void queryServer(boolean enforceFingerprint) { try { - ArrayList<ImportKeysListEntry> searchResult = server.search(query); + ArrayList<ImportKeysListEntry> searchResult = CloudSearch.search(mServerQuery, mCloudPrefs); mEntryList.clear(); // add result to data if (enforceFingerprint) { - String fingerprint = query.substring(2); + String fingerprint = mServerQuery.substring(2); Log.d(Constants.TAG, "fingerprint: " + fingerprint); // query must return only one result! if (searchResult.size() == 1) { @@ -115,11 +115,8 @@ public class ImportKeysListServerLoader mEntryList.addAll(searchResult); } mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null); - } catch (Keyserver.QueryFailedException e) { - mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e); - } catch (Keyserver.QueryNeedsRepairException e) { + } catch (Exception e) { mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e); } } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListKeybaseLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListKeybaseLoader.java deleted file mode 100644 index d2aefc220..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListKeybaseLoader.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> - * - * 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 3 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.sufficientlysecure.keychain.ui.adapter; - -import android.content.Context; -import android.support.v4.content.AsyncTaskLoader; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; -import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver; -import org.sufficientlysecure.keychain.keyimport.Keyserver; -import org.sufficientlysecure.keychain.util.Log; - -import java.util.ArrayList; - -public class ImportKeysListKeybaseLoader - extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> { - Context mContext; - - String mKeybaseQuery; - - private ArrayList<ImportKeysListEntry> mEntryList = new ArrayList<ImportKeysListEntry>(); - private AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper; - - public ImportKeysListKeybaseLoader(Context context, String keybaseQuery) { - super(context); - mContext = context; - mKeybaseQuery = keybaseQuery; - } - - @Override - public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() { - - mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null); - - if (mKeybaseQuery == null) { - Log.e(Constants.TAG, "mKeybaseQery is null!"); - return mEntryListWrapper; - } - - queryServer(mKeybaseQuery); - - return mEntryListWrapper; - } - - @Override - protected void onReset() { - super.onReset(); - - // Ensure the loader is stopped - onStopLoading(); - } - - @Override - protected void onStartLoading() { - forceLoad(); - } - - @Override - protected void onStopLoading() { - cancelLoad(); - } - - @Override - public void deliverResult(AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> data) { - super.deliverResult(data); - } - - /** - * Query keybase - */ - private void queryServer(String query) { - - KeybaseKeyserver server = new KeybaseKeyserver(); - try { - ArrayList<ImportKeysListEntry> searchResult = server.search(query); - - mEntryList.clear(); - - mEntryList.addAll(searchResult); - mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null); - } catch (Keyserver.QueryFailedException e) { - mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e); - } catch (Keyserver.QueryNeedsRepairException e) { - mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e); - } - } -} diff --git a/OpenKeychain/src/main/res/drawable/button_rounded_blue.xml b/OpenKeychain/src/main/res/drawable/button_rounded_blue.xml new file mode 100644 index 000000000..7e36b0ef8 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable/button_rounded_blue.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" > + <shape android:shape="rectangle" > + <padding + android:bottom="2dip" + android:left="2dip" + android:right="2dip" + android:top="2dip" /> + <corners android:radius="6dip" /> + <solid android:color="#4747a4" /> + </shape> + </item> + <item android:state_focused="true"> + <shape android:shape="rectangle" > + <padding + android:bottom="2dip" + android:left="2dip" + android:right="2dip" + android:top="2dip" /> + <corners android:radius="6dip" /> + <solid android:color="#4747a4"/> + </shape> + </item> + <item > + <shape android:shape="rectangle" > + <padding + android:bottom="2dip" + android:left="2dip" + android:right="2dip" + android:top="2dip" /> + <corners android:radius="6dip" /> + <solid android:color="#5c5cb8" /> + </shape> + </item> +</selector>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/import_keys_server_fragment.xml b/OpenKeychain/src/main/res/layout/import_keys_cloud_fragment.xml index 62e7d740c..46ec736ca 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_server_fragment.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_cloud_fragment.xml @@ -11,12 +11,12 @@ android:orientation="horizontal"> <AutoCompleteTextView - android:id="@+id/import_server_query" + android:id="@+id/cloud_import_server_query" android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="top|left" - android:hint="@string/hint_keyserver_search_hint" + android:hint="@string/hint_cloud_search_hint" android:imeOptions="actionSearch" android:inputType="textNoSuggestions" android:singleLine="true" @@ -26,7 +26,7 @@ android:layout_gravity="center_vertical" /> <ImageButton - android:id="@+id/import_server_search" + android:id="@+id/cloud_import_server_search" android:layout_width="wrap_content" android:layout_height="match_parent" android:padding="8dp" @@ -43,7 +43,7 @@ android:background="?android:attr/listDivider" /> <ImageButton - android:id="@+id/import_server_config_button" + android:id="@+id/cloud_import_server_config_button" android:layout_width="wrap_content" android:layout_height="match_parent" android:padding="8dp" @@ -53,24 +53,5 @@ </LinearLayout> - <LinearLayout - android:id="@+id/import_server_config" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <View - android:layout_width="match_parent" - android:layout_height="1dip" - android:background="?android:attr/listDivider" /> - - <Spinner - android:id="@+id/import_server_spinner" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> - - </LinearLayout> </LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/import_keys_keybase_fragment.xml b/OpenKeychain/src/main/res/layout/import_keys_keybase_fragment.xml deleted file mode 100644 index c70236e07..000000000 --- a/OpenKeychain/src/main/res/layout/import_keys_keybase_fragment.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal"> - - <EditText - android:id="@+id/import_keybase_query" - android:layout_marginLeft="8dp" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1" - android:gravity="top|left" - android:hint="@string/hint_keybase_search_hint" - android:imeOptions="actionSearch" - android:inputType="textNoSuggestions" - android:singleLine="true" - android:lines="1" - android:maxLines="1" - android:minLines="1" - android:layout_gravity="center_vertical" /> - - <ImageButton - android:id="@+id/import_keybase_search" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:padding="8dp" - android:src="@drawable/ic_action_search" - android:layout_gravity="center_vertical" - style="@style/SelectableItem" /> - -</LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/import_keys_list_entry.xml b/OpenKeychain/src/main/res/layout/import_keys_list_entry.xml index 773250dc6..74de77172 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_list_entry.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_list_entry.xml @@ -30,6 +30,8 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:paddingRight="8dp" + android:paddingTop="2dp" + android:gravity="top|center" android:clickable="false" android:focusable="false" android:focusableInTouchMode="false" /> diff --git a/OpenKeychain/src/main/res/layout/key_server_preference.xml b/OpenKeychain/src/main/res/layout/key_server_preference.xml index baf7111af..140b5e3d7 100644 --- a/OpenKeychain/src/main/res/layout/key_server_preference.xml +++ b/OpenKeychain/src/main/res/layout/key_server_preference.xml @@ -12,7 +12,7 @@ android:minHeight="?android:attr/listPreferredItemHeight" android:orientation="horizontal" > - <RelativeLayout + <LinearLayout android:layout_width="0dip" android:layout_height="wrap_content" android:layout_marginBottom="6sp" @@ -21,6 +21,7 @@ android:layout_marginTop="6sp" android:layout_weight="1" android:background="@android:drawable/menuitem_background" + android:orientation="vertical" android:focusable="true" > <TextView @@ -35,20 +36,28 @@ android:id="@+id/summary" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignLeft="@android:id/title" - android:layout_below="@android:id/title" - android:maxLines="2" android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - + </LinearLayout> + <Button + android:id="@+id/shuffle" + android:layout_width="wrap_content" + android:layout_height="31dp" + android:layout_gravity="center_vertical" + android:layout_marginLeft="4dip" + android:layout_marginRight="6dip" + android:text="shuffle" + android:textColor="#ffffffff" + android:textStyle="bold" + android:paddingTop="2dp" + android:paddingBottom="2dp" + android:background="@drawable/button_rounded_blue" + /> <ImageButton android:id="@+id/add" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_margin="10dp" - android:layout_marginLeft="4dip" - android:layout_marginRight="6dip" android:src="@drawable/plus" android:background="@drawable/button_rounded_green"/> </LinearLayout> diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 69bcca5a1..fe9dc0701 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -16,6 +16,7 @@ <string name="title_add_subkey">"Add subkey"</string> <string name="title_edit_key">"Edit Key"</string> <string name="title_preferences">"Preferences"</string> + <string name="title_cloud_search_preferences">"Cloud Search Preferences"</string> <string name="title_api_registered_apps">"Apps"</string> <string name="title_key_server_preference">"Keyserver Preference"</string> <string name="title_change_passphrase">"Change Passphrase"</string> @@ -39,6 +40,7 @@ <!-- section --> <string name="section_user_ids">"Identities"</string> <string name="section_keys">"Subkeys"</string> + <string name="section_cloud_search">"Cloud search"</string> <string name="section_general">"General"</string> <string name="section_defaults">"Defaults"</string> <string name="section_advanced">"Advanced"</string> @@ -125,6 +127,8 @@ <string name="label_send_key">"Upload key to selected keyserver after certification"</string> <string name="label_fingerprint">"Fingerprint"</string> <string name="expiry_date_dialog_title">"Set expiry date"</string> + <string name="label_first_keyserver_is_used">"(First keyserver listed is preferred)"</string> + <string name="label_preferred">"preferred"</string> <string name="user_id_no_name">"<no name>"</string> <string name="none">"<none>"</string> @@ -323,7 +327,7 @@ <!-- action strings --> <string name="hint_keyserver_search_hint">"Name/Email/Key ID…"</string> - <string name="hint_keybase_search_hint">"Name/Email/Proof/Key…"</string> + <string name="hint_cloud_search_hint">"Name/Email/Proof/Key…"</string> <!-- key bit length selections --> <string name="key_size_512">"512"</string> @@ -364,9 +368,9 @@ <!-- Import --> <string name="import_tab_keyserver">"Keyserver"</string> + <string name="import_tab_cloud">"Search Cloud"</string> <string name="import_tab_direct">"File/Clipboard"</string> <string name="import_tab_qr_code">"QR Code/NFC"</string> - <string name="import_tab_keybase">"Keybase.io"</string> <string name="import_import">"Import selected keys"</string> <string name="import_qr_code_wrong">"QR Code malformed! Please try again!"</string> <string name="import_qr_code_too_short_fingerprint">"Fingerprint is too short (< 16 characters)"</string> diff --git a/OpenKeychain/src/main/res/xml/adv_preferences.xml b/OpenKeychain/src/main/res/xml/adv_preferences.xml index 3180f21aa..64023b4b5 100644 --- a/OpenKeychain/src/main/res/xml/adv_preferences.xml +++ b/OpenKeychain/src/main/res/xml/adv_preferences.xml @@ -1,6 +1,12 @@ <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceCategory android:title="@string/section_defaults"> <org.sufficientlysecure.keychain.ui.widget.IntegerListPreference + android:entries="@array/passphrase_cache_ttl_entries" + android:entryValues="@array/passphrase_cache_ttl_values" + android:key="passphraseCacheTtl" + android:persistent="false" + android:title="@string/label_passphrase_cache_ttl" /> + <org.sufficientlysecure.keychain.ui.widget.IntegerListPreference android:key="defaultEncryptionAlgorithm" android:persistent="false" android:title="@string/label_encryption_algorithm" /> @@ -28,11 +34,4 @@ android:title="@string/label_write_version_header" android:summary="@string/label_write_version_header_summary" /> </PreferenceCategory> - <CheckBoxPreference - android:key="useDefaultYubikeyPin" - android:persistent="false" - android:defaultValue="true" - android:title="@string/label_use_default_yubikey_pin" - android:summary="@string/label_label_use_default_yubikey_pin_summary" /> - </PreferenceScreen> diff --git a/OpenKeychain/src/main/res/xml/cloud_search_prefs.xml b/OpenKeychain/src/main/res/xml/cloud_search_prefs.xml new file mode 100644 index 000000000..c87c4df7b --- /dev/null +++ b/OpenKeychain/src/main/res/xml/cloud_search_prefs.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> + +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> + <CheckBoxPreference + android:title="Search Keyserver" + android:defaultValue="true" + android:summary="Search HKP keyserver" + android:key="search_keyserver_pref" /> + + <CheckBoxPreference + android:title="Search Keybase.io" + android:defaultValue="true" + android:summary="Search Keybase.io index" + android:key="search_keybase_pref" /> + + <PreferenceScreen + android:key="keyServers" + android:persistent="false" + android:title="@string/label_keyservers" /> +</PreferenceScreen>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/xml/gen_preferences.xml b/OpenKeychain/src/main/res/xml/gen_preferences.xml deleted file mode 100644 index fd3c6c31a..000000000 --- a/OpenKeychain/src/main/res/xml/gen_preferences.xml +++ /dev/null @@ -1,15 +0,0 @@ -<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> - <PreferenceCategory android:title="@string/section_general"> - <org.sufficientlysecure.keychain.ui.widget.IntegerListPreference - android:entries="@array/passphrase_cache_ttl_entries" - android:entryValues="@array/passphrase_cache_ttl_values" - android:key="passphraseCacheTtl" - android:persistent="false" - android:title="@string/label_passphrase_cache_ttl" /> - - <PreferenceScreen - android:key="keyServers" - android:persistent="false" - android:title="@string/label_keyservers" /> - </PreferenceCategory> -</PreferenceScreen> diff --git a/OpenKeychain/src/main/res/xml/preference_headers.xml b/OpenKeychain/src/main/res/xml/preference_headers.xml index e6b68507a..dd7f1c3e6 100644 --- a/OpenKeychain/src/main/res/xml/preference_headers.xml +++ b/OpenKeychain/src/main/res/xml/preference_headers.xml @@ -1,7 +1,7 @@ <preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> <header - android:fragment="org.sufficientlysecure.keychain.ui.PreferencesActivity$GeneralPrefsFragment" - android:title="@string/section_general" /> + android:fragment="org.sufficientlysecure.keychain.ui.PreferencesActivity$CloudSearchPrefsFragment" + android:title="@string/section_cloud_search" /> <header android:fragment="org.sufficientlysecure.keychain.ui.PreferencesActivity$AdvancedPrefsFragment" android:title="@string/section_advanced" /> |