Externalize QR Code scanning and import into its own activity for better code separation

This commit is contained in:
Dominik Schürmann 2014-10-23 17:13:49 +02:00
parent ecfa2288eb
commit e494beb58a
6 changed files with 251 additions and 121 deletions

View File

@ -108,7 +108,7 @@
android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_edit_key" /> android:label="@string/title_edit_key" />
<activity <activity
android:name=".ui.QrCodeActivity" android:name=".ui.QrCodeViewActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/share_qr_code_dialog_title" /> android:label="@string/share_qr_code_dialog_title" />
<activity <activity
@ -441,10 +441,10 @@
android:value=".ui.KeyListActivity" /> android:value=".ui.KeyListActivity" />
</activity> </activity>
<activity <activity
android:name=".ui.ImportKeysActivity" android:name=".ui.QrCodeScanActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_import_keys" android:label="@string/app_name"
android:launchMode="singleTop" android:theme="@android:style/Theme.NoDisplay"
android:windowSoftInputMode="stateHidden"> android:windowSoftInputMode="stateHidden">
<!-- VIEW with fingerprint scheme: <!-- VIEW with fingerprint scheme:
@ -462,6 +462,21 @@
<data android:scheme="OpenPGP4Fpr" /> <data android:scheme="OpenPGP4Fpr" />
<data android:scheme="OpenPGP4fpr" /> <data android:scheme="OpenPGP4fpr" />
</intent-filter> </intent-filter>
<!-- IMPORT_KEY without mimeType to allow import with extras Bundle -->
<intent-filter android:label="@string/intent_import_key">
<action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_QR_CODE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".ui.ImportKeysActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_import_keys"
android:launchMode="singleTop"
android:windowSoftInputMode="stateHidden">
<!-- VIEW with mimeType: Allows to import keys (attached to emails) from email apps --> <!-- VIEW with mimeType: Allows to import keys (attached to emails) from email apps -->
<intent-filter android:label="@string/intent_import_key"> <intent-filter android:label="@string/intent_import_key">
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
@ -625,7 +640,6 @@
<!-- IMPORT_KEY without mimeType to allow import with extras Bundle --> <!-- IMPORT_KEY without mimeType to allow import with extras Bundle -->
<intent-filter android:label="@string/intent_import_key"> <intent-filter android:label="@string/intent_import_key">
<action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY" /> <action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY" />
<action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_QR_CODE" />
<action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_KEYSERVER" /> <action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_KEYSERVER" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />

View File

@ -602,6 +602,8 @@ public abstract class OperationResult implements Parcelable {
MSG_ACC_SAVED (LogLevel.INFO, R.string.api_settings_save_msg), MSG_ACC_SAVED (LogLevel.INFO, R.string.api_settings_save_msg),
MSG_WRONG_QR_CODE (LogLevel.INFO, R.string.import_qr_code_wrong),
MSG_NO_VALID_ENC (LogLevel.ERROR, R.string.error_invalid_data), MSG_NO_VALID_ENC (LogLevel.ERROR, R.string.error_invalid_data),
// get key // get key

View File

@ -20,35 +20,26 @@ package org.sufficientlysecure.keychain.ui;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Message; import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.ExportHelper; import org.sufficientlysecure.keychain.util.ExportHelper;
import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Locale;
public class KeyListActivity extends DrawerActivity { public class KeyListActivity extends DrawerActivity {
@ -97,8 +88,9 @@ public class KeyListActivity extends DrawerActivity {
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.menu_key_list_add: case R.id.menu_key_list_add:
// scan using xzing's Barcode Scanner Intent scanQrCode = new Intent(this, QrCodeScanActivity.class);
new IntentIntegrator(this).initiateScan(); scanQrCode.setAction(QrCodeScanActivity.ACTION_SCAN_WITH_RESULT);
startActivityForResult(scanQrCode, 0);
return true; return true;
case R.id.menu_key_list_search_cloud: case R.id.menu_key_list_search_cloud:
@ -219,35 +211,6 @@ public class KeyListActivity extends DrawerActivity {
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == IntentIntegratorSupportV4.REQUEST_CODE) {
IntentResult scanResult = IntentIntegratorSupportV4.parseActivityResult(requestCode,
resultCode, data);
if (scanResult != null && scanResult.getFormatName() != null) {
String scannedContent = scanResult.getContents();
Log.d(Constants.TAG, "scannedContent: " + scannedContent);
// look if it's fingerprint only
if (scannedContent.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) {
String fingerprint =
Uri.parse(scanResult.getContents()).toString().split(":")[1].toLowerCase(Locale.ENGLISH);
importKeys(fingerprint);
return;
}
// is this a full key encoded as qr code?
if (scannedContent.startsWith("-----BEGIN PGP")) {
// TODO
// mImportActivity.loadCallback(new ImportKeysListFragment.BytesLoaderState(scannedContent.getBytes(), null));
return;
}
// fail...
Notify.showNotify(this, R.string.import_qr_code_wrong, Notify.Style.ERROR);
}
return;
}
// if a result has been returned, display a notify // if a result has been returned, display a notify
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
@ -257,76 +220,4 @@ public class KeyListActivity extends DrawerActivity {
} }
} }
public void importKeys(String fingerprint) {
// Message is received after importing is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
this,
getString(R.string.progress_importing),
ProgressDialog.STYLE_HORIZONTAL,
true) {
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
// get returned data bundle
Bundle returnData = message.getData();
if (returnData == null) {
return;
}
final ImportKeyResult result =
returnData.getParcelable(OperationResult.EXTRA_RESULT);
if (result == null) {
Log.e(Constants.TAG, "result == null");
return;
}
if ( ! result.success()) {
result.createNotify(KeyListActivity.this).show();
return;
}
Intent certifyIntent = new Intent(KeyListActivity.this, MultiCertifyKeyActivity.class);
certifyIntent.putExtra(MultiCertifyKeyActivity.EXTRA_RESULT, result);
certifyIntent.putExtra(MultiCertifyKeyActivity.EXTRA_KEY_IDS, result.getImportedMasterKeyIds());
startActivityForResult(certifyIntent, REQUEST_CODE_RESULT_TO_LIST);
}
}
};
// search config
Preferences prefs = Preferences.getPreferences(this);
Preferences.CloudSearchPrefs cloudPrefs = new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
// Send all information needed to service to query keys in other thread
Intent intent = new Intent(this, KeychainIntentService.class);
intent.setAction(KeychainIntentService.ACTION_DOWNLOAD_AND_IMPORT_KEYS);
// fill values for this action
Bundle data = new Bundle();
data.putString(KeychainIntentService.DOWNLOAD_KEY_SERVER, cloudPrefs.keyserver);
final ImportKeysListEntry keyEntry = new ImportKeysListEntry();
keyEntry.setFingerprintHex(fingerprint);
keyEntry.addOrigin(cloudPrefs.keyserver);
ArrayList<ImportKeysListEntry> selectedEntries = new ArrayList<ImportKeysListEntry>();
selectedEntries.add(keyEntry);
data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
// show progress dialog
saveHandler.showProgressDialog(this);
// start service with intent
startService(intent);
}
} }

View File

@ -0,0 +1,223 @@
/*
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui;
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.FragmentActivity;
import android.widget.Toast;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.api.OpenKeychainIntents;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.SingletonResult;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
import java.util.ArrayList;
import java.util.Locale;
/**
* Proxy activity (just a transparent content view) to scan QR Codes using the Barcode Scanner app
*/
public class QrCodeScanActivity extends FragmentActivity {
public static final String ACTION_QR_CODE_API = OpenKeychainIntents.IMPORT_KEY_FROM_QR_CODE;
public static final String ACTION_SCAN_WITH_RESULT = Constants.INTENT_PREFIX + "SCAN_QR_CODE_WITH_RESULT";
boolean returnResult;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// this activity itself has no content view (see manifest)
handleActions(getIntent());
}
protected void handleActions(Intent intent) {
String action = intent.getAction();
Uri dataUri = intent.getData();
String scheme = intent.getScheme();
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;
startCertify(dataUri);
} else if (ACTION_SCAN_WITH_RESULT.equals(action)) {
// scan using xzing's Barcode Scanner and return result parcel in OpenKeychain
returnResult = true;
new IntentIntegrator(this).initiateScan();
} else if (ACTION_QR_CODE_API.equals(action)) {
// scan using xzing's Barcode Scanner from outside OpenKeychain
returnResult = false;
new IntentIntegrator(this).initiateScan();
} else {
Log.e(Constants.TAG, "No valid scheme or action given!");
finish();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == IntentIntegratorSupportV4.REQUEST_CODE) {
IntentResult scanResult = IntentIntegratorSupportV4.parseActivityResult(requestCode,
resultCode, data);
if (scanResult != null && scanResult.getFormatName() != null) {
String scannedContent = scanResult.getContents();
Log.d(Constants.TAG, "scannedContent: " + scannedContent);
startCertify(Uri.parse(scanResult.getContents()));
} else {
Log.e(Constants.TAG, "scanResult or formatName null! Should not happen!");
finish();
}
return;
}
// if a result has been returned, return it down to other activity
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
returnResult(data);
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
public void returnResult(Intent data) {
if (returnResult) {
setResult(RESULT_OK, data);
finish();
} else {
// 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();
}
}
public void startCertify(Uri dataUri) {
// example: openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282
if (dataUri.getScheme().equals(Constants.FINGERPRINT_SCHEME)) {
String fingerprint = dataUri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH);
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);
}
}
public void importKeys(final String fingerprint) {
// Message is received after importing is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
this,
getString(R.string.progress_importing),
ProgressDialog.STYLE_HORIZONTAL,
true) {
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
// get returned data bundle
Bundle returnData = message.getData();
if (returnData == null) {
finish();
return;
}
final ImportKeyResult result =
returnData.getParcelable(OperationResult.EXTRA_RESULT);
if (result == null) {
Log.e(Constants.TAG, "result == null");
finish();
return;
}
if (!result.success()) {
// only return if no success...
Intent data = new Intent();
data.putExtras(returnData);
returnResult(data);
return;
}
Intent certifyIntent = new Intent(QrCodeScanActivity.this, MultiCertifyKeyActivity.class);
certifyIntent.putExtra(MultiCertifyKeyActivity.EXTRA_RESULT, result);
certifyIntent.putExtra(MultiCertifyKeyActivity.EXTRA_KEY_IDS, result.getImportedMasterKeyIds());
startActivityForResult(certifyIntent, 0);
}
}
};
// search config
Preferences prefs = Preferences.getPreferences(this);
Preferences.CloudSearchPrefs cloudPrefs = new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
// Send all information needed to service to query keys in other thread
Intent intent = new Intent(this, KeychainIntentService.class);
intent.setAction(KeychainIntentService.ACTION_DOWNLOAD_AND_IMPORT_KEYS);
// fill values for this action
Bundle data = new Bundle();
data.putString(KeychainIntentService.DOWNLOAD_KEY_SERVER, cloudPrefs.keyserver);
final ImportKeysListEntry keyEntry = new ImportKeysListEntry();
keyEntry.setFingerprintHex(fingerprint);
keyEntry.addOrigin(cloudPrefs.keyserver);
ArrayList<ImportKeysListEntry> selectedEntries = new ArrayList<ImportKeysListEntry>();
selectedEntries.add(keyEntry);
data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
// show progress dialog
saveHandler.showProgressDialog(this);
// start service with intent
startService(intent);
}
}

View File

@ -36,7 +36,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.ui.util.QrCodeUtils; import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
public class QrCodeActivity extends ActionBarActivity { public class QrCodeViewActivity extends ActionBarActivity {
private ImageView mFingerprintQrCode; private ImageView mFingerprintQrCode;

View File

@ -291,7 +291,7 @@ public class ViewKeyShareFragment extends LoaderFragment implements
private void showQrCodeDialog() { private void showQrCodeDialog() {
Intent qrCodeIntent = new Intent(getActivity(), QrCodeActivity.class); Intent qrCodeIntent = new Intent(getActivity(), QrCodeViewActivity.class);
qrCodeIntent.setData(mDataUri); qrCodeIntent.setData(mDataUri);
startActivity(qrCodeIntent); startActivity(qrCodeIntent);
} }