diff options
| author | Vincent Breitmoser <valodim@mugenguild.com> | 2015-04-26 01:02:22 +0200 | 
|---|---|---|
| committer | Vincent Breitmoser <valodim@mugenguild.com> | 2015-04-26 01:02:22 +0200 | 
| commit | 1865852cccccf0b78ffe8bcdc0cfbca66ddebfdc (patch) | |
| tree | add2f0d85813f9ece1be3c926b4ae8f221d7cf47 /OpenKeychain/src/main/java | |
| parent | d6d678dae3ed5d794a9aa5e289197d264f6a7ff9 (diff) | |
| parent | c442d3bd0d3bac96614d3d5c0e833a42cab4706e (diff) | |
| download | open-keychain-1865852cccccf0b78ffe8bcdc0cfbca66ddebfdc.tar.gz open-keychain-1865852cccccf0b78ffe8bcdc0cfbca66ddebfdc.tar.bz2 open-keychain-1865852cccccf0b78ffe8bcdc0cfbca66ddebfdc.zip | |
Merge remote-tracking branch 'origin/development' into linked-identities
Conflicts:
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java
	OpenKeychain/src/main/res/menu/decrypt_menu.xml
Diffstat (limited to 'OpenKeychain/src/main/java')
27 files changed, 802 insertions, 183 deletions
| 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                      } 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/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: { 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/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(); 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;              }          } 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..bf7e41045 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.<Uri>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/ImportKeysProxyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java index b9f1bf870..29f2511a0 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,18 +81,9 @@ 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)) -                    .setResultDisplayDuration(0); -            integrator.setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); -            integrator.initiateScan(); -        } else if (ACTION_SCAN_WITH_RESULT.equals(action)) { -            returnResult = true; +        } 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)) @@ -103,7 +93,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"); @@ -147,69 +136,63 @@ 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 (returnResult) { -            Intent result = new Intent(); -            result.putExtra(EXTRA_FINGERPRINT, fingerprint); -            setResult(RESULT_OK, result); -            finish(); -        } else { -            importKeys(fingerprint);          }      }      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();          }      }      public void importKeys(byte[] keyringData) { -          ParcelableKeyRing keyEntry = new ParcelableKeyRing(keyringData);          ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();          selectedEntries.add(keyEntry);          startImportService(selectedEntries); -      }      public void importKeys(String fingerprint) { -          ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);          ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();          selectedEntries.add(keyEntry);          startImportService(selectedEntries); -      } -    private void startImportService (ArrayList<ParcelableKeyRing> keyRings) { +    private void startImportService(ArrayList<ParcelableKeyRing> keyRings) {          // Message is received after importing is done in KeychainIntentService          ServiceProgressHandler serviceHandler = new ServiceProgressHandler( @@ -282,7 +265,6 @@ public class ImportKeysProxyActivity extends FragmentActivity {          // start service with intent          startService(intent); -      }      /** 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 36ed99f69..edaea539b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -682,17 +682,16 @@ public class KeyListFragment extends LoaderFragment      }      private void showMultiExportDialog(long[] masterKeyIds) { -        mIdsForRepeatAskPassphrase = new ArrayList<Long>(); -        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; @@ -701,8 +700,8 @@ public class KeyListFragment extends LoaderFragment              return;          }          long[] idsForMultiExport = new long[mIdsForRepeatAskPassphrase.size()]; -        for(int i=0; i<mIdsForRepeatAskPassphrase.size(); ++i) { -            idsForMultiExport[i] = mIdsForRepeatAskPassphrase.get(i).longValue(); +        for (int i = 0; i < mIdsForRepeatAskPassphrase.size(); ++i) { +            idsForMultiExport[i] = mIdsForRepeatAskPassphrase.get(i);          }          mExportHelper.showExportKeysDialog(idsForMultiExport,                  Constants.Path.APP_DIR_FILE, @@ -711,7 +710,7 @@ public class KeyListFragment extends LoaderFragment      private void startPassphraseActivity() {          Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); -        long masterKeyId = mIdsForRepeatAskPassphrase.get(mIndex++).longValue(); +        long masterKeyId = mIdsForRepeatAskPassphrase.get(mIndex++);          intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, masterKeyId);          startActivityForResult(intent, REQUEST_REPEAT_PASSPHRASE);      } @@ -719,7 +718,7 @@ public class KeyListFragment extends LoaderFragment      @Override      public void onActivityResult(int requestCode, int resultCode, Intent data) {          if (requestCode == REQUEST_REPEAT_PASSPHRASE) { -            if(resultCode != Activity.RESULT_OK) { +            if (resultCode != Activity.RESULT_OK) {                  return;              }              if (mIndex < mIdsForRepeatAskPassphrase.size()) { @@ -727,8 +726,8 @@ public class KeyListFragment extends LoaderFragment                  return;              }              long[] idsForMultiExport = new long[mIdsForRepeatAskPassphrase.size()]; -            for(int i=0; i<mIdsForRepeatAskPassphrase.size(); ++i) { -                idsForMultiExport[i] = mIdsForRepeatAskPassphrase.get(i).longValue(); +            for (int i = 0; i < mIdsForRepeatAskPassphrase.size(); ++i) { +                idsForMultiExport[i] = mIdsForRepeatAskPassphrase.get(i);              }              mExportHelper.showExportKeysDialog(idsForMultiExport,                      Constants.Path.APP_DIR_FILE, 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..05cf64092 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 <dominik@dominikschuermann.de>   * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com> + * Copyright (C) 2015 Kai Jiang <jiangkai@gmail.com>   *   * This program is free software: you can redistribute it and/or modify   * it under the terms of the GNU General Public License as published by @@ -20,36 +21,98 @@ 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.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.widget.AdapterView; + +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.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 org.sufficientlysecure.keychain.util.Preferences; -import it.neokree.materialnavigationdrawer.MaterialNavigationDrawer; +public class MainActivity extends AppCompatActivity implements FabContainer { -public class MainActivity extends MaterialNavigationDrawer implements FabContainer { +    public Drawer.Result result; -    @Override -    public void init(Bundle savedInstanceState) { -        // don't open drawer on first run -        disableLearningPattern(); +    private KeyListFragment mKeyListFragment ; +    private AppsListFragment mAppsListFragment; +    private EncryptDecryptOverviewFragment mEncryptDecryptOverviewFragment; +    private Fragment mLastUsedFragment; +    private Toolbar mToolbar; -//        addMultiPaneSupport(); +    @Override +    public void onCreate(Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); +        setContentView(R.layout.main_activity); -        // set the header image -        // create and set the header -        setDrawerHeaderImage(R.drawable.drawer_header); +        //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(); -        // 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())); +        mToolbar = (Toolbar) findViewById(R.id.activity_main_toolbar); +        mToolbar.setTitle(R.string.app_name); +        setSupportActionBar(mToolbar); -        // 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(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), +                        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) +                ) +                .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: +                                    onKeysSelected(); +                                    break; +                                case 2: +                                    onEnDecryptSelected(); +                                    break; +                                case 3: +                                    onAppsSelected(); +                                    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); +                            } +                        } +                    } +                }) +                .withSelectedItem(-1) +                .withSavedInstance(savedInstanceState) +                .build();          // if this is the first time show first time activity          Preferences prefs = Preferences.getPreferences(this); @@ -69,9 +132,83 @@ public class MainActivity extends MaterialNavigationDrawer implements FabContain          }      } +    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.mLastUsedFragment = fragment; +        FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); +        ft.replace(R.id.main_fragment_container, fragment); +        if (addToBackStack) { +            ft.addToBackStack(null); +        } +        ft.commit(); +    } + +    private boolean onKeysSelected() { +        mToolbar.setTitle(R.string.app_name); +        clearFragments(); + +        if (mKeyListFragment == null) { +            mKeyListFragment = new KeyListFragment(); +        } + +        setFragment(mKeyListFragment, false); +        return true; +    } + +    private boolean onEnDecryptSelected() { +        mToolbar.setTitle(R.string.nav_encrypt_decrypt); +        clearFragments(); +        if (mEncryptDecryptOverviewFragment == null) { +            mEncryptDecryptOverviewFragment = new EncryptDecryptOverviewFragment(); +        } + +        setFragment(mEncryptDecryptOverviewFragment); +        return true; +    } + +    private boolean onAppsSelected() { +        mToolbar.setTitle(R.string.nav_apps); +        clearFragments(); +        if (mAppsListFragment == null) { +            mAppsListFragment = new AppsListFragment(); +        } + +        setFragment(mAppsListFragment); +        return true; +    } + +    @Override +    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(); +        Object fragment = getSupportFragmentManager() +                .findFragmentById(R.id.main_fragment_container);          if (fragment instanceof FabContainer) {              ((FabContainer) fragment).fabMoveUp(height);          } @@ -79,7 +216,8 @@ public class MainActivity extends MaterialNavigationDrawer implements FabContain      @Override      public void fabRestorePosition() { -        Object fragment = getCurrentSection().getTargetFragment(); +        Object fragment = getSupportFragmentManager() +                .findFragmentById(R.id.main_fragment_container);          if (fragment instanceof FabContainer) {              ((FabContainer) fragment).fabRestorePosition();          } 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/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/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/base/BaseNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java index 95c1690b1..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 @@ -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; @@ -197,10 +200,15 @@ 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!");          } +        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; +            }          }      } @@ -476,6 +507,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 +531,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!");      } 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 <dominik@dominikschuermann.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.ui.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<String, Void, FailureReason>() { +            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); +        } +    } +} 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 5a6c61f7d..8cf15a75a 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 @@ -25,7 +25,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; @@ -47,10 +47,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<Cursor> { +public abstract class KeySpinner extends AppCompatSpinner implements LoaderManager.LoaderCallbacks<Cursor> {      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/passwordstrengthindicator/PasswordStrengthBarView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PasswordStrengthBarView.java index 9e06c4cce..e5886345f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/passwordstrengthindicator/PasswordStrengthBarView.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PasswordStrengthBarView.java @@ -22,13 +22,10 @@   * SOFTWARE.   */ -package org.sufficientlysecure.keychain.ui.widget.passwordstrengthindicator; +package org.sufficientlysecure.keychain.ui.widget;  import android.content.Context;  import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode;  import android.util.AttributeSet;  /** 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/PasswordStrengthView.java index bc5018497..1487c3053 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/passwordstrengthindicator/PasswordStrengthView.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PasswordStrengthView.java @@ -22,7 +22,7 @@   * SOFTWARE.   */ -package org.sufficientlysecure.keychain.ui.widget.passwordstrengthindicator; +package org.sufficientlysecure.keychain.ui.widget;  import android.content.Context;  import android.content.res.TypedArray; @@ -56,7 +56,6 @@ import org.sufficientlysecure.keychain.R;   */  public class PasswordStrengthView extends View { -      protected int mMinWidth;      protected int mMinHeight; 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; | 
