From 555b0bf7573a9273bdd5e8c7870cabd8cd0e2186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sat, 18 Apr 2015 16:50:52 +0200 Subject: Temporarily fix devices without nfc --- .../org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java index 95c1690b1..db7aba519 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java @@ -476,6 +476,9 @@ public abstract class BaseNfcActivity extends BaseActivity { */ public void enableNfcForegroundDispatch() { mNfcAdapter = NfcAdapter.getDefaultAdapter(this); + if (mNfcAdapter == null) { + return; + } Intent nfcI = new Intent(this, getClass()) .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent nfcPendingIntent = PendingIntent.getActivity(this, 0, nfcI, PendingIntent.FLAG_CANCEL_CURRENT); @@ -497,6 +500,9 @@ public abstract class BaseNfcActivity extends BaseActivity { * Disable foreground dispatch in onPause! */ public void disableNfcForegroundDispatch() { + if (mNfcAdapter == null) { + return; + } mNfcAdapter.disableForegroundDispatch(this); Log.d(Constants.TAG, "NfcForegroundDispatch has been disabled!"); } -- cgit v1.2.3 From f2d3abd4c9cd521754d932a057356b511247be00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sat, 18 Apr 2015 16:52:02 +0200 Subject: Format KeyListFragment --- .../keychain/ui/KeyListFragment.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 4733dce01..96ce101b5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -681,17 +681,16 @@ public class KeyListFragment extends LoaderFragment } private void showMultiExportDialog(long[] masterKeyIds) { - mIdsForRepeatAskPassphrase = new ArrayList(); - for(long id: masterKeyIds) { + mIdsForRepeatAskPassphrase = new ArrayList<>(); + for (long id : masterKeyIds) { try { if (PassphraseCacheService.getCachedPassphrase( getActivity(), id, id) == null) { - mIdsForRepeatAskPassphrase.add(Long.valueOf(id)); + mIdsForRepeatAskPassphrase.add(id); } } catch (PassphraseCacheService.KeyNotFoundException e) { // This happens when the master key is stripped // and ignore this key. - continue; } } mIndex = 0; @@ -700,8 +699,8 @@ public class KeyListFragment extends LoaderFragment return; } long[] idsForMultiExport = new long[mIdsForRepeatAskPassphrase.size()]; - for(int i=0; i Date: Mon, 20 Apr 2015 13:53:00 +0200 Subject: Cleanup in HkpKeyserver --- .../org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java index 480319081..cb8a53e25 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java @@ -242,7 +242,7 @@ public class HkpKeyserver extends Keyserver { String encodedQuery; try { - encodedQuery = URLEncoder.encode(query, "utf8"); + encodedQuery = URLEncoder.encode(query, "UTF8"); } catch (UnsupportedEncodingException e) { return null; } @@ -286,7 +286,7 @@ public class HkpKeyserver extends Keyserver { entry.setAlgorithm(KeyFormattingUtils.getAlgorithmInfo(algorithmId, bitSize, null)); // group 1 contains the full fingerprint (v4) or the long key id if available - // see http://bit.ly/1d4bxbk and http://bit.ly/1gD1wwr + // see https://bitbucket.org/skskeyserver/sks-keyserver/pull-request/12/fixes-for-machine-readable-indexes/diff String fingerprintOrKeyId = matcher.group(1).toLowerCase(Locale.ENGLISH); if (fingerprintOrKeyId.length() > 16) { entry.setFingerprintHex(fingerprintOrKeyId); @@ -312,14 +312,13 @@ public class HkpKeyserver extends Keyserver { String tmp = uidMatcher.group(1).trim(); if (tmp.contains("%")) { if (tmp.contains("%%")) { - // This is a fix for issue #683 // The server encodes a percent sign as %%, so it is swapped out with its // urlencoded counterpart to prevent errors tmp = tmp.replace("%%", "%25"); } try { // converts Strings like "Universit%C3%A4t" to a proper encoding form "Universität". - tmp = (URLDecoder.decode(tmp, "UTF8")); + tmp = URLDecoder.decode(tmp, "UTF8"); } catch (UnsupportedEncodingException ignored) { // will never happen, because "UTF8" is supported } -- cgit v1.2.3 From 91443c2f43615f25cd412007d89fe5a8ccc082bb Mon Sep 17 00:00:00 2001 From: Adithya Abraham Philip Date: Sun, 19 Apr 2015 13:31:37 +0530 Subject: support for handling keyserver urls from browser, added documentation --- .../keychain/ui/CreateKeyYubiImportFragment.java | 3 +- .../keychain/ui/ImportKeysActivity.java | 93 +++++++++++++++++----- .../keychain/ui/ImportKeysCloudFragment.java | 23 +++++- .../keychain/ui/ImportKeysListFragment.java | 56 ++++++++++--- .../keychain/ui/adapter/ImportKeysListLoader.java | 4 +- .../keychain/util/Preferences.java | 5 ++ 6 files changed, 147 insertions(+), 37 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiImportFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiImportFragment.java index 1cd0aaf2f..db62d53c5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiImportFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiImportFragment.java @@ -123,7 +123,8 @@ public class CreateKeyYubiImportFragment extends Fragment implements NfcListener }); } - mListFragment = ImportKeysListFragment.newInstance(null, null, "0x" + mNfcFingerprint, true); + mListFragment = ImportKeysListFragment.newInstance(null, null, + "0x" + mNfcFingerprint, true, null); view.findViewById(R.id.button_search).setOnClickListener(new OnClickListener() { @Override 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 7fe5be793..5d9950db6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -39,6 +39,7 @@ import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity; import org.sufficientlysecure.keychain.service.CloudImportService; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; +import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.Log; @@ -62,6 +63,8 @@ public class ImportKeysActivity extends BaseNfcActivity { // Actions for internal use only: public static final String ACTION_IMPORT_KEY_FROM_FILE = Constants.INTENT_PREFIX + "IMPORT_KEY_FROM_FILE"; + public static final String ACTION_SEARCH_KEYSERVER_FROM_URL = Constants.INTENT_PREFIX + + "SEARCH_KEYSERVER_FROM_URL"; public static final String EXTRA_RESULT = "result"; // only used by ACTION_IMPORT_KEY @@ -112,15 +115,19 @@ public class ImportKeysActivity extends BaseNfcActivity { } if (action == null) { - startCloudFragment(savedInstanceState, null, false); - startListFragment(savedInstanceState, null, null, null); + startCloudFragment(savedInstanceState, null, false, null); + startListFragment(savedInstanceState, null, null, null, null); return; } if (Intent.ACTION_VIEW.equals(action)) { - // Android's Action when opening file associated to Keychain (see AndroidManifest.xml) - // delegate action to ACTION_IMPORT_KEY - action = ACTION_IMPORT_KEY; + if (scheme.equals("http") || scheme.equals("https")) { + action = ACTION_SEARCH_KEYSERVER_FROM_URL; + } else { + // Android's Action when opening file associated to Keychain (see AndroidManifest.xml) + // delegate action to ACTION_IMPORT_KEY + action = ACTION_IMPORT_KEY; + } } switch (action) { @@ -130,12 +137,12 @@ public class ImportKeysActivity extends BaseNfcActivity { if (dataUri != null) { // action: directly load data - startListFragment(savedInstanceState, null, dataUri, null); + startListFragment(savedInstanceState, null, dataUri, null, null); } else if (extras.containsKey(EXTRA_KEY_BYTES)) { byte[] importData = extras.getByteArray(EXTRA_KEY_BYTES); // action: directly load data - startListFragment(savedInstanceState, importData, null, null); + startListFragment(savedInstanceState, importData, null, null, null); } break; } @@ -162,10 +169,10 @@ public class ImportKeysActivity extends BaseNfcActivity { if (query != null && query.length() > 0) { // display keyserver fragment with query - startCloudFragment(savedInstanceState, query, false); + startCloudFragment(savedInstanceState, query, false, null); // action: search immediately - startListFragment(savedInstanceState, null, null, query); + startListFragment(savedInstanceState, null, null, query, null); } else { Log.e(Constants.TAG, "Query is empty!"); return; @@ -181,10 +188,10 @@ public class ImportKeysActivity extends BaseNfcActivity { String query = "0x" + fingerprint; // display keyserver fragment with query - startCloudFragment(savedInstanceState, query, true); + startCloudFragment(savedInstanceState, query, true, null); // action: search immediately - startListFragment(savedInstanceState, null, null, query); + startListFragment(savedInstanceState, null, null, query, null); } } else { Log.e(Constants.TAG, @@ -200,7 +207,29 @@ public class ImportKeysActivity extends BaseNfcActivity { startFileFragment(savedInstanceState); // no immediate actions! - startListFragment(savedInstanceState, null, null, null); + startListFragment(savedInstanceState, null, null, null, null); + break; + } + case ACTION_SEARCH_KEYSERVER_FROM_URL: { + // need to process URL to get search query and keyserver authority + String query = dataUri.getQueryParameter("search"); + String keyserver = dataUri.getAuthority(); + // if query not specified, we still allow users to search the keyserver in the link + if (query == null) { + Notify.create(this, R.string.import_url_warn_no_search_parameter, Notify.LENGTH_INDEFINITE, + Notify.Style.WARN).show(mTopFragment); + // we just set the keyserver + startCloudFragment(savedInstanceState, null, false, keyserver); + // it's not necessary to set the keyserver for ImportKeysListFragment since + // it'll be taken care of by ImportKeysCloudFragment when the user clicks + // the search button + startListFragment(savedInstanceState, null, null, null, null); + } else { + // we allow our users to edit the query if they wish + startCloudFragment(savedInstanceState, query, false, keyserver); + // search immediately + startListFragment(savedInstanceState, null, null, query, keyserver); + } break; } case ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN: { @@ -208,18 +237,31 @@ public class ImportKeysActivity extends BaseNfcActivity { startFileFragment(savedInstanceState); // no immediate actions! - startListFragment(savedInstanceState, null, null, null); + startListFragment(savedInstanceState, null, null, null, null); break; } default: { - startCloudFragment(savedInstanceState, null, false); - startListFragment(savedInstanceState, null, null, null); + startCloudFragment(savedInstanceState, null, false, null); + startListFragment(savedInstanceState, null, null, null, null); break; } } } - private void startListFragment(Bundle savedInstanceState, byte[] bytes, Uri dataUri, String serverQuery) { + + /** + * if the fragment is started with non-null bytes/dataUri/serverQuery, it will immediately + * load content + * + * @param savedInstanceState + * @param bytes bytes containing list of keyrings to import + * @param dataUri uri to file to import keyrings from + * @param serverQuery query to search for on the keyserver + * @param keyserver keyserver authority to search on. If null will use keyserver from + * user preferences + */ + private void startListFragment(Bundle savedInstanceState, byte[] bytes, Uri dataUri, + String serverQuery, String keyserver) { // However, if we're being restored from a previous state, // then we don't need to do anything and should return or else // we could end up with overlapping fragments. @@ -227,8 +269,8 @@ public class ImportKeysActivity extends BaseNfcActivity { return; } - // Create an instance of the fragment - mListFragment = ImportKeysListFragment.newInstance(bytes, dataUri, serverQuery); + mListFragment = ImportKeysListFragment.newInstance(bytes, dataUri, serverQuery, false, + keyserver); // Add the fragment to the 'fragment_container' FrameLayout // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! @@ -259,7 +301,18 @@ public class ImportKeysActivity extends BaseNfcActivity { getSupportFragmentManager().executePendingTransactions(); } - private void startCloudFragment(Bundle savedInstanceState, String query, boolean disableQueryEdit) { + /** + * loads the CloudFragment, which consists of the search bar, search button and settings icon + * visually. + * + * @param savedInstanceState + * @param query search query + * @param disableQueryEdit if true, user will not be able to edit the search query + * @param keyserver keyserver authority to use for search, if null will use keyserver + * specified in user preferences + */ + + private void startCloudFragment(Bundle savedInstanceState, String query, boolean disableQueryEdit, String keyserver) { // However, if we're being restored from a previous state, // then we don't need to do anything and should return or else // we could end up with overlapping fragments. @@ -268,7 +321,7 @@ public class ImportKeysActivity extends BaseNfcActivity { } // Create an instance of the fragment - mTopFragment = ImportKeysCloudFragment.newInstance(query, disableQueryEdit); + mTopFragment = ImportKeysCloudFragment.newInstance(query, disableQueryEdit, keyserver); // Add the fragment to the 'fragment_container' FrameLayout // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java index 91ca93c36..1cd5c24f3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java @@ -45,6 +45,7 @@ import java.util.List; public class ImportKeysCloudFragment extends Fragment { public static final String ARG_QUERY = "query"; public static final String ARG_DISABLE_QUERY_EDIT = "disable_query_edit"; + public static final String ARG_KEYSERVER = "keyserver"; private ImportKeysActivity mImportActivity; @@ -54,13 +55,20 @@ public class ImportKeysCloudFragment extends Fragment { /** * Creates new instance of this fragment + * + * @param query query to search for + * @param disableQueryEdit if true, user cannot edit query + * @param keyserver specified keyserver authority to use. If null, will use keyserver + * specified in user preferences */ - public static ImportKeysCloudFragment newInstance(String query, boolean disableQueryEdit) { + public static ImportKeysCloudFragment newInstance(String query, boolean disableQueryEdit, + String keyserver) { ImportKeysCloudFragment frag = new ImportKeysCloudFragment(); Bundle args = new Bundle(); args.putString(ARG_QUERY, query); args.putBoolean(ARG_DISABLE_QUERY_EDIT, disableQueryEdit); + args.putString(ARG_KEYSERVER, keyserver); frag.setArguments(args); @@ -151,8 +159,17 @@ public class ImportKeysCloudFragment extends Fragment { } private void search(String query) { - Preferences prefs = Preferences.getPreferences(getActivity()); - mImportActivity.loadCallback(new ImportKeysListFragment.CloudLoaderState(query, prefs.getCloudSearchPrefs())); + Preferences.CloudSearchPrefs cloudSearchPrefs; + String explicitKeyserver = getArguments().getString(ARG_KEYSERVER); + // no explicit keyserver passed + if (explicitKeyserver == null) { + cloudSearchPrefs = Preferences.getPreferences(getActivity()).getCloudSearchPrefs(); + } else { + // assume we are also meant to search keybase.io + cloudSearchPrefs = new Preferences.CloudSearchPrefs(true, true, explicitKeyserver); + } + mImportActivity.loadCallback( + new ImportKeysListFragment.CloudLoaderState(query, cloudSearchPrefs)); toggleKeyboard(false); } 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 b9fdbea5c..81ff87216 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -58,6 +58,7 @@ public class ImportKeysListFragment extends ListFragment implements private static final String ARG_BYTES = "bytes"; public static final String ARG_SERVER_QUERY = "query"; public static final String ARG_NON_INTERACTIVE = "non_interactive"; + public static final String ARG_KEYSERVER_URL = "keyserver_url"; private Activity mActivity; private ImportKeysAdapter mAdapter; @@ -78,7 +79,8 @@ public class ImportKeysListFragment extends ListFragment implements return mAdapter.getData(); } - /** Returns an Iterator (with size) of the selected data items. + /** + * Returns an Iterator (with size) of the selected data items. * This iterator is sort of a tradeoff, it's slightly more complex than an * ArrayList would have been, but we save some memory by just returning * relevant elements on demand. @@ -121,12 +123,36 @@ public class ImportKeysListFragment extends ListFragment implements } - public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, String serverQuery) { - return newInstance(bytes, dataUri, serverQuery, false); + /** + * Creates an interactive ImportKeyListFragment which reads keyrings from bytes, or file specified + * by dataUri, or searches a keyserver for serverQuery, if parameter is not null, in that order + * + * @param bytes byte data containing list of keyrings to be imported + * @param dataUri file from which keyrings are to be imported + * @param serverQuery query to search for on keyserver + * @param keyserver if not null, will perform search on specified keyserver. Else, uses + * keyserver specified in user preferences + * @return fragment with arguments set based on passed parameters + */ + public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, String serverQuery, + String keyserver) { + return newInstance(bytes, dataUri, serverQuery, false, keyserver); } - public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, - String serverQuery, boolean nonInteractive) { + /** + * Visually consists of a list of keyrings with checkboxes to specify which are to be imported + * Can immediately load keyrings specified by any of its parameters + * + * @param bytes byte data containing list of keyrings to be imported + * @param dataUri file from which keyrings are to be imported + * @param serverQuery query to search for on keyserver + * @param nonInteractive if true, users will not be able to check/uncheck items in the list + * @param keyserver if set, will perform search on specified keyserver. If null, falls back + * to keyserver specified in user preferences + * @return fragment with arguments set based on passed parameters + */ + public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, String serverQuery, + boolean nonInteractive, String keyserver) { ImportKeysListFragment frag = new ImportKeysListFragment(); Bundle args = new Bundle(); @@ -134,6 +160,7 @@ public class ImportKeysListFragment extends ListFragment implements args.putParcelable(ARG_DATA_URI, dataUri); args.putString(ARG_SERVER_QUERY, serverQuery); args.putBoolean(ARG_NON_INTERACTIVE, nonInteractive); + args.putString(ARG_KEYSERVER_URL, keyserver); frag.setArguments(args); @@ -180,16 +207,23 @@ public class ImportKeysListFragment extends ListFragment implements setListAdapter(mAdapter); Bundle args = getArguments(); - Uri dataUri = args.containsKey(ARG_DATA_URI) ? args.getParcelable(ARG_DATA_URI) : null; - byte[] bytes = args.containsKey(ARG_BYTES) ? args.getByteArray(ARG_BYTES) : null; - String query = args.containsKey(ARG_SERVER_QUERY) ? args.getString(ARG_SERVER_QUERY) : null; - mNonInteractive = args.containsKey(ARG_NON_INTERACTIVE) ? args.getBoolean(ARG_NON_INTERACTIVE) : false; + Uri dataUri = args.getParcelable(ARG_DATA_URI); + byte[] bytes = args.getByteArray(ARG_BYTES); + String query = args.getString(ARG_SERVER_QUERY); + String keyserver = args.getString(ARG_KEYSERVER_URL); + mNonInteractive = args.getBoolean(ARG_NON_INTERACTIVE, false); if (dataUri != null || bytes != null) { mLoaderState = new BytesLoaderState(bytes, dataUri); } else if (query != null) { - Preferences prefs = Preferences.getPreferences(getActivity()); - mLoaderState = new CloudLoaderState(query, prefs.getCloudSearchPrefs()); + Preferences.CloudSearchPrefs cloudSearchPrefs; + if (keyserver != null) { + cloudSearchPrefs = Preferences.getPreferences(getActivity()).getCloudSearchPrefs(); + } else { + cloudSearchPrefs = new Preferences.CloudSearchPrefs(true, true, keyserver); + } + + mLoaderState = new CloudLoaderState(query, cloudSearchPrefs); } getListView().setOnTouchListener(new OnTouchListener() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java index 5447c5f96..139512ba9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java @@ -82,12 +82,12 @@ public class ImportKeysListLoader @Override protected void onStartLoading() { - forceLoad(); + super.forceLoad(); } @Override protected void onStopLoading() { - cancelLoad(); + super.cancelLoad(); } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java index 8a7638054..303687315 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java @@ -193,6 +193,11 @@ public class Preferences { public final boolean searchKeybase; public final String keyserver; + /** + * @param searchKeyserver should passed keyserver be searched + * @param searchKeybase should keybase.io be searched + * @param keyserver the keyserver url authority to search on + */ public CloudSearchPrefs(boolean searchKeyserver, boolean searchKeybase, String keyserver) { this.searchKeyserver = searchKeyserver; this.searchKeybase = searchKeybase; -- cgit v1.2.3 From 104fdcd6bf0442f44a5c591101d8a232bf6455ef Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 20 Apr 2015 17:27:55 -0400 Subject: Track state of PIN verification on smart card; only verify when needed. --- .../keychain/ui/base/BaseNfcActivity.java | 35 ++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java index db7aba519..5990a8b5a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java @@ -57,6 +57,9 @@ public abstract class BaseNfcActivity extends BaseActivity { public static final int REQUEST_CODE_PASSPHRASE = 1; protected Passphrase mPin; + protected boolean mPw1ValidForMultipleSignatures; + protected boolean mPw1ValidatedForSignature; + protected boolean mPw1ValidatedForDecrypt; // Mode 82 does other things; consider renaming? private NfcAdapter mNfcAdapter; private IsoDep mIsoDep; @@ -201,6 +204,11 @@ public abstract class BaseNfcActivity extends BaseActivity { throw new IOException("Initialization failed!"); } + byte[] pwStatusBytes = nfcGetPwStatusBytes(); + mPw1ValidForMultipleSignatures = (pwStatusBytes[0] == 1); + mPw1ValidatedForSignature = false; + mPw1ValidatedForDecrypt = false; + onNfcPerform(); mIsoDep.close(); @@ -278,6 +286,15 @@ public abstract class BaseNfcActivity extends BaseActivity { return fptlv.mV; } + /** Return the PW Status Bytes from the card. This is a simple DO; no TLV decoding needed. + * + * @return Seven bytes in fixed format, plus 0x9000 status word at the end. + */ + public byte[] nfcGetPwStatusBytes() throws IOException { + String data = "00CA00C400"; + return mIsoDep.transceive(Hex.decode(data)); + } + /** Return the fingerprint from application specific data stored on tag, or * null if it doesn't exist. * @@ -316,7 +333,9 @@ public abstract class BaseNfcActivity extends BaseActivity { * @return a big integer representing the MPI for the given hash */ public byte[] nfcCalculateSignature(byte[] hash, int hashAlgo) throws IOException { - nfcVerifyPIN(0x81); // (Verify PW1 with mode 81 for signing) + if (!mPw1ValidatedForSignature) { + nfcVerifyPIN(0x81); // (Verify PW1 with mode 81 for signing) + } // dsi, including Lc String dsi; @@ -391,6 +410,10 @@ public abstract class BaseNfcActivity extends BaseActivity { Log.d(Constants.TAG, "final response:" + status); + if (!mPw1ValidForMultipleSignatures) { + mPw1ValidatedForSignature = false; + } + if ( ! "9000".equals(status)) { throw new IOException("Bad NFC response code: " + status); } @@ -410,7 +433,9 @@ public abstract class BaseNfcActivity extends BaseActivity { * @return the decoded session key */ public byte[] nfcDecryptSessionKey(byte[] encryptedSessionKey) throws IOException { - nfcVerifyPIN(0x82); // (Verify PW1 with mode 82 for decryption) + if (!mPw1ValidatedForDecrypt) { + nfcVerifyPIN(0x82); // (Verify PW1 with mode 82 for decryption) + } String firstApdu = "102a8086fe"; String secondApdu = "002a808603"; @@ -458,6 +483,12 @@ public abstract class BaseNfcActivity extends BaseActivity { handlePinError(); throw new IOException("Bad PIN!"); } + + if (mode == 0x81) { + mPw1ValidatedForSignature = true; + } else if (mode == 0x82) { + mPw1ValidatedForDecrypt = true; + } } } -- cgit v1.2.3 From 9d56fc9768c10cc0e0331cda787abff64186371b Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 20 Apr 2015 17:38:36 -0400 Subject: Fix for initialization failure when card returns FCI in data field. --- .../java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java index 5990a8b5a..1d09b281f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java @@ -200,7 +200,7 @@ public abstract class BaseNfcActivity extends BaseActivity { + "06" // Lc (number of bytes) + "D27600012401" // Data (6 bytes) + "00"; // Le - if ( ! nfcCommunicate(opening).equals(accepted)) { // activate connection + if ( ! nfcCommunicate(opening).endsWith(accepted)) { // activate connection throw new IOException("Initialization failed!"); } -- cgit v1.2.3 From 5a4a8f2b6a0d5d8eb9b23269f72a369cab25f8ff Mon Sep 17 00:00:00 2001 From: Adithya Abraham Philip Date: Tue, 21 Apr 2015 23:16:33 +0530 Subject: added log to QR code scan/import fail --- .../keychain/ui/ImportKeysProxyActivity.java | 23 +++++++++++----------- 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java index b9f1bf870..5c49de727 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java @@ -58,13 +58,12 @@ import java.util.Locale; public class ImportKeysProxyActivity extends FragmentActivity { public static final String ACTION_QR_CODE_API = OpenKeychainIntents.IMPORT_KEY_FROM_QR_CODE; + // implies activity returns scanned fingerprint as extra and does not import public static final String ACTION_SCAN_WITH_RESULT = Constants.INTENT_PREFIX + "SCAN_QR_CODE_WITH_RESULT"; public static final String ACTION_SCAN_IMPORT = Constants.INTENT_PREFIX + "SCAN_QR_CODE_IMPORT"; public static final String EXTRA_FINGERPRINT = "fingerprint"; - boolean returnResult; - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -82,10 +81,8 @@ public class ImportKeysProxyActivity extends FragmentActivity { if (scheme != null && scheme.toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) { // Scanning a fingerprint directly with Barcode Scanner, thus we already have scanned - returnResult = false; processScannedContent(dataUri); } else if (ACTION_SCAN_IMPORT.equals(action) || ACTION_QR_CODE_API.equals(action)) { - returnResult = false; IntentIntegrator integrator = new IntentIntegrator(this); integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES) .setPrompt(getString(R.string.import_qr_code_text)) @@ -93,7 +90,6 @@ public class ImportKeysProxyActivity extends FragmentActivity { integrator.setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); integrator.initiateScan(); } else if (ACTION_SCAN_WITH_RESULT.equals(action)) { - returnResult = true; IntentIntegrator integrator = new IntentIntegrator(this); integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES) .setPrompt(getString(R.string.import_qr_code_text)) @@ -103,7 +99,6 @@ public class ImportKeysProxyActivity extends FragmentActivity { } else if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { // Check to see if the Activity started due to an Android Beam if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - returnResult = false; handleActionNdefDiscovered(getIntent()); } else { Log.e(Constants.TAG, "Android Beam not supported by Android < 4.1"); @@ -148,6 +143,8 @@ public class ImportKeysProxyActivity extends FragmentActivity { private void processScannedContent(Uri uri) { + String action = getIntent().getAction(); + Log.d(Constants.TAG, "scanned: " + uri); String fingerprint = null; @@ -166,7 +163,7 @@ public class ImportKeysProxyActivity extends FragmentActivity { return; } - if (returnResult) { + if (ACTION_SCAN_WITH_RESULT.equals(action)) { Intent result = new Intent(); result.putExtra(EXTRA_FINGERPRINT, fingerprint); setResult(RESULT_OK, result); @@ -177,15 +174,17 @@ public class ImportKeysProxyActivity extends FragmentActivity { } public void returnResult(Intent data) { - if (returnResult) { - setResult(RESULT_OK, data); - finish(); - } else { + String action = getIntent().getAction(); + + if (ACTION_QR_CODE_API.equals(action)) { // display last log message but as Toast for calls from outside OpenKeychain OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); String str = getString(result.getLog().getLast().mType.getMsgId()); Toast.makeText(this, str, Toast.LENGTH_LONG).show(); finish(); + } else { + setResult(RESULT_OK, data); + finish(); } } @@ -209,7 +208,7 @@ public class ImportKeysProxyActivity extends FragmentActivity { } - private void startImportService (ArrayList keyRings) { + private void startImportService(ArrayList keyRings) { // Message is received after importing is done in KeychainIntentService ServiceProgressHandler serviceHandler = new ServiceProgressHandler( -- cgit v1.2.3 From 713f6ba6c79b703881e39d9d642b47f8140f8ace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 21 Apr 2015 20:15:10 +0200 Subject: Simplify ImportKeysProxyActivity --- .../keychain/ui/ImportKeysProxyActivity.java | 41 +++++++--------------- 1 file changed, 12 insertions(+), 29 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java index 5c49de727..29f2511a0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java @@ -82,14 +82,8 @@ public class ImportKeysProxyActivity extends FragmentActivity { // Scanning a fingerprint directly with Barcode Scanner, thus we already have scanned processScannedContent(dataUri); - } else if (ACTION_SCAN_IMPORT.equals(action) || ACTION_QR_CODE_API.equals(action)) { - IntentIntegrator integrator = new IntentIntegrator(this); - integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES) - .setPrompt(getString(R.string.import_qr_code_text)) - .setResultDisplayDuration(0); - integrator.setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - integrator.initiateScan(); - } else if (ACTION_SCAN_WITH_RESULT.equals(action)) { + } else if (ACTION_SCAN_WITH_RESULT.equals(action) + || ACTION_SCAN_IMPORT.equals(action) || ACTION_QR_CODE_API.equals(action)) { IntentIntegrator integrator = new IntentIntegrator(this); integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES) .setPrompt(getString(R.string.import_qr_code_text)) @@ -142,34 +136,28 @@ public class ImportKeysProxyActivity extends FragmentActivity { } private void processScannedContent(Uri uri) { - String action = getIntent().getAction(); Log.d(Constants.TAG, "scanned: " + uri); - String fingerprint = null; - // example: openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282 if (uri != null && uri.getScheme() != null && uri.getScheme().toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) { - fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH); - } + String fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH); - if (fingerprint == null) { + if (ACTION_SCAN_WITH_RESULT.equals(action)) { + Intent result = new Intent(); + result.putExtra(EXTRA_FINGERPRINT, fingerprint); + setResult(RESULT_OK, result); + finish(); + } else { + importKeys(fingerprint); + } + } else { SingletonResult result = new SingletonResult( SingletonResult.RESULT_ERROR, OperationResult.LogType.MSG_WRONG_QR_CODE); Intent intent = new Intent(); intent.putExtra(SingletonResult.EXTRA_RESULT, result); returnResult(intent); - return; - } - - if (ACTION_SCAN_WITH_RESULT.equals(action)) { - Intent result = new Intent(); - result.putExtra(EXTRA_FINGERPRINT, fingerprint); - setResult(RESULT_OK, result); - finish(); - } else { - importKeys(fingerprint); } } @@ -189,23 +177,19 @@ public class ImportKeysProxyActivity extends FragmentActivity { } public void importKeys(byte[] keyringData) { - ParcelableKeyRing keyEntry = new ParcelableKeyRing(keyringData); ArrayList selectedEntries = new ArrayList<>(); selectedEntries.add(keyEntry); startImportService(selectedEntries); - } public void importKeys(String fingerprint) { - ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null); ArrayList selectedEntries = new ArrayList<>(); selectedEntries.add(keyEntry); startImportService(selectedEntries); - } private void startImportService(ArrayList keyRings) { @@ -281,7 +265,6 @@ public class ImportKeysProxyActivity extends FragmentActivity { // start service with intent startService(intent); - } /** -- cgit v1.2.3 From 09cb00a87c78256f7d085dfde06b61e89ff28a25 Mon Sep 17 00:00:00 2001 From: Adithya Abraham Philip Date: Wed, 22 Apr 2015 03:32:59 +0530 Subject: corrected possible null pointer --- .../java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 81ff87216..bf7e41045 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -217,7 +217,7 @@ public class ImportKeysListFragment extends ListFragment implements mLoaderState = new BytesLoaderState(bytes, dataUri); } else if (query != null) { Preferences.CloudSearchPrefs cloudSearchPrefs; - if (keyserver != null) { + if (keyserver == null) { cloudSearchPrefs = Preferences.getPreferences(getActivity()).getCloudSearchPrefs(); } else { cloudSearchPrefs = new Preferences.CloudSearchPrefs(true, true, keyserver); -- cgit v1.2.3 From 2571d25058706ae2725e0a70db4fa1c7eacfc153 Mon Sep 17 00:00:00 2001 From: Adithya Abraham Philip Date: Wed, 22 Apr 2015 06:15:14 +0530 Subject: keyserver verification on addition --- .../keychain/ui/SettingsKeyServerActivity.java | 60 +++- .../ui/dialog/AddKeyserverDialogFragment.java | 320 +++++++++++++++++++++ 2 files changed, 379 insertions(+), 1 deletion(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddKeyserverDialogFragment.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java index 9f2e46b38..8f025c769 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java @@ -20,6 +20,9 @@ package org.sufficientlysecure.keychain.ui; import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -28,6 +31,8 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.base.BaseActivity; +import org.sufficientlysecure.keychain.ui.dialog.AddKeyserverDialogFragment; +import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.widget.Editor; import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener; import org.sufficientlysecure.keychain.ui.widget.KeyServerEditor; @@ -95,7 +100,7 @@ public class SettingsKeyServerActivity extends BaseActivity implements OnClickLi Intent intent = getIntent(); String servers[] = intent.getStringArrayExtra(EXTRA_KEY_SERVERS); makeServerList(servers); - } + } @Override protected void initLayout() { @@ -124,10 +129,63 @@ public class SettingsKeyServerActivity extends BaseActivity implements OnClickLi } + // button to add keyserver clicked public void onClick(View v) { + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + Bundle data = message.getData(); + switch (message.what) { + case AddKeyserverDialogFragment.MESSAGE_OKAY: { + boolean verified = data.getBoolean(AddKeyserverDialogFragment.MESSAGE_VERIFIED); + if (verified) { + Notify.create(SettingsKeyServerActivity.this, + R.string.add_keyserver_verified, Notify.Style.OK).show(); + } else { + Notify.create(SettingsKeyServerActivity.this, + R.string.add_keyserver_without_verification, + Notify.Style.WARN).show(); + } + String keyserver = data.getString(AddKeyserverDialogFragment.MESSAGE_KEYSERVER); + addKeyserver(keyserver); + break; + } + case AddKeyserverDialogFragment.MESSAGE_VERIFICATION_FAILED: { + AddKeyserverDialogFragment.FailureReason failureReason = + (AddKeyserverDialogFragment.FailureReason) data.getSerializable( + AddKeyserverDialogFragment.MESSAGE_FAILURE_REASON); + switch (failureReason) { + case CONNECTION_FAILED: { + Notify.create(SettingsKeyServerActivity.this, + R.string.add_keyserver_connection_failed, + Notify.Style.ERROR).show(); + break; + } + case INVALID_URL: { + Notify.create(SettingsKeyServerActivity.this, + R.string.add_keyserver_invalid_url, + Notify.Style.ERROR).show(); + break; + } + } + break; + } + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + AddKeyserverDialogFragment dialogFragment = AddKeyserverDialogFragment + .newInstance(messenger, R.string.add_keyserver_dialog_title); + dialogFragment.show(getSupportFragmentManager(), "addKeyserverDialog"); + } + + public void addKeyserver(String keyserverUrl) { KeyServerEditor view = (KeyServerEditor) mInflater.inflate(R.layout.key_server_editor, mEditors, false); view.setEditorListener(this); + view.setValue(keyserverUrl); mEditors.addView(view); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddKeyserverDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddKeyserverDialogFragment.java new file mode 100644 index 000000000..cbef5950f --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddKeyserverDialogFragment.java @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2012-2014 Dominik Schürmann + * + * 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 . + */ + +package org.sufficientlysecure.keychain.ui.dialog; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.support.v4.app.DialogFragment; +import android.test.suitebuilder.TestSuiteBuilder; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.TlsHelper; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +import javax.net.ssl.HttpsURLConnection; + +public class AddKeyserverDialogFragment extends DialogFragment implements OnEditorActionListener { + private static final String ARG_MESSENGER = "messenger"; + private static final String ARG_TITLE = "title"; + + public static final int MESSAGE_OKAY = 1; + public static final int MESSAGE_VERIFICATION_FAILED = 2; + + public static final String MESSAGE_KEYSERVER = "new_keyserver"; + public static final String MESSAGE_VERIFIED = "verified"; + public static final String MESSAGE_FAILURE_REASON = "failure_reason"; + + private Messenger mMessenger; + private EditText mKeyserverEditText; + private CheckBox mVerifyKeyserverCheckBox; + + public static enum FailureReason { + INVALID_URL, + CONNECTION_FAILED + } + + ; + + /** + * Creates new instance of this dialog fragment + * + * @param title title of dialog + * @param messenger to communicate back after setting the passphrase + * @return + */ + public static AddKeyserverDialogFragment newInstance(Messenger messenger, int title) { + AddKeyserverDialogFragment frag = new AddKeyserverDialogFragment(); + Bundle args = new Bundle(); + args.putInt(ARG_TITLE, title); + args.putParcelable(ARG_MESSENGER, messenger); + + frag.setArguments(args); + + return frag; + } + + /** + * Creates dialog + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Activity activity = getActivity(); + + int title = getArguments().getInt(ARG_TITLE); + mMessenger = getArguments().getParcelable(ARG_MESSENGER); + + CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity); + + alert.setTitle(title); + + LayoutInflater inflater = activity.getLayoutInflater(); + View view = inflater.inflate(R.layout.add_keyserver_dialog, null); + alert.setView(view); + + mKeyserverEditText = (EditText) view.findViewById(R.id.keyserver_url_edit_text); + mVerifyKeyserverCheckBox = (CheckBox) view.findViewById(R.id.verify_keyserver_checkbox); + + // we don't want dialog to be dismissed on click, thereby requiring the hack seen below + // and in onStart + alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + // we need to have an empty listener to prevent errors on some devices as mentioned + // at http://stackoverflow.com/q/13746412/3000919 + // actual listener set in onStart + } + }); + + alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int id) { + dismiss(); + } + }); + + // Hack to open keyboard. + // This is the only method that I found to work across all Android versions + // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/ + // Notes: * onCreateView can't be used because we want to add buttons to the dialog + // * opening in onActivityCreated does not work on Android 4.4 + mKeyserverEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + mKeyserverEditText.post(new Runnable() { + @Override + public void run() { + InputMethodManager imm = (InputMethodManager) getActivity() + .getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(mKeyserverEditText, InputMethodManager.SHOW_IMPLICIT); + } + }); + } + }); + mKeyserverEditText.requestFocus(); + + mKeyserverEditText.setImeActionLabel(getString(android.R.string.ok), + EditorInfo.IME_ACTION_DONE); + mKeyserverEditText.setOnEditorActionListener(this); + + return alert.show(); + } + + @Override + public void onStart() { + super.onStart(); + AlertDialog addKeyserverDialog = (AlertDialog) getDialog(); + if (addKeyserverDialog != null) { + Button positiveButton = addKeyserverDialog.getButton(Dialog.BUTTON_POSITIVE); + positiveButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String keyserverUrl = mKeyserverEditText.getText().toString(); + if (mVerifyKeyserverCheckBox.isChecked()) { + verifyConnection(keyserverUrl); + } else { + dismiss(); + // return unverified keyserver back to activity + addKeyserver(keyserverUrl, false); + } + } + }); + } + } + + public void addKeyserver(String keyserver, boolean verified) { + dismiss(); + Bundle data = new Bundle(); + data.putString(MESSAGE_KEYSERVER, keyserver); + data.putBoolean(MESSAGE_VERIFIED, verified); + + sendMessageToHandler(MESSAGE_OKAY, data); + } + + public void verificationFailed(FailureReason reason) { + Bundle data = new Bundle(); + data.putSerializable(MESSAGE_FAILURE_REASON, reason); + + sendMessageToHandler(MESSAGE_VERIFICATION_FAILED, data); + } + + public void verifyConnection(String keyserver) { + + new AsyncTask() { + ProgressDialog mProgressDialog; + String mKeyserver; + + @Override + protected void onPreExecute() { + mProgressDialog = new ProgressDialog(getActivity()); + mProgressDialog.setMessage(getString(R.string.progress_verifying_keyserver_url)); + mProgressDialog.setCancelable(false); + mProgressDialog.show(); + } + + @Override + protected FailureReason doInBackground(String... keyservers) { + mKeyserver = keyservers[0]; + FailureReason reason = null; + try { + // replace hkps/hkp scheme and reconstruct Uri + Uri keyserverUri = Uri.parse(mKeyserver); + String scheme = keyserverUri.getScheme(); + String schemeSpecificPart = keyserverUri.getSchemeSpecificPart(); + String fragment = keyserverUri.getFragment(); + if (scheme == null) throw new MalformedURLException(); + if (scheme.equalsIgnoreCase("hkps")) scheme = "https"; + else if (scheme.equalsIgnoreCase("hkp")) scheme = "http"; + URI newKeyserver = new URI(scheme, schemeSpecificPart, fragment); + + Log.d("Converted URL", newKeyserver.toString()); + TlsHelper.openConnection(newKeyserver.toURL()).getInputStream(); + } catch (TlsHelper.TlsHelperException e) { + reason = FailureReason.CONNECTION_FAILED; + } catch (MalformedURLException e) { + Log.w(Constants.TAG, "Invalid keyserver URL entered by user."); + reason = FailureReason.INVALID_URL; + } catch (URISyntaxException e) { + Log.w(Constants.TAG, "Invalid keyserver URL entered by user."); + reason = FailureReason.INVALID_URL; + } catch (IOException e) { + Log.w(Constants.TAG, "Could not connect to entered keyserver url"); + reason = FailureReason.CONNECTION_FAILED; + } + return reason; + } + + @Override + protected void onPostExecute(FailureReason failureReason) { + mProgressDialog.dismiss(); + if (failureReason == null) { + addKeyserver(mKeyserver, true); + } else { + verificationFailed(failureReason); + } + } + }.execute(keyserver); + } + + @Override + public void onDismiss(DialogInterface dialog) { + super.onDismiss(dialog); + + // hide keyboard on dismiss + hideKeyboard(); + } + + private void hideKeyboard() { + if (getActivity() == null) { + return; + } + InputMethodManager inputManager = (InputMethodManager) getActivity() + .getSystemService(Context.INPUT_METHOD_SERVICE); + + //check if no view has focus: + View v = getActivity().getCurrentFocus(); + if (v == null) + return; + + inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0); + } + + /** + * Associate the "done" button on the soft keyboard with the okay button in the view + */ + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (EditorInfo.IME_ACTION_DONE == actionId) { + AlertDialog dialog = ((AlertDialog) getDialog()); + Button bt = dialog.getButton(AlertDialog.BUTTON_POSITIVE); + + bt.performClick(); + return true; + } + return false; + } + + /** + * Send message back to handler which is initialized in a activity + * + * @param what Message integer you want to send + */ + private void sendMessageToHandler(Integer what, Bundle data) { + Message msg = Message.obtain(); + msg.what = what; + if (data != null) { + msg.setData(data); + } + + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); + } catch (NullPointerException e) { + Log.w(Constants.TAG, "Messenger is null!", e); + } + } +} -- cgit v1.2.3 From aecebd2ac012804477ab3ec59ca6cf0417a56c5f Mon Sep 17 00:00:00 2001 From: Manoj Khanna Date: Wed, 22 Apr 2015 18:30:35 +0530 Subject: Fixed Snackbar behind layout Surrounded string with quotes --- .../keychain/ui/EncryptFilesFragment.java | 14 +++++++------- .../keychain/ui/EncryptTextFragment.java | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java index ccb4a6355..458810541 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -202,7 +202,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { } catch (IOException e) { Notify.create(getActivity(), getActivity().getString(R.string.error_file_added_already, FileHelper.getFilename(getActivity(), inputUri)), - Notify.Style.ERROR).show(); + Notify.Style.ERROR).show(this); return; } mSelectedFiles.requestFocus(); @@ -230,7 +230,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { private void encryptClicked(boolean share) { if (mFilesModels.isEmpty()) { Notify.create(getActivity(), R.string.error_no_file_selected, - Notify.Style.ERROR).show(); + Notify.Style.ERROR).show(this); return; } if (share) { @@ -247,7 +247,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { } else { if (mFilesModels.size() > 1) { Notify.create(getActivity(), R.string.error_multi_not_supported, - Notify.Style.ERROR).show(); + Notify.Style.ERROR).show(this); return; } showOutputFileDialog(); @@ -330,7 +330,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { if (mFilesModels.isEmpty()) { Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR) - .show(); + .show(this); return false; } else if (mFilesModels.size() > 1 && !mShareAfterEncrypt) { Log.e(Constants.TAG, "Aborting: mInputUris.size() > 1 && !mShareAfterEncrypt"); @@ -347,12 +347,12 @@ public class EncryptFilesFragment extends CryptoOperationFragment { if (mPassphrase == null) { Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR) - .show(); + .show(this); return false; } if (mPassphrase.isEmpty()) { Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR) - .show(); + .show(this); return false; } @@ -365,7 +365,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { // Files must be encrypted, only text can be signed-only right now if (!gotEncryptionKeys) { Notify.create(getActivity(), R.string.select_encryption_key, Notify.Style.ERROR) - .show(); + .show(this); return false; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java index b37a2ca79..3f9147cc4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java @@ -288,9 +288,9 @@ public class EncryptTextFragment extends CryptoOperationFragment { } protected boolean inputIsValid() { - if (mMessage == null) { - Notify.create(getActivity(), R.string.error_message, Notify.Style.ERROR) - .show(); + if (mMessage == null || mMessage.isEmpty()) { + Notify.create(getActivity(), R.string.error_empty_text, Notify.Style.ERROR) + .show(this); return false; } @@ -299,12 +299,12 @@ public class EncryptTextFragment extends CryptoOperationFragment { if (mSymmetricPassphrase == null) { Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR) - .show(); + .show(this); return false; } if (mSymmetricPassphrase.isEmpty()) { Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR) - .show(); + .show(this); return false; } @@ -316,7 +316,7 @@ public class EncryptTextFragment extends CryptoOperationFragment { if (!gotEncryptionKeys && mSigningKeyId == 0) { Notify.create(getActivity(), R.string.select_encryption_or_signature_key, Notify.Style.ERROR) - .show(); + .show(this); return false; } } -- cgit v1.2.3 From 3ee2c6fc6a2a5e18e333bb82f16d0efe365df2b8 Mon Sep 17 00:00:00 2001 From: vectorijk Date: Fri, 24 Apr 2015 00:00:58 -0700 Subject: Introduce and Switch to mikepenz/MaterialDrawer --- .../keychain/ui/MainActivity.java | 143 +++++++++++++++++---- 1 file changed, 118 insertions(+), 25 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java index 5fa3edba4..045d1e658 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -1,6 +1,7 @@ /* * Copyright (C) 2012-2014 Dominik Schürmann * Copyright (C) 2014 Vincent Breitmoser + * Copyright (C) 2015 Kai Jiang * * 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 @@ -20,6 +21,20 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.widget.AdapterView; +import android.widget.Toast; + +import com.mikepenz.community_material_typeface_library.CommunityMaterial; +import com.mikepenz.google_material_typeface_library.GoogleMaterial; +import com.mikepenz.iconics.typeface.FontAwesome; +import com.mikepenz.materialdrawer.Drawer; +import com.mikepenz.materialdrawer.model.DividerDrawerItem; +import com.mikepenz.materialdrawer.model.SectionDrawerItem; +import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; +import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult; @@ -29,27 +44,78 @@ import org.sufficientlysecure.keychain.util.FabContainer; import it.neokree.materialnavigationdrawer.MaterialNavigationDrawer; -public class MainActivity extends MaterialNavigationDrawer implements FabContainer { - - @Override - public void init(Bundle savedInstanceState) { - // don't open drawer on first run - disableLearningPattern(); +public class MainActivity extends ActionBarActivity implements FabContainer { -// addMultiPaneSupport(); + public Drawer.Result result; - // set the header image - // create and set the header - setDrawerHeaderImage(R.drawable.drawer_header); +// public Drawer.Result result; + @Override + public void onCreate(Bundle savedInstanceState) { +// // don't open drawer on first run +// disableLearningPattern(); +// +//// addMultiPaneSupport(); +// +// // set the header image +// // create and set the header +// setDrawerHeaderImage(R.drawable.drawer_header); +// +// // create sections +// addSection(newSection(getString(R.string.nav_keys), R.drawable.ic_vpn_key_black_24dp, new KeyListFragment())); +// addSection(newSection(getString(R.string.nav_encrypt_decrypt), R.drawable.ic_lock_black_24dp, new EncryptDecryptOverviewFragment())); +// addSection(newSection(getString(R.string.title_api_registered_apps), R.drawable.ic_apps_black_24dp, new AppsListFragment())); +// +// // create bottom section +// addBottomSection(newSection(getString(R.string.menu_preferences), R.drawable.ic_settings_black_24dp, new Intent(this, SettingsActivity.class))); +// addBottomSection(newSection(getString(R.string.menu_help), R.drawable.ic_help_black_24dp, new Intent(this, HelpActivity.class))); + super.onCreate(savedInstanceState); + setContentView(R.layout.main_activity); - // create sections - addSection(newSection(getString(R.string.nav_keys), R.drawable.ic_vpn_key_black_24dp, new KeyListFragment())); - addSection(newSection(getString(R.string.nav_encrypt_decrypt), R.drawable.ic_lock_black_24dp, new EncryptDecryptOverviewFragment())); - addSection(newSection(getString(R.string.title_api_registered_apps), R.drawable.ic_apps_black_24dp, new AppsListFragment())); + final Toolbar toolbar = (Toolbar) findViewById(R.id.activity_main_toolbar); + toolbar.setTitle(R.string.app_name); + setSupportActionBar(toolbar); - // create bottom section - addBottomSection(newSection(getString(R.string.menu_preferences), R.drawable.ic_settings_black_24dp, new Intent(this, SettingsActivity.class))); - addBottomSection(newSection(getString(R.string.menu_help), R.drawable.ic_help_black_24dp, new Intent(this, HelpActivity.class))); + result = new Drawer() + .withActivity(this) + .withHeader(R.layout.main_drawer_header) + .withToolbar(toolbar) + .addDrawerItems( + new PrimaryDrawerItem().withName(R.string.nav_keys).withIcon(CommunityMaterial.Icon.cmd_key).withIdentifier(1).withCheckable(false), + new PrimaryDrawerItem().withName(R.string.nav_encrypt_decrypt).withIcon(FontAwesome.Icon.faw_lock).withIdentifier(2).withCheckable(false), + new PrimaryDrawerItem().withName(R.string.title_api_registered_apps).withIcon(CommunityMaterial.Icon.cmd_apps).withIdentifier(3).withCheckable(false), + new DividerDrawerItem(), + new PrimaryDrawerItem().withName(R.string.menu_preferences).withIcon(GoogleMaterial.Icon.gmd_settings).withIdentifier(4).withCheckable(false), + new PrimaryDrawerItem().withName(R.string.menu_help).withIcon(CommunityMaterial.Icon.cmd_help_circle).withIdentifier(5).withCheckable(false) + ) + .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id, IDrawerItem drawerItem) { + if (drawerItem != null) { + Intent intent = null; + switch(drawerItem.getIdentifier()) { + case 1: + break; + case 2: + break; + case 3: + break; + case 4: + intent = new Intent(MainActivity.this, SettingsActivity.class); + break; + case 5: + intent = new Intent(MainActivity.this, HelpActivity.class); + break; + } + if (intent != null) { + MainActivity.this.startActivity(intent); + } + } + Toast.makeText(MainActivity.this, Integer.toString(drawerItem.getIdentifier()), Toast.LENGTH_SHORT).show(); + } + }) + .withSelectedItem(-1) + .withSavedInstance(savedInstanceState) + .build(); // if this is the first time show first time activity Preferences prefs = Preferences.getPreferences(this); @@ -69,20 +135,47 @@ public class MainActivity extends MaterialNavigationDrawer implements FabContain } } +// private void onPreferenceSelected() { +// Intent intent = new Intent(this, SettingsActivity.class); +// startActivity(intent); +// } +// +// private void onHelpSelected() { +// Intent intent = new Intent(this, HelpActivity.class); +// startActivity(intent); +// } + @Override - public void fabMoveUp(int height) { - Object fragment = getCurrentSection().getTargetFragment(); - if (fragment instanceof FabContainer) { - ((FabContainer) fragment).fabMoveUp(height); + protected void onSaveInstanceState(Bundle outState) { + //add the values which need to be saved from the drawer to the bundle + outState = result.saveInstanceState(outState); + super.onSaveInstanceState(outState); + } + + @Override + public void onBackPressed(){ + //handle the back press :D close the drawer first and if the drawer is closed close the activity + if (result != null && result.isDrawerOpen()) { + result.closeDrawer(); + } else { + super.onBackPressed(); } } + @Override + public void fabMoveUp(int height) { +// Object fragment = getCurrentSection().getTargetFragment(); +// if (fragment instanceof FabContainer) { +// ((FabContainer) fragment).fabMoveUp(height); +// } + } + @Override public void fabRestorePosition() { - Object fragment = getCurrentSection().getTargetFragment(); - if (fragment instanceof FabContainer) { - ((FabContainer) fragment).fabRestorePosition(); - } +// Object fragment = getCurrentSection().getTargetFragment(); +// if (fragment instanceof FabContainer) { +// ((FabContainer) fragment).fabRestorePosition(); +// } } } -- cgit v1.2.3 From b323fc29c29ec2a7cba87ed19c7809b843a1fd60 Mon Sep 17 00:00:00 2001 From: vectorijk Date: Fri, 24 Apr 2015 03:21:54 -0700 Subject: Fix #1197 Switch to MaterialDrawer library --- .../keychain/ui/MainActivity.java | 129 ++++++++++++++------- 1 file changed, 85 insertions(+), 44 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java index 045d1e658..fa4f07d88 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -21,56 +21,48 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; import android.support.v7.app.ActionBarActivity; import android.support.v7.widget.Toolbar; import android.view.View; import android.widget.AdapterView; -import android.widget.Toast; import com.mikepenz.community_material_typeface_library.CommunityMaterial; import com.mikepenz.google_material_typeface_library.GoogleMaterial; import com.mikepenz.iconics.typeface.FontAwesome; import com.mikepenz.materialdrawer.Drawer; -import com.mikepenz.materialdrawer.model.DividerDrawerItem; -import com.mikepenz.materialdrawer.model.SectionDrawerItem; import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.remote.ui.AppsListFragment; -import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.FabContainer; - -import it.neokree.materialnavigationdrawer.MaterialNavigationDrawer; +import org.sufficientlysecure.keychain.util.Preferences; public class MainActivity extends ActionBarActivity implements FabContainer { public Drawer.Result result; -// public Drawer.Result result; + private KeyListFragment mKeyListFragment ; + private AppsListFragment mAppsListFragment; + private EncryptDecryptOverviewFragment mEncryptDecryptOverviewFragment; + private Fragment lastUsedFragment; + @Override public void onCreate(Bundle savedInstanceState) { -// // don't open drawer on first run -// disableLearningPattern(); -// -//// addMultiPaneSupport(); -// -// // set the header image -// // create and set the header -// setDrawerHeaderImage(R.drawable.drawer_header); -// -// // create sections -// addSection(newSection(getString(R.string.nav_keys), R.drawable.ic_vpn_key_black_24dp, new KeyListFragment())); -// addSection(newSection(getString(R.string.nav_encrypt_decrypt), R.drawable.ic_lock_black_24dp, new EncryptDecryptOverviewFragment())); -// addSection(newSection(getString(R.string.title_api_registered_apps), R.drawable.ic_apps_black_24dp, new AppsListFragment())); -// -// // create bottom section -// addBottomSection(newSection(getString(R.string.menu_preferences), R.drawable.ic_settings_black_24dp, new Intent(this, SettingsActivity.class))); -// addBottomSection(newSection(getString(R.string.menu_help), R.drawable.ic_help_black_24dp, new Intent(this, HelpActivity.class))); super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); + //initialize FragmentLayout with KeyListFragment at first + Fragment mainFragment = new KeyListFragment(); + FragmentManager fm = getSupportFragmentManager(); + FragmentTransaction transaction = fm.beginTransaction(); + transaction.replace(R.id.main_fragment_container, mainFragment); + transaction.commit(); + final Toolbar toolbar = (Toolbar) findViewById(R.id.activity_main_toolbar); toolbar.setTitle(R.string.app_name); setSupportActionBar(toolbar); @@ -82,8 +74,10 @@ public class MainActivity extends ActionBarActivity implements FabContainer { .addDrawerItems( new PrimaryDrawerItem().withName(R.string.nav_keys).withIcon(CommunityMaterial.Icon.cmd_key).withIdentifier(1).withCheckable(false), new PrimaryDrawerItem().withName(R.string.nav_encrypt_decrypt).withIcon(FontAwesome.Icon.faw_lock).withIdentifier(2).withCheckable(false), - new PrimaryDrawerItem().withName(R.string.title_api_registered_apps).withIcon(CommunityMaterial.Icon.cmd_apps).withIdentifier(3).withCheckable(false), - new DividerDrawerItem(), + new PrimaryDrawerItem().withName(R.string.title_api_registered_apps).withIcon(CommunityMaterial.Icon.cmd_apps).withIdentifier(3).withCheckable(false) + ) + .addStickyDrawerItems( + // display and stick on bottom of drawer new PrimaryDrawerItem().withName(R.string.menu_preferences).withIcon(GoogleMaterial.Icon.gmd_settings).withIdentifier(4).withCheckable(false), new PrimaryDrawerItem().withName(R.string.menu_help).withIcon(CommunityMaterial.Icon.cmd_help_circle).withIdentifier(5).withCheckable(false) ) @@ -94,10 +88,13 @@ public class MainActivity extends ActionBarActivity implements FabContainer { Intent intent = null; switch(drawerItem.getIdentifier()) { case 1: + onKeysSelected(); break; case 2: + onEnDecryptSelected(); break; case 3: + onAppsSelected(); break; case 4: intent = new Intent(MainActivity.this, SettingsActivity.class); @@ -110,7 +107,6 @@ public class MainActivity extends ActionBarActivity implements FabContainer { MainActivity.this.startActivity(intent); } } - Toast.makeText(MainActivity.this, Integer.toString(drawerItem.getIdentifier()), Toast.LENGTH_SHORT).show(); } }) .withSelectedItem(-1) @@ -135,15 +131,58 @@ public class MainActivity extends ActionBarActivity implements FabContainer { } } -// private void onPreferenceSelected() { -// Intent intent = new Intent(this, SettingsActivity.class); -// startActivity(intent); -// } -// -// private void onHelpSelected() { -// Intent intent = new Intent(this, HelpActivity.class); -// startActivity(intent); -// } + private void clearFragments() { + mKeyListFragment = null; + mAppsListFragment = null; + mEncryptDecryptOverviewFragment = null; + + getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); + } + + private void setFragment(Fragment fragment) { + setFragment(fragment, true); + } + + private void setFragment(Fragment fragment, boolean addToBackStack) { + this.lastUsedFragment = fragment; + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + ft.replace(R.id.main_fragment_container, fragment); + if (addToBackStack) { + ft.addToBackStack(null); + } + ft.commit(); + } + + private boolean onKeysSelected() { + clearFragments(); + + if (mKeyListFragment == null) { + mKeyListFragment = new KeyListFragment(); + } + + setFragment(mKeyListFragment, false); + return true; + } + + private boolean onEnDecryptSelected() { + clearFragments(); + if (mEncryptDecryptOverviewFragment == null) { + mEncryptDecryptOverviewFragment = new EncryptDecryptOverviewFragment(); + } + + setFragment(mEncryptDecryptOverviewFragment); + return true; + } + + private boolean onAppsSelected() { + clearFragments(); + if (mAppsListFragment == null) { + mAppsListFragment = new AppsListFragment(); + } + + setFragment(mAppsListFragment); + return true; + } @Override protected void onSaveInstanceState(Bundle outState) { @@ -164,18 +203,20 @@ public class MainActivity extends ActionBarActivity implements FabContainer { @Override public void fabMoveUp(int height) { -// Object fragment = getCurrentSection().getTargetFragment(); -// if (fragment instanceof FabContainer) { -// ((FabContainer) fragment).fabMoveUp(height); -// } + Object fragment = getSupportFragmentManager() + .findFragmentById(R.id.main_fragment_container); + if (fragment instanceof FabContainer) { + ((FabContainer) fragment).fabMoveUp(height); + } } @Override public void fabRestorePosition() { -// Object fragment = getCurrentSection().getTargetFragment(); -// if (fragment instanceof FabContainer) { -// ((FabContainer) fragment).fabRestorePosition(); -// } + Object fragment = getSupportFragmentManager() + .findFragmentById(R.id.main_fragment_container); + if (fragment instanceof FabContainer) { + ((FabContainer) fragment).fabRestorePosition(); + } } } -- cgit v1.2.3 From 96853a15ee535860fd789bc5be4979f2205e959c Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 24 Apr 2015 19:00:33 +0200 Subject: revamp decrypt ui --- .../keychain/ui/DecryptFilesFragment.java | 6 +- .../keychain/ui/DecryptFragment.java | 416 +++++++++++++++------ .../keychain/ui/DecryptTextFragment.java | 74 ++-- 3 files changed, 347 insertions(+), 149 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java index 766e65e8b..6c1902af1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java @@ -240,7 +240,7 @@ public class DecryptFilesFragment extends DecryptFragment { } case KeychainIntentService.ACTION_DECRYPT_VERIFY: { // display signature result in activity - onResult(pgpResult); + loadVerifyResult(pgpResult); if (mDeleteAfter.isChecked()) { // Create and show dialog to delete original file @@ -308,4 +308,8 @@ public class DecryptFilesFragment extends DecryptFragment { } } + @Override + protected void onVerifyLoaded(boolean verified) { + + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java index 33209be86..d641f02f9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java @@ -17,26 +17,49 @@ package org.sufficientlysecure.keychain.ui; +import java.util.ArrayList; + import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import org.openintents.openpgp.OpenPgpSignatureResult; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; +import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; +import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.ui.util.Notify.Style; +import org.sufficientlysecure.keychain.util.Preferences; + -public abstract class DecryptFragment extends CryptoOperationFragment { - private static final int RESULT_CODE_LOOKUP_KEY = 0x00007006; +public abstract class DecryptFragment extends CryptoOperationFragment implements + LoaderManager.LoaderCallbacks { - protected long mSignatureKeyId = 0; + public static final int LOADER_ID_UNIFIED = 0; protected LinearLayout mResultLayout; @@ -46,155 +69,118 @@ public abstract class DecryptFragment extends CryptoOperationFragment { protected TextView mSignatureText; protected View mSignatureLayout; - protected View mSignatureDivider1; - protected View mSignatureDivider2; protected TextView mSignatureName; protected TextView mSignatureEmail; protected TextView mSignatureAction; + private OpenPgpSignatureResult mSignatureResult; + @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); - mResultLayout = (LinearLayout) getView().findViewById(R.id.result_main_layout); + mResultLayout = (LinearLayout) view.findViewById(R.id.result_main_layout); mResultLayout.setVisibility(View.GONE); - mEncryptionIcon = (ImageView) getView().findViewById(R.id.result_encryption_icon); - mEncryptionText = (TextView) getView().findViewById(R.id.result_encryption_text); - mSignatureIcon = (ImageView) getView().findViewById(R.id.result_signature_icon); - mSignatureText = (TextView) getView().findViewById(R.id.result_signature_text); - mSignatureLayout = getView().findViewById(R.id.result_signature_layout); - mSignatureDivider1 = getView().findViewById(R.id.result_signature_divider1); - mSignatureDivider2 = getView().findViewById(R.id.result_signature_divider2); - mSignatureName = (TextView) getView().findViewById(R.id.result_signature_name); - mSignatureEmail = (TextView) getView().findViewById(R.id.result_signature_email); - mSignatureAction = (TextView) getView().findViewById(R.id.result_signature_action); + mEncryptionIcon = (ImageView) view.findViewById(R.id.result_encryption_icon); + mEncryptionText = (TextView) view.findViewById(R.id.result_encryption_text); + mSignatureIcon = (ImageView) view.findViewById(R.id.result_signature_icon); + mSignatureText = (TextView) view.findViewById(R.id.result_signature_text); + mSignatureLayout = view.findViewById(R.id.result_signature_layout); + mSignatureName = (TextView) view.findViewById(R.id.result_signature_name); + mSignatureEmail = (TextView) view.findViewById(R.id.result_signature_email); + mSignatureAction = (TextView) view.findViewById(R.id.result_signature_action); } private void lookupUnknownKey(long unknownKeyId) { - Intent intent = new Intent(getActivity(), ImportKeysActivity.class); - intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER); - intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, unknownKeyId); - startActivityForResult(intent, RESULT_CODE_LOOKUP_KEY); - } - - private void showKey(long keyId) { - Intent viewKeyIntent = new Intent(getActivity(), ViewKeyActivity.class); - viewKeyIntent.setData(KeychainContract.KeyRings - .buildGenericKeyRingUri(keyId)); - startActivity(viewKeyIntent); - } - /** - * - * @return returns false if signature is invalid, key is revoked or expired. - */ - protected boolean onResult(DecryptVerifyResult decryptVerifyResult) { - final OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult(); + // Message is received after importing is done in KeychainIntentService + ServiceProgressHandler serviceHandler = new ServiceProgressHandler(getActivity()) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); - boolean valid = false; + if (message.arg1 == MessageStatus.OKAY.ordinal()) { + // get returned data bundle + Bundle returnData = message.getData(); - mSignatureKeyId = 0; - mResultLayout.setVisibility(View.VISIBLE); - if (signatureResult != null) { - mSignatureKeyId = signatureResult.getKeyId(); - - String userId = signatureResult.getPrimaryUserId(); - KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId); - if (userIdSplit.name != null) { - mSignatureName.setText(userIdSplit.name); - } else { - mSignatureName.setText(R.string.user_id_no_name); - } - if (userIdSplit.email != null) { - mSignatureEmail.setText(userIdSplit.email); - } else { - mSignatureEmail.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix(getActivity(), mSignatureKeyId)); - } + if (returnData == null) { + return; + } - if (signatureResult.isSignatureOnly()) { - mEncryptionText.setText(R.string.decrypt_result_not_encrypted); - KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.NOT_ENCRYPTED); - } else { - mEncryptionText.setText(R.string.decrypt_result_encrypted); - KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.ENCRYPTED); - } + final ImportKeyResult result = + returnData.getParcelable(OperationResult.EXTRA_RESULT); - switch (signatureResult.getStatus()) { - case OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED: { - mSignatureText.setText(R.string.decrypt_result_signature_certified); - KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.VERIFIED); + // if (!result.success()) { + result.createNotify(getActivity()).show(); + // } - setSignatureLayoutVisibility(View.VISIBLE); - setShowAction(mSignatureKeyId); + getLoaderManager().restartLoader(LOADER_ID_UNIFIED, null, DecryptFragment.this); - valid = true; - break; } + } + }; - case OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED: { - mSignatureText.setText(R.string.decrypt_result_signature_uncertified); - KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.UNVERIFIED); + // fill values for this action + Bundle data = new Bundle(); - setSignatureLayoutVisibility(View.VISIBLE); - setShowAction(mSignatureKeyId); + // search config + { + Preferences prefs = Preferences.getPreferences(getActivity()); + Preferences.CloudSearchPrefs cloudPrefs = + new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver()); + data.putString(KeychainIntentService.IMPORT_KEY_SERVER, cloudPrefs.keyserver); + } - valid = true; - break; - } + { + ParcelableKeyRing keyEntry = new ParcelableKeyRing(null, + KeyFormattingUtils.convertKeyIdToHex(unknownKeyId), null); + ArrayList selectedEntries = new ArrayList<>(); + selectedEntries.add(keyEntry); - case OpenPgpSignatureResult.SIGNATURE_KEY_MISSING: { - mSignatureText.setText(R.string.decrypt_result_signature_missing_key); - KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.UNKNOWN_KEY); - - setSignatureLayoutVisibility(View.VISIBLE); - mSignatureAction.setText(R.string.decrypt_result_action_Lookup); - mSignatureAction.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_file_download_grey_24dp, 0); - mSignatureLayout.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - lookupUnknownKey(mSignatureKeyId); - } - }); - - valid = true; - break; - } + data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, selectedEntries); + } - case OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED: { - mSignatureText.setText(R.string.decrypt_result_signature_expired_key); - KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.EXPIRED); + // Send all information needed to service to query keys in other thread + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYRING); + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - setSignatureLayoutVisibility(View.VISIBLE); - setShowAction(mSignatureKeyId); + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(serviceHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - valid = false; - break; - } + getActivity().startService(intent); - case OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED: { - mSignatureText.setText(R.string.decrypt_result_signature_revoked_key); - KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.REVOKED); + } - setSignatureLayoutVisibility(View.VISIBLE); - setShowAction(mSignatureKeyId); + private void showKey(long keyId) { + try { - valid = false; - break; - } + Intent viewKeyIntent = new Intent(getActivity(), ViewKeyActivity.class); + long masterKeyId = new ProviderHelper(getActivity()).getCachedPublicKeyRing( + KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId) + ).getMasterKeyId(); + viewKeyIntent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId)); + startActivity(viewKeyIntent); + + } catch (PgpKeyNotFoundException e) { + Notify.create(getActivity(), R.string.error_key_not_found, Style.ERROR); + } + } + + /** + * @return returns false if signature is invalid, key is revoked or expired. + */ + protected void loadVerifyResult(DecryptVerifyResult decryptVerifyResult) { - case OpenPgpSignatureResult.SIGNATURE_ERROR: { - mSignatureText.setText(R.string.decrypt_result_invalid_signature); - KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.INVALID); + mSignatureResult = decryptVerifyResult.getSignatureResult(); + mResultLayout.setVisibility(View.VISIBLE); - setSignatureLayoutVisibility(View.GONE); + // unsigned data + if (mSignatureResult == null) { - valid = false; - break; - } - } - } else { setSignatureLayoutVisibility(View.GONE); mSignatureText.setText(R.string.decrypt_result_no_signature); @@ -202,16 +188,27 @@ public abstract class DecryptFragment extends CryptoOperationFragment { mEncryptionText.setText(R.string.decrypt_result_encrypted); KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.ENCRYPTED); - valid = true; + getLoaderManager().destroyLoader(LOADER_ID_UNIFIED); + + onVerifyLoaded(true); + + return; + } + + if (mSignatureResult.isSignatureOnly()) { + mEncryptionText.setText(R.string.decrypt_result_not_encrypted); + KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.NOT_ENCRYPTED); + } else { + mEncryptionText.setText(R.string.decrypt_result_encrypted); + KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.ENCRYPTED); } - return valid; + getLoaderManager().restartLoader(LOADER_ID_UNIFIED, null, this); + } private void setSignatureLayoutVisibility(int visibility) { mSignatureLayout.setVisibility(visibility); - mSignatureDivider1.setVisibility(visibility); - mSignatureDivider2.setVisibility(visibility); } private void setShowAction(final long signatureKeyId) { @@ -225,4 +222,177 @@ public abstract class DecryptFragment extends CryptoOperationFragment { }); } + // These are the rows that we will retrieve. + static final String[] UNIFIED_PROJECTION = new String[]{ + KeychainContract.KeyRings._ID, + KeychainContract.KeyRings.MASTER_KEY_ID, + KeychainContract.KeyRings.USER_ID, + KeychainContract.KeyRings.IS_REVOKED, + KeychainContract.KeyRings.IS_EXPIRED, + KeychainContract.KeyRings.VERIFIED, + }; + + @SuppressWarnings("unused") + static final int INDEX_MASTER_KEY_ID = 1; + static final int INDEX_USER_ID = 2; + static final int INDEX_IS_REVOKED = 3; + static final int INDEX_IS_EXPIRED = 4; + static final int INDEX_VERIFIED = 5; + + @Override + public Loader onCreateLoader(int id, Bundle args) { + if (id != LOADER_ID_UNIFIED) { + return null; + } + + Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri( + mSignatureResult.getKeyId()); + return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + + if (loader.getId() != LOADER_ID_UNIFIED) { + return; + } + + // If the key is unknown, show it as such + if (data.getCount() == 0 || !data.moveToFirst()) { + showUnknownKeyStatus(); + return; + } + + long signatureKeyId = mSignatureResult.getKeyId(); + + String userId = data.getString(INDEX_USER_ID); + KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId); + if (userIdSplit.name != null) { + mSignatureName.setText(userIdSplit.name); + } else { + mSignatureName.setText(R.string.user_id_no_name); + } + if (userIdSplit.email != null) { + mSignatureEmail.setText(userIdSplit.email); + } else { + mSignatureEmail.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix( + getActivity(), mSignatureResult.getKeyId())); + } + + boolean isRevoked = data.getInt(INDEX_IS_REVOKED) != 0; + boolean isExpired = data.getInt(INDEX_IS_EXPIRED) != 0; + boolean isVerified = data.getInt(INDEX_VERIFIED) > 0; + + if (isRevoked) { + mSignatureText.setText(R.string.decrypt_result_signature_revoked_key); + KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.REVOKED); + + setSignatureLayoutVisibility(View.VISIBLE); + setShowAction(signatureKeyId); + + onVerifyLoaded(false); + + } else if (isExpired) { + mSignatureText.setText(R.string.decrypt_result_signature_expired_key); + KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.EXPIRED); + + setSignatureLayoutVisibility(View.VISIBLE); + setShowAction(signatureKeyId); + + onVerifyLoaded(true); + + } else if (isVerified) { + mSignatureText.setText(R.string.decrypt_result_signature_certified); + KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.VERIFIED); + + setSignatureLayoutVisibility(View.VISIBLE); + setShowAction(signatureKeyId); + + onVerifyLoaded(true); + + } else { + mSignatureText.setText(R.string.decrypt_result_signature_uncertified); + KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.UNVERIFIED); + + setSignatureLayoutVisibility(View.VISIBLE); + setShowAction(signatureKeyId); + + onVerifyLoaded(true); + } + + } + + @Override + public void onLoaderReset(Loader loader) { + + if (loader.getId() != LOADER_ID_UNIFIED) { + return; + } + + setSignatureLayoutVisibility(View.GONE); + + } + + private void showUnknownKeyStatus() { + + final long signatureKeyId = mSignatureResult.getKeyId(); + + int result = mSignatureResult.getStatus(); + if (result != OpenPgpSignatureResult.SIGNATURE_KEY_MISSING + && result != OpenPgpSignatureResult.SIGNATURE_ERROR) { + Log.e(Constants.TAG, "got missing status for non-missing key, shouldn't happen!"); + } + + String userId = mSignatureResult.getPrimaryUserId(); + KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId); + if (userIdSplit.name != null) { + mSignatureName.setText(userIdSplit.name); + } else { + mSignatureName.setText(R.string.user_id_no_name); + } + if (userIdSplit.email != null) { + mSignatureEmail.setText(userIdSplit.email); + } else { + mSignatureEmail.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix( + getActivity(), mSignatureResult.getKeyId())); + } + + switch (mSignatureResult.getStatus()) { + + case OpenPgpSignatureResult.SIGNATURE_KEY_MISSING: { + mSignatureText.setText(R.string.decrypt_result_signature_missing_key); + KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.UNKNOWN_KEY); + + setSignatureLayoutVisibility(View.VISIBLE); + mSignatureAction.setText(R.string.decrypt_result_action_Lookup); + mSignatureAction + .setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_file_download_grey_24dp, 0); + mSignatureLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + lookupUnknownKey(signatureKeyId); + } + }); + + onVerifyLoaded(true); + + break; + } + + case OpenPgpSignatureResult.SIGNATURE_ERROR: { + mSignatureText.setText(R.string.decrypt_result_invalid_signature); + KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.INVALID); + + setSignatureLayoutVisibility(View.GONE); + + onVerifyLoaded(false); + break; + } + + } + + } + + protected abstract void onVerifyLoaded(boolean verified); + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java index 9c6c89c43..1b9ae917f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java @@ -23,6 +23,9 @@ import android.os.Bundle; import android.os.Message; import android.os.Messenger; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Button; @@ -39,7 +42,6 @@ import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; import org.sufficientlysecure.keychain.ui.util.Notify; -import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ShareHelper; import java.io.UnsupportedEncodingException; @@ -54,6 +56,7 @@ public class DecryptTextFragment extends DecryptFragment { // model private String mCiphertext; + private boolean mShowMenuOptions = false; /** * Creates new instance of this fragment @@ -79,22 +82,6 @@ public class DecryptTextFragment extends DecryptFragment { mInvalidLayout = (LinearLayout) view.findViewById(R.id.decrypt_text_invalid); mText = (TextView) view.findViewById(R.id.decrypt_text_plaintext); - View vShareButton = view.findViewById(R.id.action_decrypt_share_plaintext); - vShareButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - startActivity(sendWithChooserExcludingEncrypt(mText.getText().toString())); - } - }); - - View vCopyButton = view.findViewById(R.id.action_decrypt_copy_plaintext); - vCopyButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - copyToClipboard(mText.getText().toString()); - } - }); - Button vInvalidButton = (Button) view.findViewById(R.id.decrypt_text_invalid_button); vInvalidButton.setOnClickListener(new View.OnClickListener() { @Override @@ -139,6 +126,8 @@ public class DecryptTextFragment extends DecryptFragment { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + String ciphertext = getArguments().getString(ARG_CIPHERTEXT); if (ciphertext != null) { mCiphertext = ciphertext; @@ -146,6 +135,33 @@ public class DecryptTextFragment extends DecryptFragment { } } + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + if (mShowMenuOptions) { + inflater.inflate(R.menu.decrypt_menu, menu); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.decrypt_share: { + startActivity(sendWithChooserExcludingEncrypt(mText.getText().toString())); + break; + } + case R.id.decrypt_copy: { + copyToClipboard(mText.getText().toString()); + break; + } + default: { + return super.onOptionsItemSelected(item); + } + } + + return true; + } + @Override protected void cryptoOperation(CryptoInputParcel cryptoInput) { // Send all information needed to service to decrypt in other thread @@ -206,15 +222,8 @@ public class DecryptTextFragment extends DecryptFragment { pgpResult.createNotify(getActivity()).show(); // display signature result in activity - boolean valid = onResult(pgpResult); + loadVerifyResult(pgpResult); - if (valid) { - mInvalidLayout.setVisibility(View.GONE); - mValidLayout.setVisibility(View.VISIBLE); - } else { - mInvalidLayout.setVisibility(View.VISIBLE); - mValidLayout.setVisibility(View.GONE); - } } else { pgpResult.createNotify(getActivity()).show(); // TODO: show also invalid layout with different text? @@ -234,4 +243,19 @@ public class DecryptTextFragment extends DecryptFragment { getActivity().startService(intent); } + @Override + protected void onVerifyLoaded(boolean verified) { + + mShowMenuOptions = verified; + getActivity().supportInvalidateOptionsMenu(); + + if (verified) { + mInvalidLayout.setVisibility(View.GONE); + mValidLayout.setVisibility(View.VISIBLE); + } else { + mInvalidLayout.setVisibility(View.VISIBLE); + mValidLayout.setVisibility(View.GONE); + } + + } } -- cgit v1.2.3 From 9c174ff71dd5cf0bfdb1ae22d41abeda25eef913 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 24 Apr 2015 19:16:15 +0200 Subject: fix encrypt activity layout --- .../keychain/ui/widget/EncryptKeyCompletionView.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java index b2dfb2493..3d2e8b9df 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java @@ -108,9 +108,8 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView protected void onAttachedToWindow() { super.onAttachedToWindow(); - mLoaderManager = ((FragmentActivity) getContext()).getSupportLoaderManager(); - if (getContext() instanceof FragmentActivity) { + mLoaderManager = ((FragmentActivity) getContext()).getSupportLoaderManager(); mLoaderManager.initLoader(hashCode(), null, this); } else { Log.e(Constants.TAG, "EncryptKeyCompletionView must be attached to a FragmentActivity, this is " + getContext().getClass()); -- cgit v1.2.3 From 28da73120145078eae9a62cb98fc9d704d5ef7a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sat, 25 Apr 2015 14:15:38 +0200 Subject: Fix empty screen when clicking on encrypt in details view --- .../org/sufficientlysecure/keychain/ui/EncryptTextActivity.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java index 03ab48e23..dd6dd6594 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java @@ -98,23 +98,20 @@ public class EncryptTextActivity extends BaseActivity implements // handle like normal text encryption, override action and extras to later // executeServiceMethod ACTION_ENCRYPT_TEXT in main actions extras.putString(EXTRA_TEXT, sharedText); - action = ACTION_ENCRYPT_TEXT; } } } String textData = extras.getString(EXTRA_TEXT); - if (ACTION_ENCRYPT_TEXT.equals(action) && textData == null) { - Log.e(Constants.TAG, "Include the extra 'text' in your Intent!"); - return; + if (textData == null) { + textData = ""; } // preselect keys given by intent long mSigningKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID); long[] mEncryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS); - if (savedInstanceState == null) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); -- cgit v1.2.3 From 64ca19464b50cf14193e25724eaadd229b1a86ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sat, 25 Apr 2015 14:37:55 +0200 Subject: Set different titles when changing in nav drawer --- .../org/sufficientlysecure/keychain/ui/MainActivity.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java index fa4f07d88..151382da3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -49,7 +49,8 @@ public class MainActivity extends ActionBarActivity implements FabContainer { private KeyListFragment mKeyListFragment ; private AppsListFragment mAppsListFragment; private EncryptDecryptOverviewFragment mEncryptDecryptOverviewFragment; - private Fragment lastUsedFragment; + private Fragment mLastUsedFragment; + private Toolbar mToolbar; @Override public void onCreate(Bundle savedInstanceState) { @@ -63,14 +64,14 @@ public class MainActivity extends ActionBarActivity implements FabContainer { transaction.replace(R.id.main_fragment_container, mainFragment); transaction.commit(); - final Toolbar toolbar = (Toolbar) findViewById(R.id.activity_main_toolbar); - toolbar.setTitle(R.string.app_name); - setSupportActionBar(toolbar); + mToolbar = (Toolbar) findViewById(R.id.activity_main_toolbar); + mToolbar.setTitle(R.string.app_name); + setSupportActionBar(mToolbar); result = new Drawer() .withActivity(this) .withHeader(R.layout.main_drawer_header) - .withToolbar(toolbar) + .withToolbar(mToolbar) .addDrawerItems( new PrimaryDrawerItem().withName(R.string.nav_keys).withIcon(CommunityMaterial.Icon.cmd_key).withIdentifier(1).withCheckable(false), new PrimaryDrawerItem().withName(R.string.nav_encrypt_decrypt).withIcon(FontAwesome.Icon.faw_lock).withIdentifier(2).withCheckable(false), @@ -144,7 +145,7 @@ public class MainActivity extends ActionBarActivity implements FabContainer { } private void setFragment(Fragment fragment, boolean addToBackStack) { - this.lastUsedFragment = fragment; + this.mLastUsedFragment = fragment; FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.replace(R.id.main_fragment_container, fragment); if (addToBackStack) { @@ -154,6 +155,7 @@ public class MainActivity extends ActionBarActivity implements FabContainer { } private boolean onKeysSelected() { + mToolbar.setTitle(R.string.app_name); clearFragments(); if (mKeyListFragment == null) { @@ -165,6 +167,7 @@ public class MainActivity extends ActionBarActivity implements FabContainer { } private boolean onEnDecryptSelected() { + mToolbar.setTitle(R.string.nav_encrypt_decrypt); clearFragments(); if (mEncryptDecryptOverviewFragment == null) { mEncryptDecryptOverviewFragment = new EncryptDecryptOverviewFragment(); @@ -175,6 +178,7 @@ public class MainActivity extends ActionBarActivity implements FabContainer { } private boolean onAppsSelected() { + mToolbar.setTitle(R.string.nav_apps); clearFragments(); if (mAppsListFragment == null) { mAppsListFragment = new AppsListFragment(); -- cgit v1.2.3 From f1f0126048a66b3e649f35f2e2768bd43fe1fad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sat, 25 Apr 2015 23:40:26 +0200 Subject: Switch to new support lib version 22.1 --- .../keychain/ui/MainActivity.java | 4 +- .../keychain/ui/adapter/PagerTabStripAdapter.java | 4 +- .../keychain/ui/adapter/TabsAdapter.java | 4 +- .../keychain/ui/base/BaseActivity.java | 4 +- .../keychain/ui/widget/EmailEditText.java | 12 +- .../keychain/ui/widget/KeySpinner.java | 6 +- .../keychain/ui/widget/NameEditText.java | 12 +- .../keychain/ui/widget/PassphraseEditText.java | 6 +- .../ui/widget/PasswordStrengthBarView.java | 114 +++++++ .../keychain/ui/widget/PasswordStrengthView.java | 361 ++++++++++++++++++++ .../PasswordStrengthBarView.java | 117 ------- .../PasswordStrengthView.java | 362 --------------------- 12 files changed, 492 insertions(+), 514 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PasswordStrengthBarView.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PasswordStrengthView.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/passwordstrengthindicator/PasswordStrengthBarView.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/passwordstrengthindicator/PasswordStrengthView.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java index 151382da3..05cf64092 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -24,7 +24,7 @@ import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.View; import android.widget.AdapterView; @@ -42,7 +42,7 @@ import org.sufficientlysecure.keychain.remote.ui.AppsListFragment; import org.sufficientlysecure.keychain.util.FabContainer; import org.sufficientlysecure.keychain.util.Preferences; -public class MainActivity extends ActionBarActivity implements FabContainer { +public class MainActivity extends AppCompatActivity implements FabContainer { public Drawer.Result result; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java index 963e77fe9..7b911da96 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java @@ -21,7 +21,7 @@ import android.app.Activity; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentPagerAdapter; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import java.util.ArrayList; @@ -41,7 +41,7 @@ public class PagerTabStripAdapter extends FragmentPagerAdapter { } } - public PagerTabStripAdapter(ActionBarActivity activity) { + public PagerTabStripAdapter(AppCompatActivity activity) { super(activity.getSupportFragmentManager()); mActivity = activity; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java index 44afed351..a1f00599c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java @@ -24,7 +24,7 @@ import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.app.FragmentTransaction; import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBar; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import java.util.ArrayList; @@ -45,7 +45,7 @@ public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar. } } - public TabsAdapter(ActionBarActivity activity, ViewPager pager) { + public TabsAdapter(AppCompatActivity activity, ViewPager pager) { super(activity.getSupportFragmentManager()); mContext = activity; mActionBar = activity.getSupportActionBar(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java index 07d2ef8c0..0e752881f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java @@ -20,7 +20,7 @@ package org.sufficientlysecure.keychain.ui.base; import android.app.Activity; import android.os.Bundle; import android.support.v7.app.ActionBar; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.Gravity; import android.view.LayoutInflater; @@ -33,7 +33,7 @@ import org.sufficientlysecure.keychain.R; /** * Setups Toolbar */ -public abstract class BaseActivity extends ActionBarActivity { +public abstract class BaseActivity extends AppCompatActivity { protected Toolbar mToolbar; protected View mStatusBar; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java index e21c5d510..e55f6b1ad 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java @@ -17,9 +17,8 @@ package org.sufficientlysecure.keychain.ui.widget; -import android.annotation.TargetApi; import android.content.Context; -import android.os.Build; +import android.support.v7.widget.AppCompatAutoCompleteTextView; import android.text.Editable; import android.text.InputType; import android.text.TextWatcher; @@ -27,14 +26,13 @@ import android.util.AttributeSet; import android.util.Patterns; import android.view.inputmethod.EditorInfo; import android.widget.ArrayAdapter; -import android.widget.AutoCompleteTextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.util.ContactHelper; import java.util.regex.Matcher; -public class EmailEditText extends AutoCompleteTextView { +public class EmailEditText extends AppCompatAutoCompleteTextView { public EmailEditText(Context context) { super(context); @@ -51,12 +49,6 @@ public class EmailEditText extends AutoCompleteTextView { init(); } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public EmailEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - init(); - } - private void init() { setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); reenableKeyboardSuggestions(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java index 2c75c3a7d..fc5ecd76a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java @@ -24,7 +24,7 @@ import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.support.v4.widget.CursorAdapter; -import android.support.v7.internal.widget.TintSpinner; +import android.support.v7.widget.AppCompatSpinner; import android.text.format.DateFormat; import android.util.AttributeSet; import android.view.View; @@ -46,10 +46,10 @@ import java.util.Date; import java.util.TimeZone; /** - * Use TintSpinner from AppCompat lib instead of Spinner. Fixes white dropdown icon. + * Use AppCompatSpinner from AppCompat lib instead of Spinner. Fixes white dropdown icon. * Related: http://stackoverflow.com/a/27713090 */ -public abstract class KeySpinner extends TintSpinner implements LoaderManager.LoaderCallbacks { +public abstract class KeySpinner extends AppCompatSpinner implements LoaderManager.LoaderCallbacks { public interface OnKeyChangedListener { public void onKeyChanged(long masterKeyId); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NameEditText.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NameEditText.java index 153bf2ff2..1a034537c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NameEditText.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NameEditText.java @@ -17,17 +17,15 @@ package org.sufficientlysecure.keychain.ui.widget; -import android.annotation.TargetApi; import android.content.Context; -import android.os.Build; +import android.support.v7.widget.AppCompatAutoCompleteTextView; import android.util.AttributeSet; import android.view.inputmethod.EditorInfo; import android.widget.ArrayAdapter; -import android.widget.AutoCompleteTextView; import org.sufficientlysecure.keychain.util.ContactHelper; -public class NameEditText extends AutoCompleteTextView { +public class NameEditText extends AppCompatAutoCompleteTextView { public NameEditText(Context context) { super(context); init(); @@ -43,12 +41,6 @@ public class NameEditText extends AutoCompleteTextView { init(); } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public NameEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - init(); - } - private void init() { reenableKeyboardSuggestions(); initAdapter(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PassphraseEditText.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PassphraseEditText.java index 377f701d1..9364c5ee9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PassphraseEditText.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PassphraseEditText.java @@ -19,15 +19,13 @@ package org.sufficientlysecure.keychain.ui.widget; import android.content.Context; import android.graphics.Canvas; +import android.support.v7.widget.AppCompatEditText; import android.text.Editable; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.TypedValue; -import android.widget.EditText; -import org.sufficientlysecure.keychain.ui.widget.passwordstrengthindicator.PasswordStrengthBarView; - -public class PassphraseEditText extends EditText { +public class PassphraseEditText extends AppCompatEditText { PasswordStrengthBarView mPasswordStrengthBarView; int mPasswordBarWidth; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PasswordStrengthBarView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PasswordStrengthBarView.java new file mode 100644 index 000000000..e5886345f --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PasswordStrengthBarView.java @@ -0,0 +1,114 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 Matt Allen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.sufficientlysecure.keychain.ui.widget; + +import android.content.Context; +import android.graphics.Canvas; +import android.util.AttributeSet; + +/** + * Created by matt on 04/07/2014. + * https://github.com/matt-allen/android-password-strength-indicator + */ +public class PasswordStrengthBarView extends PasswordStrengthView { + + public PasswordStrengthBarView(Context context, AttributeSet attrs) { + super(context, attrs); + mMinHeight = 80; + mMinWidth = 300; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + generateIndicatorColor(); + // Default to full width + int indWidth = mIndicatorWidth; + // If score, leave it as full - can cause it to become + // less than full width in this calculation + if (mCurrentScore < 20) indWidth = (mIndicatorWidth / 20) * mCurrentScore; + // Draw indicator + canvas.drawRect( + getPaddingLeft(), + getPaddingTop(), + indWidth, + mIndicatorHeight, + mIndicatorPaint + ); + // Draw guides if true + if (mShowGuides) { + // TODO: Try and do this with a loop, for efficiency + // Draw bottom guide border + float positionY = getHeight() - getPaddingBottom() - getPaddingTop(); + float notchHeight = (float) (positionY * 0.8); + canvas.drawLine( + getPaddingLeft(), + positionY, + getWidth() - getPaddingRight(), + positionY, + mGuidePaint); + // Show left-most notch + canvas.drawLine( + getPaddingLeft(), + positionY, + getPaddingLeft(), + notchHeight, + mGuidePaint + ); + // Show middle-left notch + canvas.drawLine( + (float) (mIndicatorWidth * 0.25) + getPaddingLeft(), + positionY, + (float) (mIndicatorWidth * 0.25) + getPaddingLeft(), + notchHeight, + mGuidePaint + ); + // Show the middle notch + canvas.drawLine( + (float) (mIndicatorWidth * 0.5) + getPaddingLeft(), + positionY, + (float) (mIndicatorWidth * 0.5) + getPaddingLeft(), + notchHeight, + mGuidePaint + ); + // Show the middle-right notch + canvas.drawLine( + (float) (mIndicatorWidth * 0.75) + getPaddingLeft(), + positionY, + (float) (mIndicatorWidth * 0.75) + getPaddingLeft(), + notchHeight, + mGuidePaint + ); + // Show the right-most notch + canvas.drawLine( + mIndicatorWidth + getPaddingLeft(), + positionY, + mIndicatorWidth + getPaddingLeft(), + notchHeight, + mGuidePaint + ); + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PasswordStrengthView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PasswordStrengthView.java new file mode 100644 index 000000000..1487c3053 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PasswordStrengthView.java @@ -0,0 +1,361 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 Matt Allen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.sufficientlysecure.keychain.ui.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.view.View; + +import org.sufficientlysecure.keychain.R; + +/** + * Created by Matt Allen + * 01/07/14 + * http://www.mattallensoftware.co.uk + * mattallen092@gmail.com + *

+ * https://github.com/matt-allen/android-password-strength-indicator + *

+ *

+ * This View is designed to indicate how secure a user-entered password is in a visual way to + * relay to the user if they need to make it stronger. The strength of the password can be set + * at creation (or after) which will decide whether their password is strong enough. + *

+ *

+ *

+ * The password strength is decided by an index of 20. The minimum score needed to pass is 10 + * which means the String has met the conditions imposed by the strength test, but can be improved. + * If the password scores 10-19 it is considered weak, and only if it scores 20 will it be + * considered strong. + *

+ */ +public class PasswordStrengthView extends View { + + protected int mMinWidth; + protected int mMinHeight; + + protected Paint mIndicatorPaint; + protected Paint mGuidePaint; + + protected int mIndicatorHeight; + protected int mIndicatorWidth; + protected int mCurrentScore; + + protected int mColorFail; + protected int mColorWeak; + protected int mColorStrong; + + protected boolean mShowGuides = true; + + /** + * Used to define that the indicator should only be looking + * for a weak password. The bare minimum is used here to let + * the user continue. + */ + public static final int STRENGTH_WEAK = 0; + + /** + * A fairly strict rule for generating a password. It encourages a password that is + * less easy to crack. + */ + public static final int STRENGTH_MEDIUM = 1; + + /** + * A strong algorithm that encourages very strong passwords that should be fairly long, with + * non-alphanumeric, numbers, and upper case. + */ + public static final int STRENGTH_STRONG = 2; + + private int mStrengthRequirement = -1; + protected String mPassword; + + public PasswordStrengthView(Context context, AttributeSet attrs) { + super(context, attrs); + + int COLOR_FAIL = context.getResources().getColor(R.color.android_red_light); + int COLOR_WEAK = context.getResources().getColor(R.color.android_orange_light); + int COLOR_STRONG = context.getResources().getColor(R.color.android_green_light); + + TypedArray style = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.PasswordStrengthView, + 0, 0); + + try { + mStrengthRequirement = style.getInteger(R.styleable.PasswordStrengthView_strength, + STRENGTH_MEDIUM); + mShowGuides = style.getBoolean(R.styleable.PasswordStrengthView_showGuides, true); + mColorFail = style.getColor(R.styleable.PasswordStrengthView_color_fail, COLOR_FAIL); + mColorWeak = style.getColor(R.styleable.PasswordStrengthView_color_weak, COLOR_WEAK); + mColorStrong = style.getColor(R.styleable.PasswordStrengthView_color_strong, + COLOR_STRONG); + } catch (Exception e) { + e.printStackTrace(); + } + // Create and style the paint used for drawing the guide on the indicator + mGuidePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mGuidePaint.setStyle(Paint.Style.FILL_AND_STROKE); + mGuidePaint.setColor(Color.BLACK); + // Create and style paint for indicator + mIndicatorPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mIndicatorPaint.setStyle(Paint.Style.FILL); + } + + /** + * This view can determine if the password entered by the user is acceptable for + * use by your use case. This is based on the strength requirement you have set. + * + * @return True if requirement has been met + */ + public boolean isStrengthRequirementMet() { + return (mCurrentScore >= 10); + } + + /** + * Change the strength requirement of the password entered by the user. This will also + * re-check the password already entered against these new requirements. + * + * @param requiredStrength Use the public constants of this class to set + */ + public void setStrengthRequirement(int requiredStrength) { + if (requiredStrength >= 0 && requiredStrength <= 2) { + mStrengthRequirement = requiredStrength; + if (mPassword != null && mPassword.length() > 0) { + generatePasswordScore(); + // Update view with new score + invalidate(); + requestLayout(); + } + } else { + throw new IndexOutOfBoundsException("Input out of expected range"); + } + } + + /** + * Update the password string to check strength of + * + * @param passwordString String representation of user-input + */ + public void setPassword(String passwordString) { + if (passwordString != null && passwordString.length() > 0) { + mPassword = passwordString; + generatePasswordScore(); + } else { + mPassword = ""; + mCurrentScore = 0; + } + + // Update view with new score + invalidate(); + requestLayout(); + } + + /** + * Private convenience method for adding to the password score + * + * @param score Amount to be added to current score + */ + protected void addToPasswordScore(int score) { + int newScore = mCurrentScore + score; + + // Limit max score + if (newScore > 20) { + mCurrentScore = 20; + } else { + mCurrentScore = newScore; + } + } + + /** + * Call this to determine the current strength requirement set on the algorithm + * + * @return Int representation of the current strength set for the indicator + */ + public int getStrengthRequirement() { + return mStrengthRequirement; + } + + /** + * Generate a score based on the password. The password will already need to be stored + * as a class member before running this. + */ + protected void generatePasswordScore() { + mCurrentScore = 0; + int upperCase = getUppercaseCount(mPassword); + int nonAlpha = getNonAlphanumericCount(mPassword); + int numbers = getNumberCount(mPassword); + switch (mStrengthRequirement) { + case STRENGTH_WEAK: + addToPasswordScore(mPassword.length() * 2); + addToPasswordScore(upperCase * 2); + addToPasswordScore(nonAlpha * 2); + addToPasswordScore(numbers * 2); + break; + + case STRENGTH_MEDIUM: + addToPasswordScore(mPassword.length()); + addToPasswordScore(upperCase); + addToPasswordScore(nonAlpha * 2); + addToPasswordScore(numbers); + break; + + case STRENGTH_STRONG: + addToPasswordScore(mPassword.length() / 2); + // Cut the score in half to make this a very high requirement + addToPasswordScore(upperCase); + addToPasswordScore(nonAlpha); + addToPasswordScore(numbers); + break; + } + } + + @Override + protected void onSizeChanged(int w, int h, int oldW, int oldH) { + super.onSizeChanged(w, h, oldW, oldH); + int paddingX = getPaddingLeft(); + int paddingY = getPaddingTop(); + mIndicatorHeight = h - paddingY; + mIndicatorWidth = w - paddingX; + } + + /** + * The standard parts of the onMeasure needed to create the password strength + * indicator. Subclasses should call super.onMeasure, but also need to set + * the minimum height and width in the constructor. + * + * @param widthMeasureSpec The measurement given by the system + * @param heightMeasureSpec The measurement given by the system + */ + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + // Set minimum space for the view to do it's thing + int minW = getPaddingLeft() + getPaddingRight() + mMinWidth; + int w = resolveSizeAndState(minW, widthMeasureSpec, 1); + // And give it enough height so it's visible + int minH = mMinHeight + getPaddingBottom() + getPaddingTop(); + int h = resolveSizeAndState(minH, heightMeasureSpec, 0); + // Feed these back into UIKit + setMeasuredDimension(w, h); + } + + /** + * Set the colour of the indicator {@code Paint} to one that is appropriate + * for the strength of the password. + */ + protected void generateIndicatorColor() { + int color = mColorFail; + if (mCurrentScore >= 18) { + color = mColorStrong; + } else if (mCurrentScore >= 10) { + color = mColorWeak; + } + mIndicatorPaint.setColor(color); + } + + /** + * Quick method to determine how many of the characters in a given string are upper case + * + * @param stringToCheck The string to examine + * @return Number of upper case characters + */ + protected int getUppercaseCount(String stringToCheck) { + int score = 0; + int loops = stringToCheck.length() - 1; + for (int i = 0; i <= loops; i++) { + if (Character.isUpperCase(stringToCheck.charAt(i))) { + score++; + } + } + return score; + } + + /** + * A convenience method to determine how many characters in the given String aren't + * letters or numbers. + * + * @param stringToCheck + * @return Number of characters that aren't numbers or letters + */ + protected int getNonAlphanumericCount(String stringToCheck) { + int score = 0; + int loops = stringToCheck.length() - 1; + for (int i = 0; i <= loops; i++) { + if (!Character.isLetter(stringToCheck.charAt(i)) && + !Character.isDigit(stringToCheck.charAt(i))) { + score++; + } + } + return score; + } + + /** + * A convenience method for returning the count of numbers in a given String. + * + * @param stringToCheck + * @return The numbers of digits in the String + */ + protected int getNumberCount(String stringToCheck) { + int score = 0; + int loops = stringToCheck.length() - 1; + for (int i = 0; i <= loops; i++) { + if (Character.isDigit(stringToCheck.charAt(i))) { + score++; + } + } + return score; + } + + /** + * Set the guides to show on the view.
+ * On the line style, the guides will show underneath
+ * On the rounded style, the guides will be shown on the outer edges.
+ * The view will be redrawn after the method is called. + * + * @param showGuides True if you want the guides to be shown + */ + public void setShowGuides(boolean showGuides) { + mShowGuides = showGuides; + if (mPassword != null && mPassword.length() > 0) { + generatePasswordScore(); + } else { + mCurrentScore = 0; + } + + invalidate(); + requestLayout(); + } + + /** + * Determine whether the view is showing the guides for the password score + * + * @return True if the guides are being shown + */ + public boolean isShowingGuides() { + return mShowGuides; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/passwordstrengthindicator/PasswordStrengthBarView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/passwordstrengthindicator/PasswordStrengthBarView.java deleted file mode 100644 index 9e06c4cce..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/passwordstrengthindicator/PasswordStrengthBarView.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2014 Matt Allen - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.sufficientlysecure.keychain.ui.widget.passwordstrengthindicator; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; -import android.util.AttributeSet; - -/** - * Created by matt on 04/07/2014. - * https://github.com/matt-allen/android-password-strength-indicator - */ -public class PasswordStrengthBarView extends PasswordStrengthView { - - public PasswordStrengthBarView(Context context, AttributeSet attrs) { - super(context, attrs); - mMinHeight = 80; - mMinWidth = 300; - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - generateIndicatorColor(); - // Default to full width - int indWidth = mIndicatorWidth; - // If score, leave it as full - can cause it to become - // less than full width in this calculation - if (mCurrentScore < 20) indWidth = (mIndicatorWidth / 20) * mCurrentScore; - // Draw indicator - canvas.drawRect( - getPaddingLeft(), - getPaddingTop(), - indWidth, - mIndicatorHeight, - mIndicatorPaint - ); - // Draw guides if true - if (mShowGuides) { - // TODO: Try and do this with a loop, for efficiency - // Draw bottom guide border - float positionY = getHeight() - getPaddingBottom() - getPaddingTop(); - float notchHeight = (float) (positionY * 0.8); - canvas.drawLine( - getPaddingLeft(), - positionY, - getWidth() - getPaddingRight(), - positionY, - mGuidePaint); - // Show left-most notch - canvas.drawLine( - getPaddingLeft(), - positionY, - getPaddingLeft(), - notchHeight, - mGuidePaint - ); - // Show middle-left notch - canvas.drawLine( - (float) (mIndicatorWidth * 0.25) + getPaddingLeft(), - positionY, - (float) (mIndicatorWidth * 0.25) + getPaddingLeft(), - notchHeight, - mGuidePaint - ); - // Show the middle notch - canvas.drawLine( - (float) (mIndicatorWidth * 0.5) + getPaddingLeft(), - positionY, - (float) (mIndicatorWidth * 0.5) + getPaddingLeft(), - notchHeight, - mGuidePaint - ); - // Show the middle-right notch - canvas.drawLine( - (float) (mIndicatorWidth * 0.75) + getPaddingLeft(), - positionY, - (float) (mIndicatorWidth * 0.75) + getPaddingLeft(), - notchHeight, - mGuidePaint - ); - // Show the right-most notch - canvas.drawLine( - mIndicatorWidth + getPaddingLeft(), - positionY, - mIndicatorWidth + getPaddingLeft(), - notchHeight, - mGuidePaint - ); - } - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/passwordstrengthindicator/PasswordStrengthView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/passwordstrengthindicator/PasswordStrengthView.java deleted file mode 100644 index bc5018497..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/passwordstrengthindicator/PasswordStrengthView.java +++ /dev/null @@ -1,362 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2014 Matt Allen - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.sufficientlysecure.keychain.ui.widget.passwordstrengthindicator; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.Paint; -import android.util.AttributeSet; -import android.view.View; - -import org.sufficientlysecure.keychain.R; - -/** - * Created by Matt Allen - * 01/07/14 - * http://www.mattallensoftware.co.uk - * mattallen092@gmail.com - *

- * https://github.com/matt-allen/android-password-strength-indicator - *

- *

- * This View is designed to indicate how secure a user-entered password is in a visual way to - * relay to the user if they need to make it stronger. The strength of the password can be set - * at creation (or after) which will decide whether their password is strong enough. - *

- *

- *

- * The password strength is decided by an index of 20. The minimum score needed to pass is 10 - * which means the String has met the conditions imposed by the strength test, but can be improved. - * If the password scores 10-19 it is considered weak, and only if it scores 20 will it be - * considered strong. - *

- */ -public class PasswordStrengthView extends View { - - - protected int mMinWidth; - protected int mMinHeight; - - protected Paint mIndicatorPaint; - protected Paint mGuidePaint; - - protected int mIndicatorHeight; - protected int mIndicatorWidth; - protected int mCurrentScore; - - protected int mColorFail; - protected int mColorWeak; - protected int mColorStrong; - - protected boolean mShowGuides = true; - - /** - * Used to define that the indicator should only be looking - * for a weak password. The bare minimum is used here to let - * the user continue. - */ - public static final int STRENGTH_WEAK = 0; - - /** - * A fairly strict rule for generating a password. It encourages a password that is - * less easy to crack. - */ - public static final int STRENGTH_MEDIUM = 1; - - /** - * A strong algorithm that encourages very strong passwords that should be fairly long, with - * non-alphanumeric, numbers, and upper case. - */ - public static final int STRENGTH_STRONG = 2; - - private int mStrengthRequirement = -1; - protected String mPassword; - - public PasswordStrengthView(Context context, AttributeSet attrs) { - super(context, attrs); - - int COLOR_FAIL = context.getResources().getColor(R.color.android_red_light); - int COLOR_WEAK = context.getResources().getColor(R.color.android_orange_light); - int COLOR_STRONG = context.getResources().getColor(R.color.android_green_light); - - TypedArray style = context.getTheme().obtainStyledAttributes( - attrs, - R.styleable.PasswordStrengthView, - 0, 0); - - try { - mStrengthRequirement = style.getInteger(R.styleable.PasswordStrengthView_strength, - STRENGTH_MEDIUM); - mShowGuides = style.getBoolean(R.styleable.PasswordStrengthView_showGuides, true); - mColorFail = style.getColor(R.styleable.PasswordStrengthView_color_fail, COLOR_FAIL); - mColorWeak = style.getColor(R.styleable.PasswordStrengthView_color_weak, COLOR_WEAK); - mColorStrong = style.getColor(R.styleable.PasswordStrengthView_color_strong, - COLOR_STRONG); - } catch (Exception e) { - e.printStackTrace(); - } - // Create and style the paint used for drawing the guide on the indicator - mGuidePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mGuidePaint.setStyle(Paint.Style.FILL_AND_STROKE); - mGuidePaint.setColor(Color.BLACK); - // Create and style paint for indicator - mIndicatorPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mIndicatorPaint.setStyle(Paint.Style.FILL); - } - - /** - * This view can determine if the password entered by the user is acceptable for - * use by your use case. This is based on the strength requirement you have set. - * - * @return True if requirement has been met - */ - public boolean isStrengthRequirementMet() { - return (mCurrentScore >= 10); - } - - /** - * Change the strength requirement of the password entered by the user. This will also - * re-check the password already entered against these new requirements. - * - * @param requiredStrength Use the public constants of this class to set - */ - public void setStrengthRequirement(int requiredStrength) { - if (requiredStrength >= 0 && requiredStrength <= 2) { - mStrengthRequirement = requiredStrength; - if (mPassword != null && mPassword.length() > 0) { - generatePasswordScore(); - // Update view with new score - invalidate(); - requestLayout(); - } - } else { - throw new IndexOutOfBoundsException("Input out of expected range"); - } - } - - /** - * Update the password string to check strength of - * - * @param passwordString String representation of user-input - */ - public void setPassword(String passwordString) { - if (passwordString != null && passwordString.length() > 0) { - mPassword = passwordString; - generatePasswordScore(); - } else { - mPassword = ""; - mCurrentScore = 0; - } - - // Update view with new score - invalidate(); - requestLayout(); - } - - /** - * Private convenience method for adding to the password score - * - * @param score Amount to be added to current score - */ - protected void addToPasswordScore(int score) { - int newScore = mCurrentScore + score; - - // Limit max score - if (newScore > 20) { - mCurrentScore = 20; - } else { - mCurrentScore = newScore; - } - } - - /** - * Call this to determine the current strength requirement set on the algorithm - * - * @return Int representation of the current strength set for the indicator - */ - public int getStrengthRequirement() { - return mStrengthRequirement; - } - - /** - * Generate a score based on the password. The password will already need to be stored - * as a class member before running this. - */ - protected void generatePasswordScore() { - mCurrentScore = 0; - int upperCase = getUppercaseCount(mPassword); - int nonAlpha = getNonAlphanumericCount(mPassword); - int numbers = getNumberCount(mPassword); - switch (mStrengthRequirement) { - case STRENGTH_WEAK: - addToPasswordScore(mPassword.length() * 2); - addToPasswordScore(upperCase * 2); - addToPasswordScore(nonAlpha * 2); - addToPasswordScore(numbers * 2); - break; - - case STRENGTH_MEDIUM: - addToPasswordScore(mPassword.length()); - addToPasswordScore(upperCase); - addToPasswordScore(nonAlpha * 2); - addToPasswordScore(numbers); - break; - - case STRENGTH_STRONG: - addToPasswordScore(mPassword.length() / 2); - // Cut the score in half to make this a very high requirement - addToPasswordScore(upperCase); - addToPasswordScore(nonAlpha); - addToPasswordScore(numbers); - break; - } - } - - @Override - protected void onSizeChanged(int w, int h, int oldW, int oldH) { - super.onSizeChanged(w, h, oldW, oldH); - int paddingX = getPaddingLeft(); - int paddingY = getPaddingTop(); - mIndicatorHeight = h - paddingY; - mIndicatorWidth = w - paddingX; - } - - /** - * The standard parts of the onMeasure needed to create the password strength - * indicator. Subclasses should call super.onMeasure, but also need to set - * the minimum height and width in the constructor. - * - * @param widthMeasureSpec The measurement given by the system - * @param heightMeasureSpec The measurement given by the system - */ - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - // Set minimum space for the view to do it's thing - int minW = getPaddingLeft() + getPaddingRight() + mMinWidth; - int w = resolveSizeAndState(minW, widthMeasureSpec, 1); - // And give it enough height so it's visible - int minH = mMinHeight + getPaddingBottom() + getPaddingTop(); - int h = resolveSizeAndState(minH, heightMeasureSpec, 0); - // Feed these back into UIKit - setMeasuredDimension(w, h); - } - - /** - * Set the colour of the indicator {@code Paint} to one that is appropriate - * for the strength of the password. - */ - protected void generateIndicatorColor() { - int color = mColorFail; - if (mCurrentScore >= 18) { - color = mColorStrong; - } else if (mCurrentScore >= 10) { - color = mColorWeak; - } - mIndicatorPaint.setColor(color); - } - - /** - * Quick method to determine how many of the characters in a given string are upper case - * - * @param stringToCheck The string to examine - * @return Number of upper case characters - */ - protected int getUppercaseCount(String stringToCheck) { - int score = 0; - int loops = stringToCheck.length() - 1; - for (int i = 0; i <= loops; i++) { - if (Character.isUpperCase(stringToCheck.charAt(i))) { - score++; - } - } - return score; - } - - /** - * A convenience method to determine how many characters in the given String aren't - * letters or numbers. - * - * @param stringToCheck - * @return Number of characters that aren't numbers or letters - */ - protected int getNonAlphanumericCount(String stringToCheck) { - int score = 0; - int loops = stringToCheck.length() - 1; - for (int i = 0; i <= loops; i++) { - if (!Character.isLetter(stringToCheck.charAt(i)) && - !Character.isDigit(stringToCheck.charAt(i))) { - score++; - } - } - return score; - } - - /** - * A convenience method for returning the count of numbers in a given String. - * - * @param stringToCheck - * @return The numbers of digits in the String - */ - protected int getNumberCount(String stringToCheck) { - int score = 0; - int loops = stringToCheck.length() - 1; - for (int i = 0; i <= loops; i++) { - if (Character.isDigit(stringToCheck.charAt(i))) { - score++; - } - } - return score; - } - - /** - * Set the guides to show on the view.
- * On the line style, the guides will show underneath
- * On the rounded style, the guides will be shown on the outer edges.
- * The view will be redrawn after the method is called. - * - * @param showGuides True if you want the guides to be shown - */ - public void setShowGuides(boolean showGuides) { - mShowGuides = showGuides; - if (mPassword != null && mPassword.length() > 0) { - generatePasswordScore(); - } else { - mCurrentScore = 0; - } - - invalidate(); - requestLayout(); - } - - /** - * Determine whether the view is showing the guides for the password score - * - * @return True if the guides are being shown - */ - public boolean isShowingGuides() { - return mShowGuides; - } -} -- cgit v1.2.3 From c442d3bd0d3bac96614d3d5c0e833a42cab4706e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sun, 26 Apr 2015 00:33:26 +0200 Subject: Put decrypt result in toolbar, some ui fixes for decrypt --- .../keychain/ui/DecryptFragment.java | 19 ++++++++++--------- .../keychain/ui/DecryptTextFragment.java | 6 +++--- 2 files changed, 13 insertions(+), 12 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java index d641f02f9..651b56ab0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java @@ -79,17 +79,18 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - mResultLayout = (LinearLayout) view.findViewById(R.id.result_main_layout); + // NOTE: These views are inside the activity! + mResultLayout = (LinearLayout) getActivity().findViewById(R.id.result_main_layout); mResultLayout.setVisibility(View.GONE); - mEncryptionIcon = (ImageView) view.findViewById(R.id.result_encryption_icon); - mEncryptionText = (TextView) view.findViewById(R.id.result_encryption_text); - mSignatureIcon = (ImageView) view.findViewById(R.id.result_signature_icon); - mSignatureText = (TextView) view.findViewById(R.id.result_signature_text); - mSignatureLayout = view.findViewById(R.id.result_signature_layout); - mSignatureName = (TextView) view.findViewById(R.id.result_signature_name); - mSignatureEmail = (TextView) view.findViewById(R.id.result_signature_email); - mSignatureAction = (TextView) view.findViewById(R.id.result_signature_action); + mEncryptionIcon = (ImageView) getActivity().findViewById(R.id.result_encryption_icon); + mEncryptionText = (TextView) getActivity().findViewById(R.id.result_encryption_text); + mSignatureIcon = (ImageView) getActivity().findViewById(R.id.result_signature_icon); + mSignatureText = (TextView) getActivity().findViewById(R.id.result_signature_text); + mSignatureLayout = getActivity().findViewById(R.id.result_signature_layout); + mSignatureName = (TextView) getActivity().findViewById(R.id.result_signature_name); + mSignatureEmail = (TextView) getActivity().findViewById(R.id.result_signature_email); + mSignatureAction = (TextView) getActivity().findViewById(R.id.result_signature_action); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java index 1b9ae917f..6f576a112 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java @@ -97,9 +97,9 @@ public class DecryptTextFragment extends DecryptFragment { /** * Create Intent Chooser but exclude decrypt activites */ - private Intent sendWithChooserExcludingEncrypt(String text) { + private Intent sendWithChooserExcludingDecrypt(String text) { Intent prototype = createSendIntent(text); - String title = getString(R.string.title_share_file); + String title = getString(R.string.title_share_message); // we don't want to decrypt the decrypted, no inception ;) String[] blacklist = new String[]{ @@ -147,7 +147,7 @@ public class DecryptTextFragment extends DecryptFragment { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.decrypt_share: { - startActivity(sendWithChooserExcludingEncrypt(mText.getText().toString())); + startActivity(sendWithChooserExcludingDecrypt(mText.getText().toString())); break; } case R.id.decrypt_copy: { -- cgit v1.2.3