This commit is contained in:
Daniel Hammann 2014-03-12 20:44:33 +01:00
commit 6ca58dd211
44 changed files with 657 additions and 333 deletions

View file

@ -50,6 +50,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<!-- android:allowBackup="false": Don't allow backup over adb backup or other apps! -->
<application

View file

@ -43,6 +43,8 @@ public final class Constants {
public static final class path {
public static final String APP_DIR = Environment.getExternalStorageDirectory()
+ "/OpenPGP-Keychain";
public static final String APP_DIR_FILE_SEC = APP_DIR + "/secexport.asc";
public static final String APP_DIR_FILE_PUB = APP_DIR + "/pubexport.asc";
}
public static final class pref {

View file

@ -56,28 +56,33 @@ public class ActionBarHelper {
* Sets custom view on ActionBar for Done/Cancel activities
*
* @param actionBar
* @param doneText
* @param doneOnClickListener
* @param cancelText
* @param cancelOnClickListener
* @param firstText
* @param firstDrawableId
* @param firstOnClickListener
* @param secondText
* @param secondDrawableId
* @param secondOnClickListener
*/
public static void setDoneCancelView(ActionBar actionBar, int doneText,
OnClickListener doneOnClickListener, int cancelText,
OnClickListener cancelOnClickListener) {
public static void setTwoButtonView(ActionBar actionBar, int firstText, int firstDrawableId,
OnClickListener firstOnClickListener, int secondText, int secondDrawableId,
OnClickListener secondOnClickListener) {
// Inflate a "Done"/"Cancel" custom action bar view
// Inflate the custom action bar view
final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext()
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
final View customActionBarView = inflater.inflate(
R.layout.actionbar_custom_view_done_cancel, null);
((TextView) customActionBarView.findViewById(R.id.actionbar_done_text)).setText(doneText);
TextView firstTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_done_text));
firstTextView.setText(firstText);
firstTextView.setCompoundDrawablesWithIntrinsicBounds(firstDrawableId, 0, 0, 0);
customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(
doneOnClickListener);
((TextView) customActionBarView.findViewById(R.id.actionbar_cancel_text))
.setText(cancelText);
firstOnClickListener);
TextView secondTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_cancel_text));
secondTextView.setText(secondText);
secondTextView.setCompoundDrawablesWithIntrinsicBounds(secondDrawableId, 0, 0, 0);
customActionBarView.findViewById(R.id.actionbar_cancel).setOnClickListener(
cancelOnClickListener);
secondOnClickListener);
// Show the custom action bar view and hide the normal Home icon and title.
actionBar.setDisplayShowTitleEnabled(false);
@ -91,20 +96,22 @@ public class ActionBarHelper {
* Sets custom view on ActionBar for Done activities
*
* @param actionBar
* @param doneText
* @param doneOnClickListener
* @param firstText
* @param firstOnClickListener
*/
public static void setDoneView(ActionBar actionBar, int doneText,
OnClickListener doneOnClickListener) {
public static void setOneButtonView(ActionBar actionBar, int firstText, int firstDrawableId,
OnClickListener firstOnClickListener) {
// Inflate a "Done" custom action bar view to serve as the "Up" affordance.
final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext()
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
final View customActionBarView = inflater
.inflate(R.layout.actionbar_custom_view_done, null);
((TextView) customActionBarView.findViewById(R.id.actionbar_done_text)).setText(doneText);
TextView firstTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_done_text));
firstTextView.setText(firstText);
firstTextView.setCompoundDrawablesWithIntrinsicBounds(firstDrawableId, 0, 0, 0);
customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(
doneOnClickListener);
firstOnClickListener);
// Show the custom action bar view and hide the normal Home icon and title.
actionBar.setDisplayShowTitleEnabled(false);
@ -112,65 +119,4 @@ public class ActionBarHelper {
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setCustomView(customActionBarView);
}
/**
* Sets custom view on ActionBar for Save activities
*
* @param actionBar
* @param saveText
* @param saveOnClickListener
*/
public static void setSaveView(ActionBar actionBar, int saveText,
OnClickListener saveOnClickListener) {
// Inflate a "Save" custom action bar view to serve as the "Up" affordance.
final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext()
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
final View customActionBarView = inflater
.inflate(R.layout.actionbar_custom_view_save, null);
((TextView) customActionBarView.findViewById(R.id.actionbar_save_text)).setText(saveText);
customActionBarView.findViewById(R.id.actionbar_save).setOnClickListener(
saveOnClickListener);
// Show the custom action bar view and hide the normal Home icon and title.
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowHomeEnabled(false);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setCustomView(customActionBarView);
}
/**
* Sets custom view on ActionBar for Save/Cancel activities
*
* @param actionBar
* @param saveText
* @param saveOnClickListener
* @param cancelText
* @param cancelOnClickListener
*/
public static void setSaveCancelView(ActionBar actionBar, int saveText,
OnClickListener saveOnClickListener, int cancelText,
OnClickListener cancelOnClickListener) {
// Inflate a "Done"/"Cancel" custom action bar view
final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext()
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
final View customActionBarView = inflater.inflate(
R.layout.actionbar_custom_view_save_cancel, null);
((TextView) customActionBarView.findViewById(R.id.actionbar_save_text)).setText(saveText);
customActionBarView.findViewById(R.id.actionbar_save).setOnClickListener(
saveOnClickListener);
((TextView) customActionBarView.findViewById(R.id.actionbar_cancel_text))
.setText(cancelText);
customActionBarView.findViewById(R.id.actionbar_cancel).setOnClickListener(
cancelOnClickListener);
// Show the custom action bar view and hide the normal Home icon and title.
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowHomeEnabled(false);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setCustomView(customActionBarView, new ActionBar.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sufficientlysecure.keychain.helper;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.Context;
import android.util.Patterns;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ContactHelper {
public static final List<String> getMailAccounts(Context context) {
final Account[] accounts = AccountManager.get(context).getAccounts();
final Set<String> emailSet = new HashSet<String>();
for (Account account : accounts) {
if (Patterns.EMAIL_ADDRESS.matcher(account.name).matches()) {
emailSet.add(account.name);
}
}
return new ArrayList<String>(emailSet);
}
}

View file

@ -20,7 +20,6 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
@ -63,7 +62,7 @@ public class ExportHelper {
/**
* Show dialog where to export keys
*/
public void showExportKeysDialog(final Uri dataUri, final int keyType,
public void showExportKeysDialog(final long[] rowIds, final int keyType,
final String exportFilename) {
mExportFilename = exportFilename;
@ -75,7 +74,7 @@ public class ExportHelper {
Bundle data = message.getData();
mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
exportKeys(dataUri, keyType);
exportKeys(rowIds, keyType);
}
}
};
@ -86,7 +85,7 @@ public class ExportHelper {
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
public void run() {
String title = null;
if (dataUri == null) {
if (rowIds == null) {
// export all keys
title = activity.getString(R.string.title_export_keys);
} else {
@ -112,7 +111,7 @@ public class ExportHelper {
/**
* Export keys
*/
public void exportKeys(Uri dataUri, int keyType) {
public void exportKeys(long[] rowIds, int keyType) {
Log.d(Constants.TAG, "exportKeys started");
// Send all information needed to service to export key in other thread
@ -126,13 +125,10 @@ public class ExportHelper {
data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename);
data.putInt(KeychainIntentService.EXPORT_KEY_TYPE, keyType);
if (dataUri == null) {
if (rowIds == null) {
data.putBoolean(KeychainIntentService.EXPORT_ALL, true);
} else {
// TODO: put data uri into service???
long keyRingMasterKeyId = ProviderHelper.getMasterKeyId(activity, dataUri);
data.putLong(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, keyRingMasterKeyId);
data.putLongArray(KeychainIntentService.EXPORT_KEY_RING_ROW_ID, rowIds);
}
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);

View file

@ -144,8 +144,8 @@ public class Preferences {
Constants.defaults.KEY_SERVERS);
Vector<String> servers = new Vector<String>();
String chunks[] = rawData.split(",");
for (int i = 0; i < chunks.length; ++i) {
String tmp = chunks[i].trim();
for (String c : chunks) {
String tmp = c.trim();
if (tmp.length() > 0) {
servers.add(tmp);
}
@ -156,8 +156,8 @@ public class Preferences {
public void setKeyServers(String[] value) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
String rawData = "";
for (int i = 0; i < value.length; ++i) {
String tmp = value[i].trim();
for (String v : value) {
String tmp = v.trim();
if (tmp.length() == 0) {
continue;
}

View file

@ -193,11 +193,10 @@ public class PgpHelper {
* @param context
* @param progress
* @param file
* @throws FileNotFoundException
* @throws IOException
*/
public static void deleteFileSecurely(Context context, ProgressDialogUpdater progress, File file)
throws FileNotFoundException, IOException {
throws IOException {
long length = file.length();
SecureRandom random = new SecureRandom();
RandomAccessFile raf = new RandomAccessFile(file, "rws");

View file

@ -79,8 +79,9 @@ public class PgpImportExport {
public boolean uploadKeyRingToServer(HkpKeyServer server, PGPPublicKeyRing keyring) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = new ArmoredOutputStream(bos);
ArmoredOutputStream aos = null;
try {
aos = new ArmoredOutputStream(bos);
aos.write(keyring.getEncoded());
aos.close();
@ -95,7 +96,8 @@ public class PgpImportExport {
return false;
} finally {
try {
bos.close();
if (aos != null) aos.close();
if (bos != null) bos.close();
} catch (IOException e) {
}
}
@ -155,59 +157,53 @@ public class PgpImportExport {
return returnData;
}
public Bundle exportKeyRings(ArrayList<Long> keyRingMasterKeyIds, int keyType,
OutputStream outStream) throws PgpGeneralException, FileNotFoundException,
public Bundle exportKeyRings(ArrayList<Long> keyRingRowIds, int keyType,
OutputStream outStream) throws PgpGeneralException,
PGPException, IOException {
Bundle returnData = new Bundle();
int rowIdsSize = keyRingRowIds.size();
updateProgress(
mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
keyRingMasterKeyIds.size()), 0, 100);
rowIdsSize), 0, 100);
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
throw new PgpGeneralException(
mContext.getString(R.string.error_external_storage_not_ready));
}
// For each row id
for (int i = 0; i < rowIdsSize; ++i) {
// Create an output stream
ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream);
arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext));
if (keyType == Id.type.secret_key) {
ArmoredOutputStream outSec = new ArmoredOutputStream(outStream);
outSec.setHeader("Version", PgpHelper.getFullVersion(mContext));
for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) {
updateProgress(i * 100 / keyRingMasterKeyIds.size() / 2, 100);
PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(
mContext, keyRingMasterKeyIds.get(i));
// If the keyType is secret get the PGPSecretKeyRing
// based on the row id and encode it to the output
if (keyType == Id.type.secret_key) {
updateProgress(i * 100 / rowIdsSize / 2, 100);
PGPSecretKeyRing secretKeyRing =
ProviderHelper.getPGPSecretKeyRingByRowId(mContext, keyRingRowIds.get(i));
if (secretKeyRing != null) {
secretKeyRing.encode(outSec);
secretKeyRing.encode(arOutStream);
}
}
outSec.close();
} else {
// export public keyrings...
ArmoredOutputStream outPub = new ArmoredOutputStream(outStream);
outPub.setHeader("Version", PgpHelper.getFullVersion(mContext));
for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) {
// double the needed time if exporting both public and secret parts
if (keyType == Id.type.secret_key) {
updateProgress(i * 100 / keyRingMasterKeyIds.size() / 2, 100);
} else {
updateProgress(i * 100 / keyRingMasterKeyIds.size(), 100);
}
PGPPublicKeyRing publicKeyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(
mContext, keyRingMasterKeyIds.get(i));
// Else if it's a public key get the PGPPublicKeyRing
// and encode that to the output
} else {
updateProgress(i * 100 / rowIdsSize, 100);
PGPPublicKeyRing publicKeyRing =
ProviderHelper.getPGPPublicKeyRingByRowId(mContext, keyRingRowIds.get(i));
if (publicKeyRing != null) {
publicKeyRing.encode(outPub);
publicKeyRing.encode(arOutStream);
}
}
outPub.close();
arOutStream.close();
}
returnData.putInt(KeychainIntentService.RESULT_EXPORT, keyRingMasterKeyIds.size());
returnData.putInt(KeychainIntentService.RESULT_EXPORT, rowIdsSize);
updateProgress(R.string.progress_done, 100, 100);

View file

@ -190,7 +190,7 @@ public class PgpKeyOperation {
}
public void changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassPhrase,
String newPassPhrase) throws IOException, PGPException, PGPException,
String newPassPhrase) throws IOException, PGPException,
NoSuchProviderException {
updateProgress(R.string.progress_building_key, 0, 100);

View file

@ -404,6 +404,30 @@ public class ProviderHelper {
return masterKeyIds;
}
/**
* Private helper method
*/
private static ArrayList<Long> getKeyRingsRowIds(Context context, Uri queryUri) {
Cursor cursor = context.getContentResolver().query(queryUri,
new String[]{KeyRings._ID}, null, null, null);
ArrayList<Long> rowIds = new ArrayList<Long>();
if (cursor != null) {
int IdCol = cursor.getColumnIndex(KeyRings._ID);
if (cursor.moveToFirst()) {
do {
rowIds.add(cursor.getLong(IdCol));
} while (cursor.moveToNext());
}
}
if (cursor != null) {
cursor.close();
}
return rowIds;
}
/**
* Retrieves ids of all SecretKeyRings
*/
@ -420,6 +444,22 @@ public class ProviderHelper {
return getKeyRingsMasterKeyIds(context, queryUri);
}
/**
* Retrieves ids of all SecretKeyRings
*/
public static ArrayList<Long> getSecretKeyRingsRowIds(Context context) {
Uri queryUri = KeyRings.buildSecretKeyRingsUri();
return getKeyRingsRowIds(context, queryUri);
}
/**
* Retrieves ids of all PublicKeyRings
*/
public static ArrayList<Long> getPublicKeyRingsRowIds(Context context) {
Uri queryUri = KeyRings.buildPublicKeyRingsUri();
return getKeyRingsRowIds(context, queryUri);
}
public static void deletePublicKeyRing(Context context, long rowId) {
ContentResolver cr = context.getContentResolver();
cr.delete(KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)), null, null);

View file

@ -50,6 +50,7 @@ import org.sufficientlysecure.keychain.pgp.PgpImportExport;
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.DataStream;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
@ -153,6 +154,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
public static final String EXPORT_KEY_TYPE = "export_key_type";
public static final String EXPORT_ALL = "export_all";
public static final String EXPORT_KEY_RING_MASTER_KEY_ID = "export_key_ring_id";
public static final String EXPORT_KEY_RING_ROW_ID = "export_key_rind_row_id";
// upload key
public static final String UPLOAD_KEY_SERVER = "upload_key_server";
@ -675,10 +677,12 @@ public class KeychainIntentService extends IntentService implements ProgressDial
String outputFile = data.getString(EXPORT_FILENAME);
long[] rowIds = new long[0];
// If not exporting all keys get the rowIds of the keys to export from the intent
boolean exportAll = data.getBoolean(EXPORT_ALL);
long keyRingMasterKeyId = -1;
if (!exportAll) {
keyRingMasterKeyId = data.getLong(EXPORT_KEY_RING_MASTER_KEY_ID);
rowIds = data.getLongArray(EXPORT_KEY_RING_ROW_ID);
}
/* Operation */
@ -691,24 +695,26 @@ public class KeychainIntentService extends IntentService implements ProgressDial
// OutputStream
FileOutputStream outStream = new FileOutputStream(outputFile);
ArrayList<Long> keyRingMasterKeyIds = new ArrayList<Long>();
ArrayList<Long> keyRingRowIds = new ArrayList<Long>();
if (exportAll) {
// get all key ring row ids based on export type
// get all key ring row ids based on export type
if (keyType == Id.type.public_key) {
keyRingMasterKeyIds = ProviderHelper.getPublicKeyRingsMasterKeyIds(this);
keyRingRowIds = ProviderHelper.getPublicKeyRingsRowIds(this);
} else {
keyRingMasterKeyIds = ProviderHelper.getSecretKeyRingsMasterKeyIds(this);
keyRingRowIds = ProviderHelper.getSecretKeyRingsRowIds(this);
}
} else {
keyRingMasterKeyIds.add(keyRingMasterKeyId);
for(long rowId : rowIds) {
keyRingRowIds.add(rowId);
}
}
Bundle resultData = new Bundle();
Bundle resultData;
PgpImportExport pgpImportExport = new PgpImportExport(this, this);
resultData = pgpImportExport
.exportKeyRings(keyRingMasterKeyIds, keyType, outStream);
.exportKeyRings(keyRingRowIds, keyType, outStream);
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
} catch (Exception e) {

View file

@ -21,6 +21,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import android.util.LongSparseArray;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPSecretKey;
@ -77,7 +78,7 @@ public class PassphraseCacheService extends Service {
private BroadcastReceiver mIntentReceiver;
private HashMap<Long, String> mPassphraseCache = new HashMap<Long, String>();
private LongSparseArray<String> mPassphraseCache = new LongSparseArray<String>();
Context mContext;
@ -347,7 +348,7 @@ public class PassphraseCacheService extends Service {
Log.d(TAG, "Timeout of keyId " + keyId + ", removed from memory!");
// stop whole service if no cached passphrases remaining
if (mPassphraseCache.isEmpty()) {
if (mPassphraseCache.size() == 0) {
Log.d(TAG, "No passphrases remaining in memory, stopping service!");
stopSelf();
}

View file

@ -41,7 +41,7 @@ public class AppSettingsActivity extends ActionBarActivity {
super.onCreate(savedInstanceState);
// Inflate a "Done" custom action bar
ActionBarHelper.setDoneView(getSupportActionBar(), R.string.api_settings_save,
ActionBarHelper.setOneButtonView(getSupportActionBar(), R.string.api_settings_save, R.drawable.ic_action_done,
new View.OnClickListener() {
@Override
public void onClick(View v) {

View file

@ -88,7 +88,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
final byte[] packageSignature = extras.getByteArray(EXTRA_PACKAGE_SIGNATURE);
// Inflate a "Done"/"Cancel" custom action bar view
ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.api_register_allow,
ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.api_register_allow, R.drawable.ic_action_done,
new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -108,7 +108,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
RemoteServiceActivity.this.finish();
}
}
}, R.string.api_register_disallow, new View.OnClickListener() {
}, R.string.api_register_disallow, R.drawable.ic_action_cancel, new View.OnClickListener() {
@Override
public void onClick(View v) {
// Disallow
@ -161,7 +161,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
}
// Inflate a "Done"/"Cancel" custom action bar view
ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_okay,
ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_okay, R.drawable.ic_action_done,
new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -173,7 +173,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
RemoteServiceActivity.this.finish();
}
}, R.string.btn_do_not_save, new View.OnClickListener() {
}, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() {
@Override
public void onClick(View v) {
// cancel
@ -214,7 +214,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
String text = "<font color=\"red\">" + errorMessage + "</font>";
// Inflate a "Done" custom action bar view
ActionBarHelper.setDoneView(getSupportActionBar(), R.string.btn_okay,
ActionBarHelper.setOneButtonView(getSupportActionBar(), R.string.btn_okay, R.drawable.ic_action_done,
new View.OnClickListener() {
@Override

View file

@ -530,6 +530,10 @@ public class DecryptActivity extends DrawerActivity {
Log.e(Constants.TAG, "File not found!", e);
AppMsg.makeText(this, getString(R.string.error_file_not_found, e.getMessage()),
AppMsg.STYLE_ALERT).show();
} finally {
try {
if (inStream != null) inStream.close();
} catch (Exception e){ }
}
} else {
inStream = new ByteArrayInputStream(mMessage.getText().toString().getBytes());

View file

@ -134,14 +134,14 @@ public class EditKeyActivity extends ActionBarActivity {
* @param intent
*/
private void handleActionCreateKey(Intent intent) {
// Inflate a "Done"/"Cancel" custom action bar
ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_save,
// Inflate a "Save"/"Cancel" custom action bar
ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_save, R.drawable.ic_action_save,
new View.OnClickListener() {
@Override
public void onClick(View v) {
saveClicked();
}
}, R.string.btn_do_not_save, new View.OnClickListener() {
}, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() {
@Override
public void onClick(View v) {
cancelClicked();
@ -249,8 +249,8 @@ public class EditKeyActivity extends ActionBarActivity {
* @param intent
*/
private void handleActionEditKey(Intent intent) {
// Inflate a "Done"/"Cancel" custom action bar
ActionBarHelper.setSaveView(getSupportActionBar(), R.string.btn_save,
// Inflate a "Save"/"Cancel" custom action bar
ActionBarHelper.setOneButtonView(getSupportActionBar(), R.string.btn_save, R.drawable.ic_action_save,
new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -329,8 +329,8 @@ public class EditKeyActivity extends ActionBarActivity {
cancelClicked();
return true;
case R.id.menu_key_edit_export_file:
mExportHelper.showExportKeysDialog(mDataUri, Id.type.secret_key, Constants.path.APP_DIR
+ "/secexport.asc");
long[] ids = new long[]{Long.valueOf(mDataUri.getLastPathSegment())};
mExportHelper.showExportKeysDialog(ids, Id.type.secret_key, Constants.path.APP_DIR_FILE_SEC);
return true;
case R.id.menu_key_edit_delete: {
// Message is received after key is deleted

View file

@ -59,8 +59,7 @@ public class KeyListPublicActivity extends DrawerActivity {
return true;
case R.id.menu_key_list_public_export:
mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.path.APP_DIR
+ "/pubexport.asc");
mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.path.APP_DIR_FILE_PUB);
return true;
default:

View file

@ -18,10 +18,11 @@
package org.sufficientlysecure.keychain.ui;
import java.util.ArrayList;
import java.util.Set;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
@ -47,6 +48,7 @@ import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.text.TextUtils;
import android.view.ActionMode;
@ -179,6 +181,11 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
encrypt(mode, ids);
break;
}
case R.id.menu_key_list_public_multi_export: {
ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity());
mExportHelper.showExportKeysDialog(ids, Id.type.public_key, Constants.path.APP_DIR_FILE_PUB);
break;
}
case R.id.menu_key_list_public_multi_delete: {
showDeleteKeyDialog(mode, ids);
break;
@ -367,7 +374,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
// Execute this when searching
mSearchView.setOnQueryTextListener(this);
//Erase search result without focus
// Erase search result without focus
MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
@ -377,6 +384,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
mCurQuery = null;
mSearchView.setQuery("", true);
getLoaderManager().restartLoader(0, null, KeyListPublicFragment.this);
return true;
}

View file

@ -62,8 +62,7 @@ public class KeyListSecretActivity extends DrawerActivity {
return true;
case R.id.menu_key_list_secret_export:
mExportHelper.showExportKeysDialog(null, Id.type.secret_key, Constants.path.APP_DIR
+ "/secexport.asc");
mExportHelper.showExportKeysDialog(null, Id.type.secret_key, Constants.path.APP_DIR_FILE_SEC);
return true;
case R.id.menu_key_list_secret_import:

View file

@ -17,10 +17,10 @@
package org.sufficientlysecure.keychain.ui;
import java.util.Set;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
@ -41,6 +41,7 @@ import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v7.app.ActionBarActivity;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
@ -106,6 +107,12 @@ public class KeyListSecretFragment extends ListFragment implements
}
break;
}
case R.id.menu_key_list_public_multi_export: {
ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity());
mExportHelper.showExportKeysDialog(ids, Id.type.secret_key, Constants.path.APP_DIR_FILE_SEC);
break;
}
}
return true;
}

View file

@ -50,14 +50,14 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O
super.onCreate(savedInstanceState);
// Inflate a "Done"/"Cancel" custom action bar view
ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_okay,
ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_okay, R.drawable.ic_action_done,
new View.OnClickListener() {
@Override
public void onClick(View v) {
// ok
okClicked();
}
}, R.string.btn_do_not_save, new View.OnClickListener() {
}, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() {
@Override
public void onClick(View v) {
// cancel
@ -81,11 +81,11 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O
Intent intent = getIntent();
String servers[] = intent.getStringArrayExtra(EXTRA_KEY_SERVERS);
if (servers != null) {
for (int i = 0; i < servers.length; ++i) {
for (String serv : servers) {
KeyServerEditor view = (KeyServerEditor) mInflater.inflate(
R.layout.key_server_editor, mEditors, false);
view.setEditorListener(this);
view.setValue(servers[i]);
view.setValue(serv);
mEditors.addView(view);
}
}

View file

@ -46,14 +46,14 @@ public class SelectPublicKeyActivity extends ActionBarActivity {
super.onCreate(savedInstanceState);
// Inflate a "Done"/"Cancel" custom action bar view
ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_okay,
ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_okay, R.drawable.ic_action_done,
new View.OnClickListener() {
@Override
public void onClick(View v) {
// ok
okClicked();
}
}, R.string.btn_do_not_save, new View.OnClickListener() {
}, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() {
@Override
public void onClick(View v) {
// cancel

View file

@ -199,8 +199,8 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
if (masterKeyIds != null) {
for (int i = 0; i < getListView().getCount(); ++i) {
long keyId = mAdapter.getMasterKeyId(i);
for (int j = 0; j < masterKeyIds.length; ++j) {
if (keyId == masterKeyIds[j]) {
for (long masterKeyId : masterKeyIds) {
if (keyId == masterKeyId) {
getListView().setItemChecked(i, true);
break;
}

View file

@ -71,50 +71,44 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
mKeyUserIdRest.setVisibility(View.GONE);
} else {
String uid = getResources().getString(R.string.user_id_no_name);
String uidExtra = "";
String masterkeyIdHex = "";
PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(
getActivity(), secretKeyId);
if (keyRing != null) {
PGPSecretKey key = PgpKeyHelper.getMasterKey(keyRing);
masterkeyIdHex = PgpKeyHelper.convertKeyIdToHex(secretKeyId);
String masterkeyIdHex = PgpKeyHelper.convertKeyIdToHex(secretKeyId);
if (key != null) {
String userId = PgpKeyHelper.getMainUserIdSafe(getActivity(), key);
/*String chunks[] = mUserId.split(" <", 2);
uid = chunks[0];
if (chunks.length > 1) {
uidExtra = "<" + chunks[1];
}*/
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
String userName, userEmail;
String userName, userEmail;
if (userIdSplit[0] != null) { userName = userIdSplit[0]; }
else { userName = getActivity().getResources().getString(R.string.user_id_no_name); }
if (userIdSplit[0] != null) {
userName = userIdSplit[0];
} else {
userName = getActivity().getResources().getString(R.string.user_id_no_name);
}
if (userIdSplit[1] != null) { userEmail = userIdSplit[1]; }
else { userEmail = getActivity().getResources().getString(R.string.error_user_id_no_email); }
if (userIdSplit[1] != null) {
userEmail = userIdSplit[1];
} else {
userEmail = getActivity().getResources().getString(R.string.error_user_id_no_email);
}
mKeyMasterKeyIdHex.setText(masterkeyIdHex);
mKeyUserId.setText(userName);
mKeyUserIdRest.setText(userEmail);
mKeyUserId.setVisibility(View.VISIBLE);
mKeyUserIdRest.setVisibility(View.VISIBLE);
}
else{
} else {
mKeyMasterKeyIdHex.setText(getActivity().getResources().getString(R.string.no_key));
mKeyUserId.setVisibility(View.GONE);
mKeyUserIdRest.setVisibility(View.GONE);
}
}
else{
mKeyMasterKeyIdHex.setText(getActivity().getResources().getString(R.string.no_keys_added_or_updated)+" for master id: "+secretKeyId);
mKeyUserId.setVisibility(View.GONE);
mKeyUserIdRest.setVisibility(View.GONE);
} else {
mKeyMasterKeyIdHex.setText(getActivity().getResources().getString(R.string.no_keys_added_or_updated) + " for master id: " + secretKeyId);
mKeyUserId.setVisibility(View.GONE);
mKeyUserIdRest.setVisibility(View.GONE);
}
}
@ -154,31 +148,31 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
startActivityForResult(intent, REQUEST_CODE_SELECT_KEY);
}
//Select Secret Key Activity delivers the intent which was sent by it using interface to Select
// Select Secret Key Activity delivers the intent which was sent by it using interface to Select
// Secret Key Fragment.Intent contains Master Key Id, User Email, User Name, Master Key Id Hex.
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode & 0xFFFF) {
case REQUEST_CODE_SELECT_KEY: {
long secretKeyId;
if (resultCode == Activity.RESULT_OK) {
Bundle bundle = data.getExtras();
secretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID);
selectKey(secretKeyId);
case REQUEST_CODE_SELECT_KEY: {
long secretKeyId;
if (resultCode == Activity.RESULT_OK) {
Bundle bundle = data.getExtras();
secretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID);
selectKey(secretKeyId);
// remove displayed errors
mKeyUserId.setError(null);
// remove displayed errors
mKeyUserId.setError(null);
// give value back to callback
mCallback.onKeySelected(secretKeyId);
// give value back to callback
mCallback.onKeySelected(secretKeyId);
}
break;
}
break;
}
default:
super.onActivityResult(requestCode, resultCode, data);
default:
super.onActivityResult(requestCode, resultCode, data);
break;
break;
}
}
}

View file

@ -118,8 +118,8 @@ public class ViewKeyActivity extends ActionBarActivity {
uploadToKeyserver(mDataUri);
return true;
case R.id.menu_key_view_export_file:
mExportHelper.showExportKeysDialog(mDataUri, Id.type.public_key, Constants.path.APP_DIR
+ "/pubexport.asc");
long[] ids = new long[]{Long.valueOf(mDataUri.getLastPathSegment())};
mExportHelper.showExportKeysDialog(ids, Id.type.public_key, Constants.path.APP_DIR_FILE_PUB);
return true;
case R.id.menu_key_view_share_default_fingerprint:
shareKey(mDataUri, true);

View file

@ -280,13 +280,14 @@ public class ViewKeyMainFragment extends Fragment implements
// for each 4 characters of the fingerprint + 1 space
for (int i = 0; i < fingerprint.length(); i += 5) {
String fourChars = fingerprint.substring(i, Math.min(i + 4, fingerprint.length()));
int minFingLength = Math.min(i + 4, fingerprint.length());
String fourChars = fingerprint.substring(i, minFingLength);
// Create a foreground color by converting the 4 fingerprint chars to an int hashcode
// and then converting that int to hex to use as a color
fcs = new ForegroundColorSpan(
Color.parseColor(String.format("#%06X", (0xFFFFFF & fourChars.hashCode()))));
sb.setSpan(fcs, i, Math.min(i+4, fingerprint.length()), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
sb.setSpan(fcs, i, minFingLength, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
return sb;

View file

@ -18,8 +18,6 @@
package org.sufficientlysecure.keychain.ui.adapter;
import java.util.HashMap;
import java.util.Set;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;

View file

@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.ui.adapter;
import java.util.HashMap;
import java.util.Set;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;

View file

@ -32,6 +32,7 @@ import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.Choice;
import java.util.ArrayList;
import java.util.Vector;
public class CreateKeyDialogFragment extends DialogFragment {
@ -78,7 +79,7 @@ public class CreateKeyDialogFragment extends DialogFragment {
boolean wouldBeMasterKey = (childCount == 0);
final Spinner algorithm = (Spinner) view.findViewById(R.id.create_key_algorithm);
Vector<Choice> choices = new Vector<Choice>();
ArrayList<Choice> choices = new ArrayList<Choice>();
choices.add(new Choice(Id.choice.algorithm.dsa, getResources().getString(
R.string.dsa)));
if (!wouldBeMasterKey) {

View file

@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui.widget;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.widget.*;
import org.sufficientlysecure.keychain.R;
import android.content.Context;
@ -26,11 +27,9 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import com.beardedhen.androidbootstrap.BootstrapButton;
import org.sufficientlysecure.keychain.helper.ContactHelper;
public class UserIdEditor extends LinearLayout implements Editor, OnClickListener {
private EditorListener mEditorListener = null;
@ -38,7 +37,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
private BootstrapButton mDeleteButton;
private RadioButton mIsMainUserId;
private EditText mName;
private EditText mEmail;
private AutoCompleteTextView mEmail;
private EditText mComment;
// see http://www.regular-expressions.info/email.html
@ -102,9 +101,17 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
mIsMainUserId.setOnClickListener(this);
mName = (EditText) findViewById(R.id.name);
mEmail = (EditText) findViewById(R.id.email);
mEmail = (AutoCompleteTextView) findViewById(R.id.email);
mComment = (EditText) findViewById(R.id.comment);
mEmail.setThreshold(1); // Start working from first character
mEmail.setAdapter(
new ArrayAdapter<String>
(this.getContext(), android.R.layout.simple_dropdown_item_1line,
ContactHelper.getMailAccounts(getContext())
));
super.onFinishInflate();
}

View file

@ -226,7 +226,7 @@ public class HkpKeyServer extends KeyServer {
HttpClient client = new DefaultHttpClient();
try {
HttpGet get = new HttpGet("http://" + mHost + ":" + mPort
+ "/pks/lookup?op=get&search=0x" + PgpKeyHelper.convertKeyIdToHex(keyId));
+ "/pks/lookup?op=get&search=" + PgpKeyHelper.convertKeyIdToHex(keyId));
HttpResponse response = client.execute(get);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {

View file

@ -1,27 +0,0 @@
<!--
Copyright 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:dividerPadding="12dp"
android:orientation="horizontal"
android:divider="@drawable/abc_list_divider_holo_light"
android:showDividers="end" >
<include layout="@layout/actionbar_include_save_button" />
</LinearLayout>

View file

@ -1,29 +0,0 @@
<!--
Copyright 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:dividerPadding="12dp"
android:divider="@drawable/abc_list_divider_holo_light"
android:orientation="horizontal"
android:showDividers="middle">
<include layout="@layout/actionbar_include_cancel_button" />
<include layout="@layout/actionbar_include_save_button" />
</LinearLayout>

View file

@ -1,36 +0,0 @@
<!--
Copyright 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/actionbar_save"
style="@style/Widget.AppCompat.ActionButton"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
<TextView
android:id="@+id/actionbar_save_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawableLeft="@drawable/ic_action_save"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:paddingRight="20dp"
style="@style/Widget.AppCompat.Light.ActionBar.TabText"
android:text="Save (set in-code!)" />
</FrameLayout>

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

View file

@ -49,7 +49,7 @@
android:paddingRight="5dip"
android:text="@string/label_email" />
<EditText
<AutoCompleteTextView
android:id="@+id/email"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >

View file

@ -3,7 +3,7 @@
xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
android:orientation="horizontal">
<com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/select_secret_key_select_key_button"
@ -25,18 +25,9 @@
android:layout_marginLeft="4dp"
android:layout_marginTop="4dp"
android:orientation="vertical"
android:paddingLeft="16dp" >
android:paddingLeft="4dp">
<!-- Has been made focusable to display error messages with setError -->
<TextView
android:id="@+id/select_secret_key_master_key_hex"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/api_settings_no_key"
/>
<TextView
android:id="@+id/select_secret_key_user_id"
android:layout_width="wrap_content"
@ -47,6 +38,7 @@
android:focusableInTouchMode="true"
android:singleLine="true"
android:visibility="gone"
android:layout_marginRight="5dip"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall" />
@ -57,10 +49,19 @@
android:layout_gravity="left"
android:ellipsize="end"
android:singleLine="true"
android:layout_marginRight="5dip"
android:text=""
android:visibility="gone"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/select_secret_key_master_key_hex"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="@string/api_settings_no_key"
android:layout_marginRight="5dip" />
</LinearLayout>

View file

@ -5,6 +5,10 @@
android:id="@+id/menu_key_list_public_multi_select_all"
android:icon="@drawable/ic_action_select_all"
android:title="@string/menu_select_all" />
<item
android:id="@+id/menu_key_list_public_multi_export"
android:icon="@drawable/ic_action_import_export"
android:title="@string/menu_export_key" />
<item
android:id="@+id/menu_key_list_public_multi_encrypt"
android:icon="@drawable/ic_action_secure"

View file

@ -5,6 +5,10 @@
android:id="@+id/menu_key_list_public_multi_select_all"
android:icon="@drawable/ic_action_select_all"
android:title="@string/menu_select_all" />
<item
android:id="@+id/menu_key_list_public_multi_export"
android:icon="@drawable/ic_action_import_export"
android:title="@string/menu_export_key" />
<item
android:id="@+id/menu_key_list_public_multi_delete"
android:icon="@drawable/ic_action_discard"

View file

@ -155,6 +155,14 @@ See http://source.android.com/source/code-style.html
See http://www.androidpolice.com/2009/11/04/auto-formatting-android-xml-files-with-eclipse/
### Automated syntax check with CheckStyle
* Paste the tools/checkstyle.xml file to ~/.AndroidStudioPreview/config/codestyles/ (in Linux/Unix)
or ~/Library/Preferences/AndroidStudioPreview/codestyles (in Mac OSX)
* Go to Settings (or Preferences in Mac OS X) > Code Style > Java, select OpenPgpChecker,
as well as Code Style > XML and select OpenPgpChecker again.
* Start code inspection and see the results by selecting Analyze > Inspect Code from Android-Studio
or you can directly run checkstyle via cli with .tools/checkstyle. Make sure it's executable first.
## Licenses
OpenPGP Kechain is licensed under GPLv3+.
Some parts (older parts and some libraries are Apache License v2, MIT X11 License)

1
tools/checkstyle Executable file
View file

@ -0,0 +1 @@
checkstyle -c tools/checkstyle.xml -r OpenPGP-Keychain/src/main/java 2>&1 | egrep -v 'log4j'

356
tools/checkstyle.xml Normal file
View file

@ -0,0 +1,356 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<!-- This is a checkstyle configuration file. For descriptions of
what the following rules do, please see the checkstyle configuration
page at http://checkstyle.sourceforge.net/config.html -->
<module name="OpenPgpChecker">
<module name="SuppressionFilter">
<property name="file" value="tools/suppressions.xml"/>
</module>
<module name="RegexpSingleline">
<!-- Requires a copyright notice in each file.
Code intended to be open-sourced may have a multi-line copyright
notice, so that this required text appears on the second line:
<pre>
/*
* Copyright 2008 Google Inc.
*
* (details of open-source license...)
</pre>
-->
<property name="format"
value="^(//| \*) Copyright (\([cC]\) )?[\d]{4}(\-[\d]{4})? .*$"/>
<property name="minimum" value="1"/>
<property name="maximum" value="10"/>
<property name="message" value="Copyright is missing or malformed."/>
<property name="severity" value="error"/>
</module>
<module name="FileTabCharacter">
<!-- Checks that there are no tab characters in the file.
-->
</module>
<module name="NewlineAtEndOfFile">
<property name="lineSeparator" value="lf"/>
</module>
<module name="RegexpSingleline">
<!-- Checks that FIXME is not used in comments. TODO is preferred.
-->
<property name="format" value="((//.*)|(\*.*))FIXME"/>
<property name="message" value='TODO is preferred to FIXME. e.g. "TODO(johndoe): Refactor when v2 is released."'/>
</module>
<!--
<module name="RegexpSingleline">
<property name="format" value="((//.*)|(\*.*))TODO[^(]"/>
<property name="message" value='All TODOs should be named. e.g. "TODO(johndoe): Refactor when v2 is released."'/>
</module>
-->
<!-- All Java AST specific tests live under TreeWalker module. -->
<module name="TreeWalker">
<!--
IMPORT CHECKS
-->
<module name="RedundantImport">
<property name="severity" value="error"/>
</module>
<module name="UnusedImports">
<property name="severity" value="error"/>
</module>
<module name="ImportOrder">
<!-- Checks for out of order import statements. -->
<property name="severity" value="warning"/>
<property name="groups" value="com.google,android,junit,com,net,org,se,java,javax"/>
<!-- This ensures that static imports go first. -->
<property name="option" value="top"/>
<property name="tokens" value="STATIC_IMPORT, IMPORT"/>
</module>
<!--
JAVADOC CHECKS
-->
<!-- Checks for Javadoc comments. -->
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
<!--
<module name="JavadocMethod">
<property name="scope" value="protected"/>
<property name="severity" value="warning"/>
<property name="allowMissingJavadoc" value="true"/>
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
<property name="allowMissingThrowsTags" value="true"/>
<property name="allowThrowsTagsForSubclasses" value="true"/>
<property name="allowUndeclaredRTE" value="true"/>
</module>
<module name="JavadocType">
<property name="scope" value="protected"/>
<property name="severity" value="error"/>
</module>
<module name="JavadocStyle">
<property name="severity" value="warning"/>
</module>
-->
<!--
NAMING CHECKS
-->
<!-- Item 38 - Adhere to generally accepted naming conventions -->
<module name="PackageName">
<!-- Validates identifiers for package names against the
supplied expression. -->
<!-- Here the default checkstyle rule restricts package name parts to
seven characters, this is not in line with common practice at Google.
-->
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]{1,})*$"/>
<property name="severity" value="warning"/>
</module>
<module name="TypeNameCheck">
<!-- Validates static, final fields against the
expression "^[A-Z][a-zA-Z0-9]*$". -->
<metadata name="altname" value="TypeName"/>
<property name="severity" value="warning"/>
</module>
<module name="ConstantNameCheck">
<!-- Validates non-private, static, final fields against the supplied
public/package final fields "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$". -->
<metadata name="altname" value="ConstantName"/>
<property name="applyToPublic" value="true"/>
<property name="applyToProtected" value="true"/>
<property name="applyToPackage" value="true"/>
<property name="applyToPrivate" value="false"/>
<property name="format" value="^([A-Z_][A-Z0-9]*(_[A-Z0-9]+)*|FLAG_.*)$"/>
<message key="name.invalidPattern"
value="Variable ''{0}'' should be in ALL_CAPS (if it is a constant) or be private (otherwise)."/>
<property name="severity" value="warning"/>
</module>
<module name="StaticVariableNameCheck">
<!-- Validates static, non-final fields against the supplied
expression "^[a-z][a-zA-Z0-9]*_?$". -->
<metadata name="altname" value="StaticVariableName"/>
<property name="applyToPublic" value="true"/>
<property name="applyToProtected" value="true"/>
<property name="applyToPackage" value="true"/>
<property name="applyToPrivate" value="true"/>
<property name="format" value="^s[A-Z][a-zA-Z0-9]*_?$"/>
<property name="severity" value="warning"/>
</module>
<module name="MemberNameCheck">
<!-- Validates non-static members against the supplied expression. -->
<metadata name="altname" value="MemberName"/>
<property name="applyToPublic" value="false"/>
<property name="applyToProtected" value="true"/>
<property name="applyToPackage" value="true"/>
<property name="applyToPrivate" value="true"/>
<property name="format" value="^m[A-Z][a-zA-Z0-9]*$"/>
<property name="severity" value="warning"/>
</module>
<module name="MemberNameCheck">
<!-- Validates non-static members against the supplied expression. -->
<metadata name="altname" value="MemberName"/>
<property name="applyToPublic" value="true"/>
<property name="applyToProtected" value="false"/>
<property name="applyToPackage" value="false"/>
<property name="applyToPrivate" value="false"/>
<property name="format" value="^[a-z]([^A-Z]|$)[a-zA-Z0-9]*$"/>
<property name="severity" value="warning"/>
</module>
<module name="MethodNameCheck">
<!-- Validates identifiers for method names. -->
<metadata name="altname" value="MethodName"/>
<property name="format" value="^[a-z]([^A-Z]|$)[a-zA-Z0-9]*(_[a-zA-Z0-9]+)*$"/>
<property name="severity" value="warning"/>
</module>
<module name="ParameterName">
<!-- Validates identifiers for method parameters against the
expression "^[a-z][a-zA-Z0-9]*$". -->
<property name="severity" value="warning"/>
</module>
<module name="LocalFinalVariableName">
<!-- Validates identifiers for local final variables against the
expression "^[a-z][a-zA-Z0-9]*$". -->
<property name="severity" value="warning"/>
</module>
<module name="LocalVariableName">
<!-- Validates identifiers for local variables against the
expression "^[a-z][a-zA-Z0-9]*$". -->
<property name="severity" value="warning"/>
</module>
<!--
LENGTH and CODING CHECKS
-->
<module name="LineLength">
<!-- Checks if a line is too long. -->
<property name="max" value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.max}" default="110"/>
<property name="severity" value="error"/>
<!--
The default ignore pattern exempts the following elements:
- import statements
- long URLs inside comments
-->
<property name="ignorePattern"
value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.ignorePattern}"
default="^(package .*;\s*)|(import .*;\s*)|( *\* *https?://.*)$"/>
</module>
<module name="LeftCurly">
<!-- Checks for placement of the left curly brace ('{'). -->
<property name="severity" value="warning"/>
</module>
<module name="RightCurly">
<!-- Checks right curlies on CATCH, ELSE, and TRY blocks are on
the same line. e.g., the following example is fine:
<pre>
if {
...
} else
</pre>
-->
<!-- This next example is not fine:
<pre>
if {
...
}
else
</pre>
-->
<property name="option" value="same"/>
<property name="severity" value="warning"/>
</module>
<!-- Checks for braces around if and else blocks -->
<module name="NeedBraces">
<property name="severity" value="warning"/>
<property name="tokens" value="LITERAL_IF, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO"/>
</module>
<module name="UpperEll">
<!-- Checks that long constants are defined with an upper ell.-->
<property name="severity" value="error"/>
</module>
<module name="FallThrough">
<!-- Warn about falling through to the next case statement. Similar to
javac -Xlint:fallthrough, but the check is suppressed if a single-line comment
on the last non-blank line preceding the fallen-into case contains 'fall through' (or
some other variants which we don't publicized to promote consistency).
-->
<property name="reliefPattern"
value="fall through|Fall through|fallthru|Fallthru|falls through|Falls through|fallthrough|Fallthrough|No break|NO break|no break|continue on"/>
<property name="severity" value="error"/>
</module>
<!--
MODIFIERS CHECKS
-->
<module name="ModifierOrder">
<!-- Warn if modifier order is inconsistent with JLS3 8.1.1, 8.3.1, and
8.4.3. The prescribed order is:
public, protected, private, abstract, static, final, transient, volatile,
synchronized, native, strictfp
-->
</module>
<!--
WHITESPACE CHECKS
-->
<module name="WhitespaceAround">
<!-- Checks that various tokens are surrounded by whitespace.
This includes most binary operators and keywords followed
by regular or curly braces.
-->
<property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR,
BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN,
EQUAL, GE, GT, LAND, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE,
LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN,
LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS,
MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION,
SL, SL_ASSIGN, SR_ASSIGN, STAR, STAR_ASSIGN"/>
<property name="severity" value="error"/>
</module>
<module name="WhitespaceAfter">
<!-- Checks that commas, semicolons and typecasts are followed by
whitespace.
-->
<property name="tokens" value="COMMA, SEMI, TYPECAST"/>
</module>
<module name="NoWhitespaceAfter">
<!-- Checks that there is no whitespace after various unary operators.
Linebreaks are allowed.
-->
<property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS,
UNARY_PLUS"/>
<property name="allowLineBreaks" value="true"/>
<property name="severity" value="error"/>
</module>
<module name="NoWhitespaceBefore">
<!-- Checks that there is no whitespace before various unary operators.
Linebreaks are allowed.
-->
<property name="tokens" value="SEMI, DOT, POST_DEC, POST_INC"/>
<property name="allowLineBreaks" value="true"/>
<property name="severity" value="error"/>
</module>
<module name="ParenPad">
<!-- Checks that there is no whitespace before close parens or after
open parens.
-->
<property name="severity" value="warning"/>
</module>
</module>
</module>