Merge remote-tracking branch 'origin/master' into external-test

Conflicts:
	OpenKeychain-Test/src/test/resources/extern/OpenPGP-Haskell
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
This commit is contained in:
Vincent Breitmoser 2014-07-11 20:21:19 +02:00
commit 54bc874ce5
9 changed files with 296 additions and 76 deletions

View file

@ -53,6 +53,12 @@ import java.util.Set;
public class OpenPgpService extends RemoteService {
static final String[] KEYRING_PROJECTION =
new String[]{
KeyRings._ID,
KeyRings.MASTER_KEY_ID,
};
/**
* Search database for key ids based on emails.
*
@ -70,7 +76,7 @@ public class OpenPgpService extends RemoteService {
for (String email : encryptionUserIds) {
Uri uri = KeyRings.buildUnifiedKeyRingsFindByEmailUri(email);
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
Cursor cursor = getContentResolver().query(uri, KEYRING_PROJECTION, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
long id = cursor.getLong(cursor.getColumnIndex(KeyRings.MASTER_KEY_ID));

View file

@ -317,21 +317,21 @@ public class OperationResultParcel implements Parcelable {
public static class OperationLog implements Iterable<LogEntryParcel> {
private final List<LogEntryParcel> parcels = new ArrayList<LogEntryParcel>();
private final List<LogEntryParcel> mParcels = new ArrayList<LogEntryParcel>();
/// Simple convenience method
public void add(LogLevel level, LogType type, int indent, Object... parameters) {
Log.d(Constants.TAG, type.toString());
parcels.add(new OperationResultParcel.LogEntryParcel(level, type, indent, parameters));
mParcels.add(new OperationResultParcel.LogEntryParcel(level, type, indent, parameters));
}
public void add(LogLevel level, LogType type, int indent) {
Log.d(Constants.TAG, type.toString());
parcels.add(new OperationResultParcel.LogEntryParcel(level, type, indent, (Object[]) null));
mParcels.add(new OperationResultParcel.LogEntryParcel(level, type, indent, (Object[]) null));
}
public boolean containsWarnings() {
for(LogEntryParcel entry : new IterableIterator<LogEntryParcel>(parcels.iterator())) {
for(LogEntryParcel entry : new IterableIterator<LogEntryParcel>(mParcels.iterator())) {
if (entry.mLevel == LogLevel.WARN || entry.mLevel == LogLevel.ERROR) {
return true;
}
@ -340,20 +340,20 @@ public class OperationResultParcel implements Parcelable {
}
public void addAll(List<LogEntryParcel> parcels) {
parcels.addAll(parcels);
mParcels.addAll(parcels);
}
public List<LogEntryParcel> toList() {
return parcels;
return mParcels;
}
public boolean isEmpty() {
return parcels.isEmpty();
return mParcels.isEmpty();
}
@Override
public Iterator<LogEntryParcel> iterator() {
return parcels.iterator();
return mParcels.iterator();
}
}

View file

@ -54,6 +54,7 @@ import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter;
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAddedAdapter;
import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
@ -214,9 +215,17 @@ public class EditKeyFragment extends LoaderFragment implements
mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mUserIdsAddedData);
mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter);
mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0);
mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0, mSaveKeyringParcel);
mSubkeysList.setAdapter(mSubkeysAdapter);
mSubkeysList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
long keyId = mSubkeysAdapter.getKeyId(position);
editSubkey(keyId);
}
});
mSubkeysAddedAdapter = new SubkeysAddedAdapter(getActivity(), mSaveKeyringParcel.mAddSubKeys);
mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter);
@ -342,6 +351,46 @@ public class EditKeyFragment extends LoaderFragment implements
});
}
private void editSubkey(final long keyId) {
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case EditSubkeyDialogFragment.MESSAGE_CHANGE_EXPIRY:
// toggle
// if (mSaveKeyringParcel.changePrimaryUserId != null
// && mSaveKeyringParcel.changePrimaryUserId.equals(userId)) {
// mSaveKeyringParcel.changePrimaryUserId = null;
// } else {
// mSaveKeyringParcel.changePrimaryUserId = userId;
// }
break;
case EditSubkeyDialogFragment.MESSAGE_REVOKE:
// toggle
if (mSaveKeyringParcel.mRevokeSubKeys.contains(keyId)) {
mSaveKeyringParcel.mRevokeSubKeys.remove(keyId);
} else {
mSaveKeyringParcel.mRevokeSubKeys.add(keyId);
}
break;
}
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
}
};
// Create a new Messenger for the communication back
final Messenger messenger = new Messenger(returnHandler);
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
public void run() {
EditSubkeyDialogFragment dialogFragment =
EditSubkeyDialogFragment.newInstance(messenger);
dialogFragment.show(getActivity().getSupportFragmentManager(), "editSubkeyDialog");
}
});
}
private void addUserId() {
mUserIdsAddedAdapter.add(new UserIdsAddedAdapter.UserIdModel());
}
@ -432,4 +481,4 @@ public class EditKeyFragment extends LoaderFragment implements
// start service with intent
getActivity().startService(intent);
}
}
}

View file

@ -32,14 +32,15 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import java.util.Date;
public class SubkeysAdapter extends CursorAdapter {
private LayoutInflater mInflater;
private SaveKeyringParcel mSaveKeyringParcel;
private boolean hasAnySecret;
private ColorStateList mDefaultTextColor;
public static final String[] SUBKEYS_PROJECTION = new String[]{
@ -71,10 +72,21 @@ public class SubkeysAdapter extends CursorAdapter {
private static final int INDEX_EXPIRY = 11;
private static final int INDEX_FINGERPRINT = 12;
public SubkeysAdapter(Context context, Cursor c, int flags) {
public SubkeysAdapter(Context context, Cursor c, int flags,
SaveKeyringParcel saveKeyringParcel) {
super(context, c, flags);
mInflater = LayoutInflater.from(context);
mSaveKeyringParcel = saveKeyringParcel;
}
public SubkeysAdapter(Context context, Cursor c, int flags) {
this(context, c, flags, null);
}
public long getKeyId(int position) {
mCursor.moveToPosition(position);
return mCursor.getLong(INDEX_KEY_ID);
}
@Override
@ -94,79 +106,94 @@ public class SubkeysAdapter extends CursorAdapter {
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView keyId = (TextView) view.findViewById(R.id.keyId);
TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
TextView keyExpiry = (TextView) view.findViewById(R.id.keyExpiry);
ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey);
ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
ImageView revokedKeyIcon = (ImageView) view.findViewById(R.id.ic_revokedKey);
TextView vKeyId = (TextView) view.findViewById(R.id.keyId);
TextView vKeyDetails = (TextView) view.findViewById(R.id.keyDetails);
TextView vKeyExpiry = (TextView) view.findViewById(R.id.keyExpiry);
ImageView vMasterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey);
ImageView vCertifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
ImageView vEncryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
ImageView vSignIcon = (ImageView) view.findViewById(R.id.ic_signKey);
ImageView vRevokedKeyIcon = (ImageView) view.findViewById(R.id.ic_revokedKey);
ImageView vEditImage = (ImageView) view.findViewById(R.id.edit_image);
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(INDEX_KEY_ID));
long keyId = cursor.getLong(INDEX_KEY_ID);
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId);
String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
context,
cursor.getInt(INDEX_ALGORITHM),
cursor.getInt(INDEX_KEY_SIZE)
);
keyId.setText(keyIdStr);
vKeyId.setText(keyIdStr);
// may be set with additional "stripped" later on
if (hasAnySecret && cursor.getInt(INDEX_HAS_SECRET) == 0) {
keyDetails.setText(algorithmStr + ", " +
vKeyDetails.setText(algorithmStr + ", " +
context.getString(R.string.key_stripped));
} else {
keyDetails.setText(algorithmStr);
vKeyDetails.setText(algorithmStr);
}
// Set icons according to properties
masterKeyIcon.setVisibility(cursor.getInt(INDEX_RANK) == 0 ? View.VISIBLE : View.INVISIBLE);
certifyIcon.setVisibility(cursor.getInt(INDEX_CAN_CERTIFY) != 0 ? View.VISIBLE : View.GONE);
encryptIcon.setVisibility(cursor.getInt(INDEX_CAN_ENCRYPT) != 0 ? View.VISIBLE : View.GONE);
signIcon.setVisibility(cursor.getInt(INDEX_CAN_SIGN) != 0 ? View.VISIBLE : View.GONE);
vMasterKeyIcon.setVisibility(cursor.getInt(INDEX_RANK) == 0 ? View.VISIBLE : View.INVISIBLE);
vCertifyIcon.setVisibility(cursor.getInt(INDEX_CAN_CERTIFY) != 0 ? View.VISIBLE : View.GONE);
vEncryptIcon.setVisibility(cursor.getInt(INDEX_CAN_ENCRYPT) != 0 ? View.VISIBLE : View.GONE);
vSignIcon.setVisibility(cursor.getInt(INDEX_CAN_SIGN) != 0 ? View.VISIBLE : View.GONE);
boolean valid = true;
if (cursor.getInt(INDEX_IS_REVOKED) > 0) {
revokedKeyIcon.setVisibility(View.VISIBLE);
boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
valid = false;
// for edit key
if (mSaveKeyringParcel != null) {
boolean revokeThisSubkey = (mSaveKeyringParcel.mRevokeSubKeys.contains(keyId));
if (revokeThisSubkey) {
if (!isRevoked) {
isRevoked = true;
}
}
vEditImage.setVisibility(View.VISIBLE);
} else {
keyId.setTextColor(mDefaultTextColor);
keyDetails.setTextColor(mDefaultTextColor);
keyExpiry.setTextColor(mDefaultTextColor);
revokedKeyIcon.setVisibility(View.GONE);
vEditImage.setVisibility(View.GONE);
}
if (isRevoked) {
vRevokedKeyIcon.setVisibility(View.VISIBLE);
} else {
vKeyId.setTextColor(mDefaultTextColor);
vKeyDetails.setTextColor(mDefaultTextColor);
vKeyExpiry.setTextColor(mDefaultTextColor);
vRevokedKeyIcon.setVisibility(View.GONE);
}
boolean isExpired;
if (!cursor.isNull(INDEX_EXPIRY)) {
Date expiryDate = new Date(cursor.getLong(INDEX_EXPIRY) * 1000);
isExpired = expiryDate.before(new Date());
valid = valid && expiryDate.after(new Date());
keyExpiry.setText(
context.getString(R.string.label_expiry) + ": " +
DateFormat.getDateFormat(context).format(expiryDate)
);
vKeyExpiry.setText(context.getString(R.string.label_expiry) + ": "
+ DateFormat.getDateFormat(context).format(expiryDate));
} else {
keyExpiry.setText(
context.getString(R.string.label_expiry) + ": " +
context.getString(R.string.none)
);
isExpired = false;
vKeyExpiry.setText(context.getString(R.string.label_expiry) + ": " + context.getString(R.string.none));
}
// if key is expired or revoked, strike through text
if (!valid) {
keyId.setText(OtherHelper.strikeOutText(keyId.getText()));
keyDetails.setText(OtherHelper.strikeOutText(keyDetails.getText()));
keyExpiry.setText(OtherHelper.strikeOutText(keyExpiry.getText()));
boolean isInvalid = isRevoked || isExpired;
if (isInvalid) {
vKeyId.setText(OtherHelper.strikeOutText(vKeyId.getText()));
vKeyDetails.setText(OtherHelper.strikeOutText(vKeyDetails.getText()));
vKeyExpiry.setText(OtherHelper.strikeOutText(vKeyExpiry.getText()));
}
keyId.setEnabled(valid);
keyDetails.setEnabled(valid);
keyExpiry.setEnabled(valid);
vKeyId.setEnabled(!isInvalid);
vKeyDetails.setEnabled(!isInvalid);
vKeyExpiry.setEnabled(!isInvalid);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = mInflater.inflate(R.layout.view_key_keys_item, null);
View view = mInflater.inflate(R.layout.view_key_subkey_item, null);
if (mDefaultTextColor == null) {
TextView keyId = (TextView) view.findViewById(R.id.keyId);
mDefaultTextColor = keyId.getTextColors();
@ -177,13 +204,21 @@ public class SubkeysAdapter extends CursorAdapter {
// Disable selection of items, http://stackoverflow.com/a/4075045
@Override
public boolean areAllItemsEnabled() {
return false;
if (mSaveKeyringParcel == null) {
return false;
} else {
return super.areAllItemsEnabled();
}
}
// Disable selection of items, http://stackoverflow.com/a/4075045
@Override
public boolean isEnabled(int position) {
return false;
if (mSaveKeyringParcel == null) {
return false;
} else {
return super.isEnabled(position);
}
}
}

View file

@ -40,9 +40,7 @@ import java.util.ArrayList;
public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemClickListener {
private LayoutInflater mInflater;
private final ArrayList<Boolean> mCheckStates;
private SaveKeyringParcel mSaveKeyringParcel;
public static final String[] USER_IDS_PROJECTION = new String[]{
@ -60,7 +58,6 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
private static final int INDEX_IS_PRIMARY = 4;
private static final int INDEX_IS_REVOKED = 5;
public UserIdsAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes,
SaveKeyringParcel saveKeyringParcel) {
super(context, c, flags);
@ -139,10 +136,13 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
&& mSaveKeyringParcel.mChangePrimaryUserId.equals(userId));
boolean revokeThisUserId = (mSaveKeyringParcel.mRevokeUserIds.contains(userId));
// only if primary user id will be changed
// (this is not triggered if the user id is currently the primary one)
if (changeAnyPrimaryUserId) {
// change all user ids, only this one should be primary
// change _all_ primary user ids and set new one to true
isPrimary = changeThisPrimaryUserId;
}
if (revokeThisUserId) {
if (!isRevoked) {
isRevoked = true;
@ -233,7 +233,7 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = mInflater.inflate(R.layout.view_key_userids_item, null);
View view = mInflater.inflate(R.layout.view_key_user_id_item, null);
// only need to do this once ever, since mShowCheckBoxes is final
view.findViewById(R.id.checkBox).setVisibility(mCheckStates != null ? View.VISIBLE : View.GONE);
return view;

View file

@ -0,0 +1,110 @@
/*
* 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.dialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.DialogFragment;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.Log;
public class EditSubkeyDialogFragment extends DialogFragment {
private static final String ARG_MESSENGER = "messenger";
public static final int MESSAGE_CHANGE_EXPIRY = 1;
public static final int MESSAGE_REVOKE = 2;
private Messenger mMessenger;
/**
* Creates new instance of this dialog fragment
*/
public static EditSubkeyDialogFragment newInstance(Messenger messenger) {
EditSubkeyDialogFragment frag = new EditSubkeyDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_MESSENGER, messenger);
frag.setArguments(args);
return frag;
}
/**
* Creates dialog
*/
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(getActivity());
CharSequence[] array = getResources().getStringArray(R.array.edit_key_edit_subkey);
builder.setTitle(R.string.edit_key_edit_subkey_title);
builder.setItems(array, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0:
sendMessageToHandler(MESSAGE_CHANGE_EXPIRY, null);
break;
case 1:
sendMessageToHandler(MESSAGE_REVOKE, null);
break;
default:
break;
}
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
dismiss();
}
});
return builder.show();
}
/**
* 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);
}
}
}

View file

@ -1,10 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="horizontal"
android:paddingRight="3dip"
android:singleLine="true">
<ImageView
@ -13,28 +12,44 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="@drawable/key_small"
android:layout_marginLeft="8dp" />
android:padding="8dp"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/edit_image"
android:src="@drawable/ic_action_edit"
android:padding="8dp"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_toRightOf="@id/ic_masterKey"
android:layout_toLeftOf="@id/edit_image"
android:layout_centerVertical="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp">
android:layout_marginRight="8dp"
android:id="@+id/linearLayout">
<LinearLayout
android:layout_width="fill_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="horizontal"
android:paddingBottom="2dip"
android:paddingTop="2dip">
<TextView
android:id="@+id/keyId"
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/label_key_id"
android:text="0x00000000"
android:textAppearance="?android:attr/textAppearanceMedium"
android:typeface="monospace"
android:layout_weight="1" />
@ -75,8 +90,8 @@
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/keyDetails"
@ -94,8 +109,9 @@
android:text="Expiry: 4/7/2016"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_gravity="right" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>

View file

@ -63,7 +63,6 @@
</LinearLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"

View file

@ -480,6 +480,11 @@
<item>Change to Primary Identity</item>
<item>Revoke Identity</item>
</string-array>
<string name="edit_key_edit_subkey_title">Select an action!</string>
<string-array name="edit_key_edit_subkey">
<item>Change Expiry</item>
<item>Revoke Subkey</item>
</string-array>
<!-- Navigation Drawer -->
<string name="nav_keys">Keys</string>