diff --git a/.gitmodules b/.gitmodules index f8db64f0a..8238c3f80 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,6 @@ [submodule "extern/StickyListHeaders"] path = extern/StickyListHeaders url = https://github.com/open-keychain/StickyListHeaders.git -[submodule "extern/zxing-android-integration"] - path = extern/zxing-android-integration - url = https://github.com/open-keychain/zxing-android-integration.git -[submodule "extern/zxing-qr-code"] - path = extern/zxing-qr-code - url = https://github.com/open-keychain/zxing-qr-code.git [submodule "extern/spongycastle"] path = extern/spongycastle url = https://github.com/open-keychain/spongycastle.git @@ -19,9 +13,6 @@ [submodule "extern/openkeychain-api-lib"] path = extern/openkeychain-api-lib url = https://github.com/open-keychain/openkeychain-api-lib.git -[submodule "extern/SuperToasts"] - path = extern/SuperToasts - url = https://github.com/open-keychain/SuperToasts.git [submodule "extern/KeybaseLib"] path = extern/KeybaseLib url = https://github.com/open-keychain/KeybaseLib.git diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle index 7fa139c06..4567f54dc 100644 --- a/OpenKeychain/build.gradle +++ b/OpenKeychain/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.application' +apply plugin: 'witness' dependencies { // NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information @@ -15,13 +16,12 @@ dependencies { compile project(':extern:spongycastle:pg') compile project(':extern:spongycastle:pkix') compile project(':extern:spongycastle:prov') - compile project(':extern:SuperToasts:supertoasts') compile project(':extern:minidns') compile project(':extern:KeybaseLib:Lib') compile project(':extern:TokenAutoComplete:library') compile project(':extern:safeslinger-exchange') - // TODO: Pin! + // NOTE: libraries are pinned to a specific build, see below compile 'com.eftimoff:android-patternview:1.0.0@aar' compile 'com.journeyapps:zxing-android-embedded:2.0.1@aar' compile 'com.journeyapps:zxing-android-integration:2.0.1@aar' @@ -32,9 +32,42 @@ dependencies { compile 'com.getbase:floatingactionbutton:1.8.0' } +// Output of ./gradlew -q calculateChecksums +// Comment out the libs referenced as git submodules! +dependencyVerification { + verify = [ + 'com.android.support:support-v4:703572d3015a088cc5604b7e38885af3d307c829d0c5ceaf8654ff41c71cd160', + 'com.android.support:appcompat-v7:5dbeb5316d0a6027d646ae552804c3baa5e3bd53f7f33db50904d51505c8a0e5', + 'com.android.support:recyclerview-v7:e525ad3f33c84bb12b73d2dc975b55364a53f0f2d0697e043efba59ba73e22d2', + 'com.android.support:cardview-v7:45c48c2ab056bc7a8573970b10f8902742c5d443f180dae43c56557397ac39af', + //'OpenKeychain.extern:openpgp-api-lib:b17bb282321351e4b00b4cd6422a57aadc13decae264019a88707bcb556439ea', + //'OpenKeychain.extern:openkeychain-api-lib:5f95f01c066069d4bde68992fd8da5faac21510d009b1fdae7a2e28e43e82cf4', + //'OpenKeychain.extern:html-textview:b58e343cf4c145e91f888806d06a2a7770a9e9331a72f08cfcf1128db30dcff3', + //'OpenKeychain.extern.StickyListHeaders:library:24e25da422efc08e4e7a06efbe927fdf17f7a9aa722db2b983385e2bf0004da5', + //'com.madgag.spongycastle:core:a9e4f60afe6b2661e0713190ade92c099b3f74ebbc67c1bc3e3fced0144307f4', + //'com.madgag.spongycastle:pg:29d544ff289fcaafcf6c3904185f5a6fbdb623cf1a1e377fcb239edc31ee9c17', + //'com.madgag.spongycastle:pkix:950d6eac8205c6a24aa87066fbf9cd0af50b95858b8d2b18d53e2fada2dbb2e3', + //'com.madgag.spongycastle:prov:0b78ffd7a59b1b690a05ebe9bb31d43405046a44a18e0529d7c826acb56350b7', + //'OpenKeychain.extern:minidns:cf332e993d7fcdc0a3821f5b997944df40582dc6c9f0ea36b5e20c1e289cb19f', + //'OpenKeychain.extern.KeybaseLib:Lib:af9bff087148e0859430d0b99ece096c41b315c5dc1ed500a68580b9b0e5ab11', + //'OpenKeychain.extern.TokenAutoComplete:library:40d4212a95e947efdb02f2ca66c95a27d49fba848471a6317eca2b9cc18e8780', + //'OpenKeychain.extern:safeslinger-exchange:94a1ce68217af7499579a042758283b1530912c53241bdfa06d1a079a5ae3faf', + 'com.eftimoff:android-patternview:a031eaed3b5cef8ea06c2d4a6e27693937f89ae483598d61b7027eeee0bed408', + 'com.journeyapps:zxing-android-embedded:5d6ba3931bd0b999695e363b571e95bd6bc9956340c1e6ce740cd0bff3d89a50', + 'com.journeyapps:zxing-android-integration:6f50bb07c057ac94319777ddfbb66f5d4f6190393418b2fc861e0e60d06f3c0d', + 'com.google.zxing:core:38c49045765281e4c170062fa3f48e4e988629bf985cab850c7497be5eaa72a1', + 'com.jpardogo.materialtabstrip:library:c6ef812fba4f74be7dc4a905faa4c2908cba261a94c13d4f96d5e67e4aad4aaa', + 'it.neokree:MaterialNavigationDrawer:1174d751a54689fccf53c1fbcdf439745926ae19024f4f1017afb6b29643c57d', + 'com.nispok:snackbar:59dc092a44c877e9ce5f9040c632d99e62d8932b0a4d67ba0ec9e35467d9047c', + 'com.getbase:floatingactionbutton:e63966148212e9685afad2370780ea239b6dbd2a06f6a3f919b98882318e6a32', + 'com.android.support:support-annotations:fdee2354787ef66b268e75958de3f7f6c4f8f325510a6dac9f49c929f83a63de', + 'com.balysv:material-ripple:587f19c1e27f16c7dc67ff9ac73838aa1451086ef05a15cee38bee3e4e1454ae', + ] +} + android { - compileSdkVersion 21 - buildToolsVersion '21.1.2' + compileSdkVersion rootProject.ext.compileSdkVersion + buildToolsVersion rootProject.ext.buildToolsVersion defaultConfig { minSdkVersion 15 diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/LinkedVerifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/LinkedVerifyResult.java index 2dae38cc4..42e9ec3f0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/LinkedVerifyResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/LinkedVerifyResult.java @@ -18,21 +18,7 @@ package org.sufficientlysecure.keychain.operations.results; -import android.app.Activity; -import android.content.Intent; import android.os.Parcel; -import android.os.Parcelable; -import android.view.View; - -import com.github.johnpersano.supertoasts.SuperCardToast; -import com.github.johnpersano.supertoasts.SuperToast; -import com.github.johnpersano.supertoasts.SuperToast.Duration; -import com.github.johnpersano.supertoasts.util.OnClickWrapper; -import com.github.johnpersano.supertoasts.util.Style; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.LogDisplayActivity; -import org.sufficientlysecure.keychain.ui.LogDisplayFragment; public class LinkedVerifyResult extends OperationResult { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 742cde75c..0be6c26f6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -803,6 +803,7 @@ public class ViewKeyActivity extends BaseActivity implements mName.setText(R.string.user_id_no_name); } + String oldFingerprint = mFingerprint; mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID); mFingerprint = KeyFormattingUtils.convertFingerprintToHex(data.getBlob(INDEX_FINGERPRINT)); @@ -866,8 +867,11 @@ public class ViewKeyActivity extends BaseActivity implements mStatusText.setText(R.string.view_key_my_key); mStatusImage.setVisibility(View.GONE); color = getResources().getColor(R.color.primary); + // reload qr code only if the fingerprint changed + if ( !mFingerprint.equals(oldFingerprint)) { + loadQrCode(mFingerprint); + } photoTask.execute(mFingerprint); - loadQrCode(mFingerprint); mQrCodeLayout.setVisibility(View.VISIBLE); // and place leftOf qr code diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index 453bfd499..32630b459 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -34,14 +34,10 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; -import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; import org.sufficientlysecure.keychain.util.Log; -import java.util.Date; - public class ViewKeyFragment extends LoaderFragment implements LoaderManager.LoaderCallbacks { @@ -58,8 +54,6 @@ public class ViewKeyFragment extends LoaderFragment implements private Uri mDataUri; - ProviderHelper mProviderHelper; - /** * Creates new instance of this fragment */ @@ -78,8 +72,6 @@ public class ViewKeyFragment extends LoaderFragment implements View root = super.onCreateView(inflater, superContainer, savedInstanceState); View view = inflater.inflate(R.layout.view_key_fragment, getContainer()); - mProviderHelper = new ProviderHelper(getActivity()); - mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids); mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() { @@ -148,16 +140,14 @@ public class ViewKeyFragment extends LoaderFragment implements private void loadData(Uri dataUri) { mDataUri = dataUri; - Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); + Log.i(Constants.TAG, "mDataUri: " + mDataUri); // Prepare the loaders. Either re-connect with an existing ones, // or start new ones. + // TODO Is this loader the same as the one in the activity? getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); } - // don't show revoked user ids here, irrelevant for average users - public static final String USER_IDS_WHERE = UserPackets.IS_REVOKED + " = 0"; - public Loader onCreateLoader(int id, Bundle args) { setContentShown(false); @@ -166,11 +156,8 @@ public class ViewKeyFragment extends LoaderFragment implements Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri); return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null); } - case LOADER_ID_USER_IDS: { - Uri baseUri = UserPackets.buildUserIdsUri(mDataUri); - return new CursorLoader(getActivity(), baseUri, - UserIdsAdapter.USER_IDS_PROJECTION, USER_IDS_WHERE, null, null); - } + case LOADER_ID_USER_IDS: + return UserIdsAdapter.createLoader(getActivity(), mDataUri); default: return null; @@ -192,14 +179,9 @@ public class ViewKeyFragment extends LoaderFragment implements if (data.moveToFirst()) { mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0; - boolean hasEncrypt = data.getInt(INDEX_HAS_ENCRYPT) != 0; - boolean isRevoked = data.getInt(INDEX_IS_REVOKED) > 0; - boolean isExpired = !data.isNull(INDEX_EXPIRY) - && new Date(data.getLong(INDEX_EXPIRY) * 1000).before(new Date()); - boolean isVerified = data.getInt(INDEX_VERIFIED) > 0; // load user ids after we know if it's a secret key - mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0, false, !mIsSecret, null); + mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0, !mIsSecret, null); mUserIds.setAdapter(mUserIdsAdapter); getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java index 36ee8ba88..457083770 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java @@ -1,234 +1,36 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * 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 . - */ - package org.sufficientlysecure.keychain.ui.adapter; import android.content.Context; import android.database.Cursor; -import android.graphics.Typeface; import android.support.v4.widget.CursorAdapter; -import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.ImageView; -import android.widget.TextView; -import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; -import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; -import org.sufficientlysecure.keychain.service.SaveKeyringParcel; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import java.util.ArrayList; - -public abstract class UserAttributesAdapter extends CursorAdapter implements AdapterView.OnItemClickListener { - private LayoutInflater mInflater; - private final ArrayList mCheckStates; - private SaveKeyringParcel mSaveKeyringParcel; - private boolean mShowStatusImages; - - public static final String[] USER_PACKETS_PROJECTION = new String[]{ +public abstract class UserAttributesAdapter extends CursorAdapter { + public static final String[] USER_IDS_PROJECTION = new String[]{ UserPackets._ID, UserPackets.TYPE, UserPackets.USER_ID, - UserPackets.ATTRIBUTE_DATA, UserPackets.RANK, UserPackets.VERIFIED, UserPackets.IS_PRIMARY, UserPackets.IS_REVOKED }; - private static final int INDEX_ID = 0; - private static final int INDEX_TYPE = 1; - private static final int INDEX_USER_ID = 2; - private static final int INDEX_ATTRIBUTE_DATA = 3; - private static final int INDEX_RANK = 4; - private static final int INDEX_VERIFIED = 5; - private static final int INDEX_IS_PRIMARY = 6; - private static final int INDEX_IS_REVOKED = 7; + protected static final int INDEX_ID = 0; + protected static final int INDEX_TYPE = 1; + protected static final int INDEX_USER_ID = 2; + protected static final int INDEX_RANK = 3; + protected static final int INDEX_VERIFIED = 4; + protected static final int INDEX_IS_PRIMARY = 5; + protected static final int INDEX_IS_REVOKED = 6; - public UserAttributesAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes, - boolean showStatusImages, SaveKeyringParcel saveKeyringParcel) { + public UserAttributesAdapter(Context context, Cursor c, int flags) { super(context, c, flags); - mInflater = LayoutInflater.from(context); - - mCheckStates = showCheckBoxes ? new ArrayList() : null; - mSaveKeyringParcel = saveKeyringParcel; - mShowStatusImages = showStatusImages; } @Override - public Cursor swapCursor(Cursor newCursor) { - if (mCheckStates != null) { - mCheckStates.clear(); - if (newCursor != null) { - int count = newCursor.getCount(); - mCheckStates.ensureCapacity(count); - // initialize to true (use case knowledge: we usually want to sign all uids) - for (int i = 0; i < count; i++) { - newCursor.moveToPosition(i); - int verified = newCursor.getInt(INDEX_VERIFIED); - mCheckStates.add(verified != Certs.VERIFIED_SECRET); - } - } - } - - return super.swapCursor(newCursor); - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - TextView vName = (TextView) view.findViewById(R.id.user_id_item_name); - TextView vAddress = (TextView) view.findViewById(R.id.user_id_item_address); - TextView vComment = (TextView) view.findViewById(R.id.user_id_item_comment); - ImageView vVerified = (ImageView) view.findViewById(R.id.user_id_item_certified); - View vVerifiedLayout = view.findViewById(R.id.user_id_item_certified_layout); - ImageView vEditImage = (ImageView) view.findViewById(R.id.user_id_item_edit_image); - ImageView vDeleteButton = (ImageView) view.findViewById(R.id.user_id_item_delete_button); - vDeleteButton.setVisibility(View.GONE); // not used - - String userId = cursor.getString(INDEX_USER_ID); - String[] splitUserId = KeyRing.splitUserId(userId); - if (splitUserId[0] != null) { - vName.setText(splitUserId[0]); - } else { - vName.setText(R.string.user_id_no_name); - } - if (splitUserId[1] != null) { - vAddress.setText(splitUserId[1]); - vAddress.setVisibility(View.VISIBLE); - } else { - vAddress.setVisibility(View.GONE); - } - if (splitUserId[2] != null) { - vComment.setText(splitUserId[2]); - vComment.setVisibility(View.VISIBLE); - } else { - vComment.setVisibility(View.GONE); - } - - boolean isPrimary = cursor.getInt(INDEX_IS_PRIMARY) != 0; - boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; - - // for edit key - if (mSaveKeyringParcel != null) { - boolean changeAnyPrimaryUserId = (mSaveKeyringParcel.mChangePrimaryUserId != null); - boolean changeThisPrimaryUserId = (mSaveKeyringParcel.mChangePrimaryUserId != null - && 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_ primary user ids and set new one to true - isPrimary = changeThisPrimaryUserId; - } - - if (revokeThisUserId) { - if (!isRevoked) { - isRevoked = true; - } - } - - vEditImage.setVisibility(View.VISIBLE); - vVerifiedLayout.setVisibility(View.GONE); - } else { - vEditImage.setVisibility(View.GONE); - - if (mShowStatusImages) { - vVerifiedLayout.setVisibility(View.VISIBLE); - } else { - vVerifiedLayout.setVisibility(View.GONE); - } - } - - if (isRevoked) { - // set revocation icon (can this even be primary?) - KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray); - - // disable revoked user ids - vName.setEnabled(false); - vAddress.setEnabled(false); - vComment.setEnabled(false); - } else { - vName.setEnabled(true); - vAddress.setEnabled(true); - vComment.setEnabled(true); - - if (isPrimary) { - vName.setTypeface(null, Typeface.BOLD); - vAddress.setTypeface(null, Typeface.BOLD); - } else { - vName.setTypeface(null, Typeface.NORMAL); - vAddress.setTypeface(null, Typeface.NORMAL); - } - - int isVerified = cursor.getInt(INDEX_VERIFIED); - switch (isVerified) { - case Certs.VERIFIED_SECRET: - KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_VERIFIED, KeyFormattingUtils.DEFAULT_COLOR); - break; - case Certs.VERIFIED_SELF: - KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR); - break; - default: - KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_INVALID, KeyFormattingUtils.DEFAULT_COLOR); - break; - } - } - - // don't care further if checkboxes aren't shown - if (mCheckStates == null) { - return; - } - - final CheckBox vCheckBox = (CheckBox) view.findViewById(R.id.user_id_item_check_box); - final int position = cursor.getPosition(); - vCheckBox.setOnCheckedChangeListener(null); - vCheckBox.setChecked(mCheckStates.get(position)); - vCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean b) { - mCheckStates.set(position, b); - } - }); - vCheckBox.setClickable(false); - } - - public void onItemClick(AdapterView adapter, View view, int position, long id) { - CheckBox box = ((CheckBox) view.findViewById(R.id.user_id_item_check_box)); - if (box != null) { - box.toggle(); - } - } - - public ArrayList getSelectedUserIds() { - ArrayList result = new ArrayList<>(); - for (int i = 0; i < mCheckStates.size(); i++) { - if (mCheckStates.get(i)) { - mCursor.moveToPosition(i); - result.add(mCursor.getString(INDEX_USER_ID)); - } - } - return result; - } + public abstract void bindView(View view, Context context, Cursor cursor); public String getUserId(int position) { mCursor.moveToPosition(position); @@ -244,28 +46,4 @@ public abstract class UserAttributesAdapter extends CursorAdapter implements Ada mCursor.moveToPosition(position); return mCursor.getInt(INDEX_VERIFIED); } - - public boolean getIsRevokedPending(int position) { - mCursor.moveToPosition(position); - String userId = mCursor.getString(INDEX_USER_ID); - - boolean isRevokedPending = false; - if (mSaveKeyringParcel != null) { - if (mSaveKeyringParcel.mRevokeUserIds.contains(userId)) { - isRevokedPending = true; - } - - } - - return isRevokedPending; - } - - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - View view = mInflater.inflate(R.layout.view_key_adv_user_id_item, null); - // only need to do this once ever, since mShowCheckBoxes is final - view.findViewById(R.id.user_id_item_check_box).setVisibility(mCheckStates != null ? View.VISIBLE : View.GONE); - return view; - } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java new file mode 100644 index 000000000..6a4f61f4b --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2014-2015 Dominik Schürmann + * Copyright (C) 2015 Vincent Breitmoser + * + * 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 . + */ + +package org.sufficientlysecure.keychain.ui.adapter; + +import android.app.Activity; +import android.content.Context; +import android.database.Cursor; +import android.graphics.Typeface; +import android.net.Uri; +import android.support.v4.content.CursorLoader; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; + +public class UserIdsAdapter extends UserAttributesAdapter { + protected LayoutInflater mInflater; + private SaveKeyringParcel mSaveKeyringParcel; + private boolean mShowStatusImages; + + public UserIdsAdapter(Context context, Cursor c, int flags, + boolean showStatusImages, SaveKeyringParcel saveKeyringParcel) { + super(context, c, flags); + mInflater = LayoutInflater.from(context); + + mSaveKeyringParcel = saveKeyringParcel; + mShowStatusImages = showStatusImages; + } + + public UserIdsAdapter(Context context, Cursor c, int flags, SaveKeyringParcel saveKeyringParcel) { + this(context, c, flags, true, saveKeyringParcel); + } + + public UserIdsAdapter(Context context, Cursor c, int flags) { + this(context, c, flags, true, null); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + TextView vName = (TextView) view.findViewById(R.id.user_id_item_name); + TextView vAddress = (TextView) view.findViewById(R.id.user_id_item_address); + TextView vComment = (TextView) view.findViewById(R.id.user_id_item_comment); + ImageView vVerified = (ImageView) view.findViewById(R.id.user_id_item_certified); + View vVerifiedLayout = view.findViewById(R.id.user_id_item_certified_layout); + ImageView vEditImage = (ImageView) view.findViewById(R.id.user_id_item_edit_image); + ImageView vDeleteButton = (ImageView) view.findViewById(R.id.user_id_item_delete_button); + vDeleteButton.setVisibility(View.GONE); // not used + + String userId = cursor.getString(INDEX_USER_ID); + String[] splitUserId = KeyRing.splitUserId(userId); + if (splitUserId[0] != null) { + vName.setText(splitUserId[0]); + } else { + vName.setText(R.string.user_id_no_name); + } + if (splitUserId[1] != null) { + vAddress.setText(splitUserId[1]); + vAddress.setVisibility(View.VISIBLE); + } else { + vAddress.setVisibility(View.GONE); + } + if (splitUserId[2] != null) { + vComment.setText(splitUserId[2]); + vComment.setVisibility(View.VISIBLE); + } else { + vComment.setVisibility(View.GONE); + } + + boolean isPrimary = cursor.getInt(INDEX_IS_PRIMARY) != 0; + boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; + + // for edit key + if (mSaveKeyringParcel != null) { + boolean changeAnyPrimaryUserId = (mSaveKeyringParcel.mChangePrimaryUserId != null); + boolean changeThisPrimaryUserId = (mSaveKeyringParcel.mChangePrimaryUserId != null + && 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_ primary user ids and set new one to true + isPrimary = changeThisPrimaryUserId; + } + + if (revokeThisUserId) { + if (!isRevoked) { + isRevoked = true; + } + } + + vEditImage.setVisibility(View.VISIBLE); + vVerifiedLayout.setVisibility(View.GONE); + } else { + vEditImage.setVisibility(View.GONE); + + if (mShowStatusImages) { + vVerifiedLayout.setVisibility(View.VISIBLE); + } else { + vVerifiedLayout.setVisibility(View.GONE); + } + } + + if (isRevoked) { + // set revocation icon (can this even be primary?) + KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray); + + // disable revoked user ids + vName.setEnabled(false); + vAddress.setEnabled(false); + vComment.setEnabled(false); + } else { + vName.setEnabled(true); + vAddress.setEnabled(true); + vComment.setEnabled(true); + + if (isPrimary) { + vName.setTypeface(null, Typeface.BOLD); + vAddress.setTypeface(null, Typeface.BOLD); + } else { + vName.setTypeface(null, Typeface.NORMAL); + vAddress.setTypeface(null, Typeface.NORMAL); + } + + int isVerified = cursor.getInt(INDEX_VERIFIED); + switch (isVerified) { + case Certs.VERIFIED_SECRET: + KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_VERIFIED, KeyFormattingUtils.DEFAULT_COLOR); + break; + case Certs.VERIFIED_SELF: + KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR); + break; + default: + KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_INVALID, KeyFormattingUtils.DEFAULT_COLOR); + break; + } + } + } + + public boolean getIsRevokedPending(int position) { + mCursor.moveToPosition(position); + String userId = mCursor.getString(INDEX_USER_ID); + + boolean isRevokedPending = false; + if (mSaveKeyringParcel != null) { + if (mSaveKeyringParcel.mRevokeUserIds.contains(userId)) { + isRevokedPending = true; + } + + } + + return isRevokedPending; + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return mInflater.inflate(R.layout.view_key_adv_user_id_item, null); + } + + // don't show revoked user ids, irrelevant for average users + public static final String USER_IDS_WHERE = UserPackets.IS_REVOKED + " = 0"; + + public static CursorLoader createLoader(Activity activity, Uri dataUri) { + Uri baseUri = UserPackets.buildUserIdsUri(dataUri); + return new CursorLoader(activity, baseUri, + UserIdsAdapter.USER_IDS_PROJECTION, USER_IDS_WHERE, null, null); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsSelectableAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsSelectableAdapter.java new file mode 100644 index 000000000..947d911c3 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsSelectableAdapter.java @@ -0,0 +1,88 @@ +package org.sufficientlysecure.keychain.ui.adapter; + +import android.content.Context; +import android.database.Cursor; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.CheckBox; +import android.widget.CompoundButton; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; + +import java.util.ArrayList; + +public class UserIdsSelectableAdapter extends UserIdsAdapter implements AdapterView.OnItemClickListener { + + private final ArrayList mCheckStates; + + public UserIdsSelectableAdapter(Context context, Cursor c, int flags, SaveKeyringParcel saveKeyringParcel) { + super(context, c, flags, saveKeyringParcel); + + mCheckStates = new ArrayList(); + } + + @Override + public Cursor swapCursor(Cursor newCursor) { + if (mCheckStates != null) { + mCheckStates.clear(); + if (newCursor != null) { + int count = newCursor.getCount(); + mCheckStates.ensureCapacity(count); + // initialize to true (use case knowledge: we usually want to sign all uids) + for (int i = 0; i < count; i++) { + newCursor.moveToPosition(i); + int verified = newCursor.getInt(INDEX_VERIFIED); + mCheckStates.add(verified != Certs.VERIFIED_SECRET); + } + } + } + + return super.swapCursor(newCursor); + } + + @Override + public void onItemClick(AdapterView adapter, View view, int position, long id) { + CheckBox box = ((CheckBox) view.findViewById(R.id.user_id_item_check_box)); + if (box != null) { + box.toggle(); + } + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + super.bindView(view, context, cursor); + + final CheckBox vCheckBox = (CheckBox) view.findViewById(R.id.user_id_item_check_box); + final int position = cursor.getPosition(); + vCheckBox.setOnCheckedChangeListener(null); + vCheckBox.setChecked(mCheckStates.get(position)); + vCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean b) { + mCheckStates.set(position, b); + } + }); + vCheckBox.setClickable(false); + } + + public ArrayList getSelectedUserIds() { + ArrayList result = new ArrayList<>(); + for (int i = 0; i < mCheckStates.size(); i++) { + if (mCheckStates.get(i)) { + mCursor.moveToPosition(i); + result.add(mCursor.getString(INDEX_USER_ID)); + } + } + return result; + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + View view = mInflater.inflate(R.layout.view_key_selectable_user_id_item, null); + return view; + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java index 60bc846b2..3cf201ed7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java @@ -57,10 +57,20 @@ public class ContactHelper { KeychainContract.KeyRings.MASTER_KEY_ID, KeychainContract.KeyRings.EXPIRY, KeychainContract.KeyRings.IS_REVOKED}; + + public static final int INDEX_USER_ID = 0; + public static final int INDEX_FINGERPRINT = 1; + public static final int INDEX_KEY_ID = 2; + public static final int INDEX_MASTER_KEY_ID = 3; + public static final int INDEX_EXPIRY = 4; + public static final int INDEX_IS_REVOKED = 5; + public static final String[] USER_IDS_PROJECTION = new String[]{ UserPackets.USER_ID }; + public static final int INDEX_USER_IDS_USER_ID = 0; + public static final String NON_REVOKED_SELECTION = UserPackets.IS_REVOKED + "=0"; public static final String[] ID_PROJECTION = new String[]{ContactsContract.RawContacts._ID}; @@ -301,23 +311,26 @@ public class ContactHelper { null, null, null); if (cursor != null) { while (cursor.moveToNext()) { - String[] primaryUserId = KeyRing.splitUserId(cursor.getString(0)); - String fingerprint = KeyFormattingUtils.convertFingerprintToHex(cursor.getBlob(1)); + String[] primaryUserId = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID)); + String fingerprint = KeyFormattingUtils.convertFingerprintToHex(cursor.getBlob(INDEX_FINGERPRINT)); contactFingerprints.remove(fingerprint); - String keyIdShort = KeyFormattingUtils.convertKeyIdToHexShort(cursor.getLong(2)); - long masterKeyId = cursor.getLong(3); - boolean isExpired = !cursor.isNull(4) && new Date(cursor.getLong(4) * 1000).before(new Date()); - boolean isRevoked = cursor.getInt(5) > 0; + String keyIdShort = KeyFormattingUtils.convertKeyIdToHexShort(cursor.getLong(INDEX_KEY_ID)); + long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID); + boolean isExpired = !cursor.isNull(INDEX_EXPIRY) + && new Date(cursor.getLong(INDEX_EXPIRY) * 1000).before(new Date()); + boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; int rawContactId = findRawContactId(resolver, fingerprint); ArrayList ops = new ArrayList<>(); + Log.d(Constants.TAG, "raw contact id: "+rawContactId); + // Do not store expired or revoked keys in contact db - and remove them if they already exist if (isExpired || isRevoked) { if (rawContactId != -1) { resolver.delete(ContactsContract.RawContacts.CONTENT_URI, ID_SELECTION, new String[]{Integer.toString(rawContactId)}); } - } else { + } else if (primaryUserId[0] != null) { // Create a new rawcontact with corresponding key if it does not exist yet if (rawContactId == -1) { diff --git a/OpenKeychain/src/main/res/layout/view_key_adv_user_id_item.xml b/OpenKeychain/src/main/res/layout/view_key_adv_user_id_item.xml index 24e8e1a10..63c1ee70f 100644 --- a/OpenKeychain/src/main/res/layout/view_key_adv_user_id_item.xml +++ b/OpenKeychain/src/main/res/layout/view_key_adv_user_id_item.xml @@ -6,13 +6,6 @@ android:orientation="horizontal" android:singleLine="true"> - - diff --git a/OpenKeychain/src/main/res/layout/view_key_selectable_user_id_item.xml b/OpenKeychain/src/main/res/layout/view_key_selectable_user_id_item.xml new file mode 100644 index 000000000..5d0edc916 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/view_key_selectable_user_id_item.xml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/OpenKeychain/src/main/res/values/colors.xml b/OpenKeychain/src/main/res/values/colors.xml index c49e06b4b..8c5358fd0 100644 --- a/OpenKeychain/src/main/res/values/colors.xml +++ b/OpenKeychain/src/main/res/values/colors.xml @@ -13,6 +13,8 @@ + #000000 + #C8E6C9 @color/accent #1976D2 diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index b9f892687..4260cac3f 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -360,8 +360,7 @@ "consolidate: reimporting…" - "Name/Email/Key ID…" - "Name/Email/Proof/Key…" + "Search via Name, Email…" "512" diff --git a/README.md b/README.md index ff045de01..77ae69262 100644 --- a/README.md +++ b/README.md @@ -106,37 +106,54 @@ see * Mailinglist Archive at http://bouncy-castle.1462172.n4.nabble.com/Bouncy-Castle-Dev-f1462173.html * Commit changelog of pg subpackage: https://github.com/bcgit/bc-java/commits/master/pg - -## Notes - -### Gradle Build System +## Build System We try to make our builds as [reproducible/deterministic](https://blog.torproject.org/blog/deterministic-builds-part-one-cyberwar-and-global-compromise) as possible. -When changing build files or dependencies, respect the following requirements: -* No precompiled libraries (you never know what pre-compiled jar files really contain!). All libraries should be forked into the open-keychain Github project and then provided as git submodules in the "extern" folder. -* No dependencies from Maven (also a soft requirement for inclusion in [F-Droid](https://f-droid.org)) + +#### Update Gradle version * Always use a fixed Android Gradle plugin version not a dynamic one, e.g. ``0.7.3`` instead of ``0.7.+`` (allows offline builds without lookups for new versions, also some minor Android plugin versions had serious issues, i.e. [0.7.2 and 0.8.1](http://tools.android.com/tech-docs/new-build-system)) -* Commit the corresponding [Gradle wrapper](http://www.gradle.org/docs/current/userguide/gradle_wrapper.html) to the repository (allows easy building for new contributors without the need to install the required Gradle version using a package manager) -* In order to update the build system to a newer gradle version you need to: - * Update every build.gradle file with the new gradle version and/or gradle plugin version +* Update every build.gradle file with the new gradle version and/or gradle plugin version * build.gradle * OpenKeychain/build.gradle - * OpenKeychain-API/build.gradle - * OpenKeychain-API/example-app/build.gradle - * OpenKeychain-API/libraries/keychain-api-library/build.gradle - * run ./gradlew wrapper twice to update gradle and download the new gradle jar file - * commit the new gradle jar and property files +* run ./gradlew wrapper twice to update gradle and download the new gradle jar file +* commit the corresponding [Gradle wrapper](http://www.gradle.org/docs/current/userguide/gradle_wrapper.html) to the repository (allows easy building for new contributors without the need to install the required Gradle version using a package manager) + +#### Update SDK and Build Tools +* Open build.gradle and change: +``` +ext { + compileSdkVersion = 21 + buildToolsVersion = '21.1.2' +} +``` +* Change SDK and Build Tools in git submodules "openkeychain-api-lib" and "openpgp-api-lib" manually. They should also build on their own without the ext variables. -### Slow Gradle? +#### Add new library +* You can add the library as a Maven dependency or as a git submodule (if patches are required) in the "extern" folder. +* If added as a Maven dependency, pin the library using [Gradle Witness](https://github.com/WhisperSystems/gradle-witness) (Do ``./gradlew -q calculateChecksums`` for Trust on First Use) +* If added as a git submodule, change the ``compileSdkVersion`` and ``buildToolsVersion`` in build.gradle to use the variables from the root project: +``` +android { + compileSdkVersion rootProject.ext.compileSdkVersion + buildToolsVersion rootProject.ext.buildToolsVersion +} +``` +* You can check for wrong ``compileSdkVersion`` by ``find -name build.gradle | xargs grep compileSdkVersion`` + +#### Slow Gradle? * https://www.timroes.de/2013/09/12/speed-up-gradle/ * Disable Lint checking if it is enabled in build.gradle -### Error:Configuration with name 'default' not found. +#### Error:Configuration with name 'default' not found. Gradle project dependencies are missing. Do a ``git submodule init && git submodule update`` -### Translations +#### Build on Mac OS X fails? + +Try exporting JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF8" + +## Translations Translations are hosted on Transifex, which is configured by ".tx/config". diff --git a/build.gradle b/build.gradle index f8e6afc27..6b3eb2c3d 100644 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,7 @@ buildscript { dependencies { // NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information classpath 'com.android.tools.build:gradle:1.0.0' + classpath files('gradle-witness.jar') } } @@ -38,3 +39,10 @@ project(':extern:spongycastle') { test.enabled = false } } + +// SDK Version and Build Tools used by all subprojects +// See http://tools.android.com/tech-docs/new-build-system/tips#TOC-Controlling-Android-properties-of-all-your-modules-from-the-main-project. +ext { + compileSdkVersion = 21 + buildToolsVersion = '21.1.2' +} \ No newline at end of file diff --git a/extern/KeybaseLib b/extern/KeybaseLib index 2b26d163d..9615d90b1 160000 --- a/extern/KeybaseLib +++ b/extern/KeybaseLib @@ -1 +1 @@ -Subproject commit 2b26d163df84a3d26c1c8da088ed3811b5ca6ec7 +Subproject commit 9615d90b18d1aee4dad994aa45875adfdcfb3c34 diff --git a/extern/StickyListHeaders b/extern/StickyListHeaders index 62cc58c12..70a2ed806 160000 --- a/extern/StickyListHeaders +++ b/extern/StickyListHeaders @@ -1 +1 @@ -Subproject commit 62cc58c12d0c09b50984caf26e5afceda8873784 +Subproject commit 70a2ed80632938540bf07b81270384f4e5a96f9e diff --git a/extern/SuperToasts b/extern/SuperToasts deleted file mode 160000 index 77042d633..000000000 --- a/extern/SuperToasts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 77042d633f4dd430bcc86101e31dda52433db9c1 diff --git a/extern/TokenAutoComplete b/extern/TokenAutoComplete index ca46b4261..1d6d3882e 160000 --- a/extern/TokenAutoComplete +++ b/extern/TokenAutoComplete @@ -1 +1 @@ -Subproject commit ca46b4261c97221ddd4db135e7838d6fa145adf4 +Subproject commit 1d6d3882e711c14c93abf73cbcbd28b9e0e2b498 diff --git a/extern/html-textview b/extern/html-textview index eedaa334e..9872a7315 160000 --- a/extern/html-textview +++ b/extern/html-textview @@ -1 +1 @@ -Subproject commit eedaa334e761273efbfc49ded2124df58c8a4d88 +Subproject commit 9872a73156518fd8df79dd2cd4d24750574b9ac7 diff --git a/extern/openkeychain-api-lib b/extern/openkeychain-api-lib index 88c004793..925907502 160000 --- a/extern/openkeychain-api-lib +++ b/extern/openkeychain-api-lib @@ -1 +1 @@ -Subproject commit 88c00479329c1aa892bef052f3f8830067c386d8 +Subproject commit 9259075028e0906d0cb085e0c4578e23829dc3c0 diff --git a/extern/safeslinger-exchange b/extern/safeslinger-exchange index 96f7c8935..7c84cb54d 160000 --- a/extern/safeslinger-exchange +++ b/extern/safeslinger-exchange @@ -1 +1 @@ -Subproject commit 96f7c893565e3a8badd740b2035beea87d8bffb3 +Subproject commit 7c84cb54df5b3d5c6780984e48f3e9e4cbc5128e diff --git a/extern/zxing-android-integration b/extern/zxing-android-integration deleted file mode 160000 index e2d0064bd..000000000 --- a/extern/zxing-android-integration +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e2d0064bd3171b7333af044bb30c25c85ee993dd diff --git a/extern/zxing-qr-code b/extern/zxing-qr-code deleted file mode 160000 index 8fd0657d3..000000000 --- a/extern/zxing-qr-code +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8fd0657d33d8277aadbdc1604fd3aaa2e8d4b487 diff --git a/gradle-witness.jar b/gradle-witness.jar new file mode 100644 index 000000000..90d2a364c Binary files /dev/null and b/gradle-witness.jar differ