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
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
private KeyListFragment mKeyListFragment ;
|
||||
private AppsListFragment mAppsListFragment;
|
||||
private EncryptDecryptOverviewFragment mEncryptDecryptOverviewFragment;
|
||||
private Fragment mLastUsedFragment;
|
||||
private Toolbar mToolbar;
|
||||
|
||||
@Override
|
||||
public void init(Bundle savedInstanceState) {
|
||||
// don't open drawer on first run
|
||||
disableLearningPattern();
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.main_activity);
|
||||
|
||||
// addMultiPaneSupport();
|
||||
//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();
|
||||
|
||||
// set the header image
|
||||
// create and set the header
|
||||
setDrawerHeaderImage(R.drawable.drawer_header);
|
||||
mToolbar = (Toolbar) findViewById(R.id.activity_main_toolbar);
|
||||
mToolbar.setTitle(R.string.app_name);
|
||||
setSupportActionBar(mToolbar);
|
||||
|
||||
// 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)));
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -82,12 +82,12 @@ public class ImportKeysListLoader
|
||||
|
||||
@Override
|
||||
protected void onStartLoading() {
|
||||
forceLoad();
|
||||
super.forceLoad();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStopLoading() {
|
||||
cancelLoad();
|
||||
super.cancelLoad();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user