From f7d7adc1ef12ee09cda80bac68a7719af8a9204a Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 24 Jul 2017 11:42:38 +0200 Subject: [PATCH 01/11] fix overridden warnings table creation (whoops) --- .../keychain/provider/KeychainDatabase.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 58266db08..da153a1c7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -197,6 +197,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { db.execSQL(CREATE_UPDATE_KEYS); db.execSQL(CREATE_API_APPS); db.execSQL(CREATE_API_APPS_ALLOWED_KEYS); + db.execSQL(CREATE_OVERRIDDEN_WARNINGS); db.execSQL("CREATE INDEX keys_by_rank ON keys (" + KeysColumns.RANK + ");"); db.execSQL("CREATE INDEX uids_by_rank ON user_packets (" + UserPacketsColumns.RANK + ", " @@ -307,7 +308,12 @@ public class KeychainDatabase extends SQLiteOpenHelper { } */ case 20: - db.execSQL(CREATE_OVERRIDDEN_WARNINGS); + db.execSQL( + "CREATE TABLE IF NOT EXISTS overridden_warnings (" + + "_id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "identifier TEXT NOT NULL UNIQUE " + + ")"); + if (oldVersion == 18 || oldVersion == 19 || oldVersion == 20) { // no consolidate for now, often crashes! return; From d75d400453ad186c127609fceabdc4a3c0276da3 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 20 May 2017 21:37:04 +0200 Subject: [PATCH 02/11] save whether key was seen on keyservers --- .../keychain/operations/ImportOperation.java | 7 ++++++- .../provider/KeyWritableRepository.java | 7 ++++++- .../keychain/provider/KeychainContract.java | 1 + .../keychain/provider/KeychainDatabase.java | 17 ++++++++++------- .../keychain/provider/KeychainProvider.java | 19 ++++++++++++------- 5 files changed, 35 insertions(+), 16 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java index 3b37fdea9..2e0d14faf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java @@ -188,6 +188,11 @@ public class ImportOperation extends BaseReadWriteOperation log.add(LogType.MSG_IMPORT_FETCH_ERROR_NOT_FOUND, 2); missingKeys += 1; + byte[] fingerprintHex = entry.getExpectedFingerprint(); + if (fingerprintHex != null) { + mKeyWritableRepository.renewKeyLastUpdatedTime( + KeyFormattingUtils.getKeyIdFromFingerprint(fingerprintHex), false); + } continue; } @@ -234,7 +239,7 @@ public class ImportOperation extends BaseReadWriteOperation } if (!skipSave) { - mKeyWritableRepository.renewKeyLastUpdatedTime(key.getMasterKeyId()); + mKeyWritableRepository.renewKeyLastUpdatedTime(key.getMasterKeyId(), keyWasDownloaded); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java index 0f92bc6af..74b667e91 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java @@ -1348,11 +1348,16 @@ public class KeyWritableRepository extends KeyRepository { return ContentProviderOperation.newInsert(uri).withValues(values).build(); } - public Uri renewKeyLastUpdatedTime(long masterKeyId) { + public Uri renewKeyLastUpdatedTime(long masterKeyId, boolean seenOnKeyservers) { ContentValues values = new ContentValues(); values.put(UpdatedKeys.MASTER_KEY_ID, masterKeyId); values.put(UpdatedKeys.LAST_UPDATED, GregorianCalendar.getInstance().getTimeInMillis() / 1000); + if (seenOnKeyservers) { + values.put(UpdatedKeys.SEEN_ON_KEYSERVERS, true); + } + // this will actually update/replace, doing the right thing™ for seenOnKeyservers value + // see `KeychainProvider.insert()` return mContentResolver.insert(UpdatedKeys.CONTENT_URI, values); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 2f6f4042e..79bef905c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -55,6 +55,7 @@ public class KeychainContract { interface UpdatedKeysColumns { String MASTER_KEY_ID = "master_key_id"; // not a database id String LAST_UPDATED = "last_updated"; // time since epoch in seconds + String SEEN_ON_KEYSERVERS = "seen_on_keyservers"; } interface UserPacketsColumns { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index da153a1c7..04a2cf356 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -52,7 +52,7 @@ import org.sufficientlysecure.keychain.util.Log; */ public class KeychainDatabase extends SQLiteOpenHelper { private static final String DATABASE_NAME = "openkeychain.db"; - private static final int DATABASE_VERSION = 21; + private static final int DATABASE_VERSION = 22; private Context mContext; public interface Tables { @@ -308,13 +308,16 @@ public class KeychainDatabase extends SQLiteOpenHelper { } */ case 20: - db.execSQL( - "CREATE TABLE IF NOT EXISTS overridden_warnings (" - + "_id INTEGER PRIMARY KEY AUTOINCREMENT, " - + "identifier TEXT NOT NULL UNIQUE " - + ")"); + db.execSQL( + "CREATE TABLE IF NOT EXISTS overridden_warnings (" + + "_id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "identifier TEXT NOT NULL UNIQUE " + + ")"); - if (oldVersion == 18 || oldVersion == 19 || oldVersion == 20) { + case 21: + db.execSQL("ALTER TABLE updated_keys ADD COLUMN seen_on_keyservers INTEGER;"); + + if (oldVersion == 18 || oldVersion == 19 || oldVersion == 20 || oldVersion == 21) { // no consolidate for now, often crashes! return; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 1554cd46b..2fde09b70 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -639,10 +639,10 @@ public class KeychainProvider extends ContentProvider { case UPDATED_KEYS_SPECIFIC: { HashMap projectionMap = new HashMap<>(); qb.setTables(Tables.UPDATED_KEYS); - projectionMap.put(UpdatedKeys.MASTER_KEY_ID, Tables.UPDATED_KEYS + "." - + UpdatedKeys.MASTER_KEY_ID); - projectionMap.put(UpdatedKeys.LAST_UPDATED, Tables.UPDATED_KEYS + "." - + UpdatedKeys.LAST_UPDATED); + projectionMap.put(UpdatedKeys.MASTER_KEY_ID, Tables.UPDATED_KEYS + "." + UpdatedKeys.MASTER_KEY_ID); + projectionMap.put(UpdatedKeys.LAST_UPDATED, Tables.UPDATED_KEYS + "." + UpdatedKeys.LAST_UPDATED); + projectionMap.put(UpdatedKeys.SEEN_ON_KEYSERVERS, + Tables.UPDATED_KEYS + "." + UpdatedKeys.SEEN_ON_KEYSERVERS); qb.setProjectionMap(projectionMap); if (match == UPDATED_KEYS_SPECIFIC) { qb.appendWhere(UpdatedKeys.MASTER_KEY_ID + " = "); @@ -780,9 +780,14 @@ public class KeychainProvider extends ContentProvider { break; } case UPDATED_KEYS: { - long updatedKeyId = db.replace(Tables.UPDATED_KEYS, null, values); - rowUri = UpdatedKeys.CONTENT_URI.buildUpon().appendPath("" + updatedKeyId) - .build(); + try { + db.insertOrThrow(Tables.UPDATED_KEYS, null, values); + } catch (SQLiteConstraintException e) { + String masterKeyId = values.getAsString(UpdatedKeys.MASTER_KEY_ID); + db.update(Tables.UPDATED_KEYS, values, + UpdatedKeys.MASTER_KEY_ID + " = ?", new String[] { masterKeyId }); + } + rowUri = UpdatedKeys.CONTENT_URI; break; } case API_APPS: { From 00e411b1e3a05ff019e23f520167f6f17e5715a7 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 20 May 2017 21:46:47 +0200 Subject: [PATCH 03/11] display keyserver status on card --- .../keychain/ui/keyview/ViewKeyFragment.java | 11 ++ .../keyview/loader/KeyserverStatusLoader.java | 136 ++++++++++++++++++ .../presenter/KeyserverStatusPresenter.java | 103 +++++++++++++ .../keyview/view/KeyserverStatusCardView.java | 124 ++++++++++++++++ .../res/drawable-hdpi/ic_cloud_off_24dp.png | Bin 0 -> 630 bytes .../drawable-hdpi/ic_cloud_unknown_24dp.png | Bin 0 -> 608 bytes .../res/drawable-mdpi/ic_cloud_off_24dp.png | Bin 0 -> 454 bytes .../drawable-mdpi/ic_cloud_unknown_24dp.png | Bin 0 -> 434 bytes .../res/drawable-xhdpi/ic_cloud_off_24dp.png | Bin 0 -> 834 bytes .../drawable-xhdpi/ic_cloud_unknown_24dp.png | Bin 0 -> 758 bytes .../res/drawable-xxhdpi/ic_cloud_off_24dp.png | Bin 0 -> 1228 bytes .../drawable-xxhdpi/ic_cloud_unknown_24dp.png | Bin 0 -> 1126 bytes .../drawable-xxxhdpi/ic_cloud_off_24dp.png | Bin 0 -> 1619 bytes .../ic_cloud_unknown_24dp.png | Bin 0 -> 1489 bytes .../res/layout/key_keyserver_card_content.xml | 78 ++++++++++ OpenKeychain/src/main/res/values/strings.xml | 5 + graphics/drawables/ic_cloud_off.svg | 4 + graphics/drawables/ic_cloud_unknown.svg | 59 ++++++++ graphics/update-drawables.sh | 2 +- 19 files changed, 521 insertions(+), 1 deletion(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/KeyserverStatusLoader.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/KeyserverStatusPresenter.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/view/KeyserverStatusCardView.java create mode 100644 OpenKeychain/src/main/res/drawable-hdpi/ic_cloud_off_24dp.png create mode 100644 OpenKeychain/src/main/res/drawable-hdpi/ic_cloud_unknown_24dp.png create mode 100644 OpenKeychain/src/main/res/drawable-mdpi/ic_cloud_off_24dp.png create mode 100644 OpenKeychain/src/main/res/drawable-mdpi/ic_cloud_unknown_24dp.png create mode 100644 OpenKeychain/src/main/res/drawable-xhdpi/ic_cloud_off_24dp.png create mode 100644 OpenKeychain/src/main/res/drawable-xhdpi/ic_cloud_unknown_24dp.png create mode 100644 OpenKeychain/src/main/res/drawable-xxhdpi/ic_cloud_off_24dp.png create mode 100644 OpenKeychain/src/main/res/drawable-xxhdpi/ic_cloud_unknown_24dp.png create mode 100644 OpenKeychain/src/main/res/drawable-xxxhdpi/ic_cloud_off_24dp.png create mode 100644 OpenKeychain/src/main/res/drawable-xxxhdpi/ic_cloud_unknown_24dp.png create mode 100644 OpenKeychain/src/main/res/layout/key_keyserver_card_content.xml create mode 100644 graphics/drawables/ic_cloud_off.svg create mode 100644 graphics/drawables/ic_cloud_unknown.svg diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java index bd61897c1..03e774f34 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java @@ -40,6 +40,8 @@ import org.sufficientlysecure.keychain.ui.keyview.presenter.ViewKeyMvpView; import org.sufficientlysecure.keychain.ui.keyview.view.IdentitiesCardView; import org.sufficientlysecure.keychain.ui.keyview.view.KeyHealthView; import org.sufficientlysecure.keychain.ui.keyview.view.SystemContactCardView; +import org.sufficientlysecure.keychain.ui.keyview.view.KeyserverStatusCardView; +import org.sufficientlysecure.keychain.ui.keyview.presenter.KeyserverStatusPresenter; public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView { @@ -51,6 +53,7 @@ public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView { private static final int LOADER_IDENTITIES = 1; private static final int LOADER_ID_LINKED_CONTACT = 2; private static final int LOADER_ID_SUBKEY_STATUS = 3; + private static final int LOADER_ID_KEYSERVER_STATUS = 4; private IdentitiesCardView mIdentitiesCardView; private IdentitiesPresenter mIdentitiesPresenter; @@ -62,6 +65,9 @@ public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView { KeyHealthPresenter mKeyHealthPresenter; + KeyserverStatusCardView mKeyserverStatusCard; + KeyserverStatusPresenter mKeyserverStatusPresenter; + /** * Creates new instance of this fragment */ @@ -85,6 +91,7 @@ public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView { mSystemContactCard = (SystemContactCardView) view.findViewById(R.id.linked_system_contact_card); mKeyStatusHealth = (KeyHealthView) view.findViewById(R.id.key_status_health); + mKeyserverStatusCard = (KeyserverStatusCardView) view.findViewById(R.id.keyserver_status_card); return root; } @@ -107,6 +114,10 @@ public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView { mKeyHealthPresenter = new KeyHealthPresenter( getContext(), mKeyStatusHealth, LOADER_ID_SUBKEY_STATUS, masterKeyId, mIsSecret); mKeyHealthPresenter.startLoader(getLoaderManager()); + + mKeyserverStatusPresenter = new KeyserverStatusPresenter( + getContext(), mKeyserverStatusCard, LOADER_ID_KEYSERVER_STATUS, masterKeyId, mIsSecret); + mKeyserverStatusPresenter.startLoader(getLoaderManager()); } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/KeyserverStatusLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/KeyserverStatusLoader.java new file mode 100644 index 000000000..8cd59f934 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/KeyserverStatusLoader.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2017 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.keyview.loader; + + +import java.util.Date; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.Cursor; +import android.support.v4.content.AsyncTaskLoader; +import android.util.Log; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys; +import org.sufficientlysecure.keychain.ui.keyview.loader.KeyserverStatusLoader.KeyserverStatus; + + +public class KeyserverStatusLoader extends AsyncTaskLoader { + public static final String[] PROJECTION = new String[] { + UpdatedKeys.LAST_UPDATED, + UpdatedKeys.SEEN_ON_KEYSERVERS + }; + private static final int INDEX_LAST_UPDATED = 0; + private static final int INDEX_SEEN_ON_KEYSERVERS = 1; + + + private final ContentResolver contentResolver; + private final long masterKeyId; + + private KeyserverStatus cachedResult; + + + public KeyserverStatusLoader(Context context, ContentResolver contentResolver, long masterKeyId) { + super(context); + + this.contentResolver = contentResolver; + this.masterKeyId = masterKeyId; + } + + @Override + public KeyserverStatus loadInBackground() { + Cursor cursor = contentResolver.query(UpdatedKeys.CONTENT_URI, PROJECTION, + UpdatedKeys.MASTER_KEY_ID + " = ?", new String[] { Long.toString(masterKeyId) }, null); + if (cursor == null) { + Log.e(Constants.TAG, "Error loading key items!"); + return null; + } + + try { + if (cursor.moveToFirst()) { + boolean isPublished = cursor.getInt(INDEX_SEEN_ON_KEYSERVERS) != 0; + Date lastUpdated = cursor.isNull(INDEX_LAST_UPDATED) ? + null : new Date(cursor.getLong(INDEX_LAST_UPDATED) * 1000); + + return new KeyserverStatus(masterKeyId, isPublished, lastUpdated); + } + + return new KeyserverStatus(masterKeyId); + } finally { + cursor.close(); + } + } + + @Override + public void deliverResult(KeyserverStatus keySubkeyStatus) { + cachedResult = keySubkeyStatus; + + if (isStarted()) { + super.deliverResult(keySubkeyStatus); + } + } + + @Override + protected void onStartLoading() { + if (cachedResult != null) { + deliverResult(cachedResult); + } + + if (takeContentChanged() || cachedResult == null) { + forceLoad(); + } + } + + public static class KeyserverStatus { + private final long masterKeyId; + private final boolean isPublished; + private final Date lastUpdated; + + KeyserverStatus(long masterKeyId, boolean isPublished, Date lastUpdated) { + this.masterKeyId = masterKeyId; + this.isPublished = isPublished; + this.lastUpdated = lastUpdated; + } + + KeyserverStatus(long masterKeyId) { + this.masterKeyId = masterKeyId; + this.isPublished = false; + this.lastUpdated = null; + } + + long getMasterKeyId() { + return masterKeyId; + } + + public boolean hasBeenUpdated() { + return lastUpdated != null; + } + + public boolean isPublished() { + if (lastUpdated == null) { + throw new IllegalStateException("Cannot get publication state if key has never been updated!"); + } + return isPublished; + } + + public Date getLastUpdated() { + return lastUpdated; + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/KeyserverStatusPresenter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/KeyserverStatusPresenter.java new file mode 100644 index 000000000..c3c248a22 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/KeyserverStatusPresenter.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017 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.keyview.presenter; + + +import java.util.Date; + +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.LoaderManager; +import android.support.v4.app.LoaderManager.LoaderCallbacks; +import android.support.v4.content.Loader; + +import org.sufficientlysecure.keychain.ui.keyview.loader.KeyserverStatusLoader; +import org.sufficientlysecure.keychain.ui.keyview.loader.KeyserverStatusLoader.KeyserverStatus; + + +public class KeyserverStatusPresenter implements LoaderCallbacks { + private final Context context; + private final KeyserverStatusMvpView view; + private final int loaderId; + + private final long masterKeyId; + private final boolean isSecret; + + + public KeyserverStatusPresenter(Context context, KeyserverStatusMvpView view, int loaderId, long masterKeyId, + boolean isSecret) { + this.context = context; + this.view = view; + this.loaderId = loaderId; + + this.masterKeyId = masterKeyId; + this.isSecret = isSecret; + + view.setOnKeyserverClickListener(new KeyserverStatusClickListener() { + @Override + public void onKeyRefreshClick() { + KeyserverStatusPresenter.this.onKeyRefreshClick(); + } + }); + } + + public void startLoader(LoaderManager loaderManager) { + loaderManager.restartLoader(loaderId, null, this); + } + + @Override + public Loader onCreateLoader(int id, Bundle args) { + return new KeyserverStatusLoader(context, context.getContentResolver(), masterKeyId); + } + + @Override + public void onLoadFinished(Loader loader, KeyserverStatus keyserverStatus) { + if (keyserverStatus.hasBeenUpdated()) { + if (keyserverStatus.isPublished()) { + view.setDisplayStatusPublished(); + } else { + view.setDisplayStatusNotPublished(); + } + view.setLastUpdated(keyserverStatus.getLastUpdated()); + } else { + view.setDisplayStatusUnknown(); + } + } + + @Override + public void onLoaderReset(Loader loader) { + + } + + private void onKeyRefreshClick() { + + } + + public interface KeyserverStatusMvpView { + void setOnKeyserverClickListener(KeyserverStatusClickListener keyserverStatusClickListener); + + void setDisplayStatusPublished(); + void setDisplayStatusNotPublished(); + void setLastUpdated(Date lastUpdated); + void setDisplayStatusUnknown(); + } + + public interface KeyserverStatusClickListener { + void onKeyRefreshClick(); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/view/KeyserverStatusCardView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/view/KeyserverStatusCardView.java new file mode 100644 index 000000000..2dcb2155b --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/view/KeyserverStatusCardView.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2017 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.keyview.view; + + +import java.util.Date; + +import android.content.Context; +import android.support.annotation.ColorRes; +import android.support.annotation.DrawableRes; +import android.support.annotation.StringRes; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.CardView; +import android.text.format.DateFormat; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.ImageView; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.keyview.presenter.KeyserverStatusPresenter.KeyserverStatusClickListener; +import org.sufficientlysecure.keychain.ui.keyview.presenter.KeyserverStatusPresenter.KeyserverStatusMvpView; + + +public class KeyserverStatusCardView extends CardView implements KeyserverStatusMvpView, OnClickListener { + private final View vLayout; + private final TextView vTitle; + private final TextView vSubtitle; + private final ImageView vIcon; + + private KeyserverStatusClickListener keyHealthClickListener; + + public KeyserverStatusCardView(Context context, AttributeSet attrs) { + super(context, attrs); + + View view = LayoutInflater.from(context).inflate(R.layout.key_keyserver_card_content, this, true); + + vLayout = view.findViewById(R.id.key_health_layout); + + vTitle = (TextView) view.findViewById(R.id.keyserver_status_title); + vSubtitle = (TextView) view.findViewById(R.id.keyserver_status_subtitle); + vIcon = (ImageView) view.findViewById(R.id.keyserver_icon); +// vExpander = (ImageView) view.findViewById(R.id.key_health_expander); + } + + private enum KeyserverDisplayStatus { + PUBLISHED (R.string.keyserver_title_published, R.drawable.ic_cloud_black_24dp, R.color.md_grey_900), + NOT_PUBLISHED (R.string.keyserver_title_not_published, R.drawable.ic_cloud_off_24dp, R.color.md_grey_900), + UNKNOWN (R.string.keyserver_title_unknown, R.drawable.ic_cloud_unknown_24dp, R.color.md_grey_900); + + @StringRes + private final int title; + @DrawableRes + private final int icon; + @ColorRes + private final int iconColor; + + KeyserverDisplayStatus(@StringRes int title, @DrawableRes int icon, @ColorRes int iconColor) { + this.title = title; + this.icon = icon; + this.iconColor = iconColor; + } + } + + @Override + public void onClick(View view) { + if (keyHealthClickListener != null) { + keyHealthClickListener.onKeyRefreshClick(); + } + } + + @Override + public void setOnKeyserverClickListener(KeyserverStatusClickListener keyHealthClickListener) { + this.keyHealthClickListener = keyHealthClickListener; + vLayout.setClickable(keyHealthClickListener != null); + } + + @Override + public void setDisplayStatusPublished() { + setDisplayStatus(KeyserverDisplayStatus.PUBLISHED); + } + + @Override + public void setDisplayStatusNotPublished() { + setDisplayStatus(KeyserverDisplayStatus.NOT_PUBLISHED); + } + + @Override + public void setDisplayStatusUnknown() { + setDisplayStatus(KeyserverDisplayStatus.UNKNOWN); + vSubtitle.setVisibility(View.GONE); + } + + @Override + public void setLastUpdated(Date lastUpdated) { + String lastUpdatedText = DateFormat.getMediumDateFormat(getContext()).format(lastUpdated); + vSubtitle.setText(getResources().getString(R.string.keyserver_last_updated, lastUpdatedText)); + } + + private void setDisplayStatus(KeyserverDisplayStatus displayStatus) { + vTitle.setText(displayStatus.title); + vIcon.setImageResource(displayStatus.icon); + vIcon.setColorFilter(ContextCompat.getColor(getContext(), displayStatus.iconColor)); + + setVisibility(View.VISIBLE); + } +} diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_cloud_off_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_cloud_off_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..fabb499c711cdeeffa6fc8ac185e115a188aabcf GIT binary patch literal 630 zcmV-+0*U>JP)jK~z|U?bkbN6j2z)@t>DM)I!t+5k$fDf)d3hSi~Y;8f{Wp z`T<1n8>p3qRkYJo))uZGKnW=cHqorFLTRIvk#o+oc9_2 zbLM^DbB3|T8mk&j;3;Z-)_36=o?r#P@e9jn;3{^O1#=utwDG0~)U6Icd&u~KJJ?zj zsMZ0AAKVRf39S@B@p#{P$-+>thd{l+b)3WwY{N0pr+nE3)x?RSP`mMJ0MsCDz!m%q z{GU4Dl|jKNd<*;!^MXuc4olW1a8{^`f%hMdWC1xWD)(WDniZ-Mc<-h)P2o!lm^@Ht z1K)=D$6-$y*cjHm$CXdwt*HMm6cLM`dN)6cO#3_LHn zGimxo9;o>~Ky7@+fjDq3SeQ)#Qv?b&;UI2ER`4P=61Dbtaxw!@5vW+FyPz%;9fI-1 z_KZMfK-~UI$O zIG&sjCRaxYHI2VP+)TW1Nb($O;hv~0*DKNYN>=!SP9KVOMz+H>&**m@UJ-QaP<1>T zG1LX@>je|)9PZ<-pQwhIEWvC{)CQnoiGFB=256G)SW;?dz QcmMzZ07*qoM6N<$f~rRacmMzZ literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_cloud_unknown_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_cloud_unknown_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..a4c7be5df38e841e33803b40d549ce45a0020cf3 GIT binary patch literal 608 zcmV-m0-ybfP)}Am69(Z9t zo?-qo`|kS=J@wSn|EDu}ia9LcBOc)tDlOi9xQ$QvnT?smRrGfYvI*l@E{NyYmH97W zzQ8++k+LvVOmu+BV+Pl-6yaUKi84SJBVo$K9Jb{X^+mw+;W4(BSxJO8qb}rDlxouL zP9E>`$*K`RM`Fpl&=5Alx(M??LXbs#!*`(!)Y}17a2Q90ycu3&h$TG4m{2C_Mdli& zrOTBm9E>HuAk>9aFcVf$ok!3}2=iK~^if(b^rKWTzp%Gu&H>!_w;z7MWm#Q3pAcpl zcUxdK2z95O_Xa#k40Nmo=v>pgN!)C@|5Cwc?3PA9Fev=$xlZDEc0G6`SaR>F7n9-6aJ@wSnztV3JF_tB!Is`rd0000jXpSW>p@7y9%ZKE(AnQSMjctr2 zl~_V&!sRrO6`!kKehVH2N1@8=vQ*N8c{~Q~IF%3DTIEt!2+>%Vv7ZHy1=1TdH$-EZ zYK}8Nx4nJ zFx~}W#_P`xw*UntOI#yLg7ec#$`gxH8OqDc^)mCai<1)zQuXqS(r3T3kpimL_jGX# z@#uVeWv{kdq6FKA`>$0adDJ}9xV?CcPQ)wBp3^y!9WcI literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_cloud_off_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_cloud_off_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..fa6dd8da2f6fdc073924afe255b8b862a86bfaab GIT binary patch literal 834 zcmV-I1HJr-P)4Q_K~!jg?bykW3}GC`@ef0YZEzs8R)2?q%W4kSdx5C{K* zH4_&nE@F!V;UHlm4ibNW1V<+%+z`toh@FFNgdx^LbtmPm?w;zd>gkd6le~SX_kDlw z^L^|0F4eTvR$Fbg)mC*Ca23TAZAV}|j^R39;48l1F|Oe#)?idK%!VqU7k^4-szHp! zKD;WmO;cWB4< z=wNI}oA5EE@w^~9@ji&RvXMaBrJwb@A#XHWz zkD$@kItI=YBJEl`67RGtsJyN5J4v2vVl0-4E_&hl^BWt>4X*^1-3^*AD^G~B7E9j8 zdX>(K-k@SZhJngz7DV_aw!r7$VVWCd8Z`srWDxH>^l1(N`9mxZVm(y)$vcJO$`c~o zYWv`F@G$EluB5ymCI>P4VMp-rjscGC29*cIilF}^m?3$PZO_YfpsWyQgT5Ea6=#Bq z?^vCe>p+b{tSI@4_gh!4oG!T=eHW$IP)Mhhcq^Gc|9Go*oxUsj#9M@MEr962jiB8x zRW6%5t3V89sRS|6zki+`s5LViB)<)Y3dBw9t!2KfX*eypJGB~OWE%5v6+~ZUmvB$= z-qZ?+A+1UaJL^AygRV*tS6k|+0mUJDMXCJXsVJKH|I&P0Z4H+G0(^b M07*qoM6N<$fM@Sp2&U5`@NTS77MX3T(rBvIGE$OOs6 zgcwR56U@lKz(7}&Qi`F3D@9#m;L*JlDJ}-rBV3PTu+G-*T<6@q&pFO6?XUi){aD}H z-}hg8t^eAG96562$dRKyjK?lq$6E~G6|Q1GW>?{k!xmgaKZ>P3ZsRZ(|0~fEHlwff z;7aimooFxPt`>||;}&sI^(54c0|_1;kJngI;_k$+6x?@M*+}u*Q;Lt`4fYD7l9u}k zOB*464!+iCpx)?3YrO`}W+Xm}UG<1>$M=j4JdF!#Noim`3Mn~_#$3#cPSP4!GIW!s zU_o?{*1*gL3#k=pRUg$2jKLZ#7q$zdH1`$i!x3D-i!{X1 z*e4W@ELNEx!aitR4W>=Q{fvYc@j+PWYQl89%uxI%EUQ)g2;9n0{4aFWDxR=A%u0L_ zrz+@b#ctdaM(B%BiH~AJ)Tm9^>SiVWvoQC{(T1BSvXtaQXLY;{7 zPefVB%E0A>22Pame&D1qQl4jM;Cw;@@g(QoQdCTDP7zlthLQX3)a@#0o=h>VRkng z+v*_?j~ze6U91znzM6#@LIQg#+#e}?WObLKOW531#AKWns$8??!Xu1L=X+>O#U5NS oxB~u*IM-+cIdbI4k>mfzZxFlK*=ZmZPyhe`07*qoM6N<$g6H{KHvj+t literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_cloud_off_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_cloud_off_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..79d9717f89b5d264a6b2c7a26ff9988a592e5a79 GIT binary patch literal 1228 zcmV;-1T*`IP)@~8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11W8FmK~#90?b}PJRaG3v@sD0_zA~t;1lgdcISeW&C<1$!MN}$e zK}7a|qDjLBfzE6Yy(1b3y)?*@1`*^yd(expq6Zm7*;R=iP&u&DJPp=8ayXs;xo4mK zI_mzye{|RW|JM55wf0*3fA*%Kp`oFnp`oFnp`oFnYG!0?KaS_I8L#HhJpmWvqN!Mf zlcr)E`|u0C!x#7f+weyY&C(f{jA`K?tju`*7?$H@?4Qb$CH}@6Sb<+FiA`W6L zW)?Hl;pu16bTg@JlUmZIF{hxh4o#6J{f?J#x3o-7kp?)930#2N@f>~&G`nLv<`py4 z;ki*hleXek9G5|JHg3X>K({mAluWLb8>DS_*ct zw_{a_`U7*l`b?UQR|4&pG|Lv2s6Q~v@t5I_0C^e1Yk_tPPnW12a+Ly(i=1dt=b3imF_lCY>nNup^W1Z#VojXmE9jo$I7-lcJgQ zlc^Q-YtUeEi5=^vPLnPRbX(F=3`?d9O6*iG>NII_a)&%+AZZ~)@Xs}y7yID77o3t&+{YbmP>YzajuN#4vlay!D(jaFG zSGF6CNtLyz;bV?YlTOC>LB74Z%CT1j4LfJj(X`wq@KKPbB^CGo$8<&eJyK2ONZ;Kq z5zn0V4&Jj+I7fe1+c7Eq=Dl1xg%(|P9E3b3^?PUX5U%Sdyiou9(UB6vn)FaVbJ|R| zK97{hYSIQ7HlsPx3rKYjvMQ6-XECRfrTA(D23n>`xp(K8(rdWyD)x1lQ8K9~SwDw@ zGjNquj$DG%rT;*d_nzzGfb`B}o=>@s3i5nga?H?}Gywe$lbdSn*GSQ8CT%tX-*b!- qohCKr1gw;nRbx&=LqkKuF~WagV&o@kjCvdZ0000o7Om-hid~U;jA?S8&AGQgiwqgj+;;%OS&f+ye z#9A;U{O8&gCTn|fWfR{wW1^tnzp)diM~t;&*5NC~#c9|awr6~A$9x3eU*Lksaks^} z!o1RL088z`Mc>;Z`<=kX5;SPaO1u|2h=x6dBPIC#8fTUvMjnrs5I`?%tPC--oP$3q z2riag6JL_!d9DUQc4ME!m*g0$_4nv$*k#$k2D;Y<+=x|?d{xP<*$1%%+7!u8)!dYQ z5KEwoBl)SCi)1v3A0A<)LUdL7;ZxoG{*es+8K6Ct@=NBP`P<8OFYyuBqO-xY;!rUwV>AY7q zk7ty07`;RoEma3u)W&gbbX6AFjF}n)S=9E5JjX2M7X35n=Go^;m}?rW$5ai1ENTaY zjAkjU7V@-J2U*nSG*9wJVMh&u?8Y8#GtRT|06xIa!WQLsVL^HtSL8Js(%+xfcfv*F za@>Q%ZTNc$r!H;yL=A%M#_n!IoclWT`6f;v92LH=L6Ajl949R!&PHL5>eTmE7_C8& z-Pn`Mh(kCc<+VM8_cfnhcEe*e2(lX+T}GTnAw#y^q+?rx4NZJc52(>)egS5LYLHe8 z(9%-^#$AgDU=8el)F^FA{DV&?VaMYaEEmH>%FB9TZW5{X12k?0No0ztmrH`>%CtN;K207*qoM6N<$f~HpqaR2}S literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_cloud_off_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_cloud_off_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..93003e9a018e8d733ba85c33effe0ffa25768840 GIT binary patch literal 1619 zcmV-Z2CVssP)k8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11<*-EK~#90?VMYPRaF?rf5(|gt(n4Uv@yz{A}8odXg4OvkVtZT z$&#QKXH=>wBeTmx7iglOo9Vpt(o8>j&T4-fR8$`tQqHYkzBj$K&yMJRXn7V*#3^A>#sBQcp(wcA^FajJSd80>}u>)9~LZ36N=KqZ!#M{I- z;YwgzNO^h0m%yU97wrm-?)>_Q^CWCS?#`kbzc#i3-3i*jDsQ2?IIRhbfC*)pdBoqq zibO48i3-hULKPSWrYC3*D^!3D=q^nAP3T3slQvD*jr71uK^zm(J_*NxkAN3|WyrYW z0$>KvhKvV#fTh6Gz)q5zxd-Tq-yC+R0B@3?37-P@B7?EG%m9`HA7bt_)W^5Lxe4mD z#v5d7!mYp!8S1+lcweb8h+W9jRT^F=TNCo&4q#NFF^COzWG3e|+&7^Ecvqn@h~;)< zCg&w|7nWqioF!|~eH#uTAN*4BD(*iC0Bg{h@SZJ!X&9m$Pr@@Hy6WQ=TLLw70`~wf zBYp56fIrZ`XKgfGKb#M|0*sTs(OoiO zu|nTgOWFWvLH;b|(eSRAu-%~TBrw~CuuGADta&!P3npBPxmUENXi-a$IrE|#-?j-m z4Z1!lQq29N`zf9_Y;;lYGiaJbCi-%xpZK$g&G015L{eO$H1*~!VhQo@{Bfi|Jpe2~ zK2}k)i4~-AT{PiigPvcIY3_}{1Him2E595`X3{qNK42ws=fuTT6SSk_+V~E5l$8Gw z+JJAA6;1%_fYvk>;-(3GNV`xu9?uI)M9!~#YzIZ5Z$Y1 z5BUs6Tr#0cslyagz>B~%O{JQDyQAbg5_H9c=}HaS*oxuzW+&z>f(f#H?S$cq30WFY zMW#7IpM*yg1+2^V`XX_`gscsyBEM`KVw&gKNBZ7QTwE}r5$6E&fjg0Lz;8;8K`g5; zup9G;cV&(j6osoM)Y5{q&+kf|eO8^?lNfF z3+9BRg$0X_i_0d=H|RM8vh*kFpj8D5PKv80bQrXp0P{MpvSbFkXu?c`j!7`5VI0Qp zR74zHGhv=V!$FY62d2rm{+xwoiAyFdHPpX1IM6g5JxaM2thr)>Y4PIb;J}9!Lf7Xc zGB0eK&U}aexD#_W*d%aM#v=2^rU|PQbzWb4f@YcN7;sUBB8$L=30;`){{8hQ z%|*Ao|9iX%QM3RbDC&8>(aBFJMULTLM~!F!-cgi)jF9x6HYCSUBXSaW4*5x47VL`x3$ldgMqmzS)>O7; z)2S)oZmq}x+*qLDLS$vaQ242h>BruNdagxU6E-6s>Dcs<|I*a5L{byBkTSFIGGrGi z+YR7C#4+J0^4d|Qr<-U69w*-NrDhGS33C+3#HJhBj>h&5hqlA^1|!S@9zwPOK2v*3 zsZGer!P&s=$n-=Hvb8}MGT?A^ERYq$$m+@e87n3BK_Qrq%@C_?D<3AKXFPLOdRi$K&yMJRVQ^_y^WC#r)E` R>#YC)002ovPDHLkV1ig-`Pl#f literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_cloud_unknown_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_cloud_unknown_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..f9196f6f2a72e5a56d95004786249c0944e14494 GIT binary patch literal 1489 zcmV;?1upuDP)8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11x`ssK~#90?VN3h6;&9=fA@MzD`|set8H7iQqe6ch>9o>!ghtU zu>4R&^db9DL=jX_2nAsjSri3n(1++l5(Q;%_VPiqG)oa8Kdkmbnt__S?Y7$Lw!3|J z?q%;@X70S5GiPS*^MgOTJ9FneXa8sJnK|b?GoUDnq9}@@D2k#eilQirq9{tyDY>pd z1M`4oz#?EFa4|5ey6+Hh5EuY<0N+%9P8x`NZZ+;=pcj}A%&zVmtc&|2uo>6}j2VcV z8I}TDfC^it5LgFX6*F#T=mowd+CT55G2kuWf{<}Lf%OLB zP6D3-m&f_%U2{J$iWw7X8U`K+h`R(hYB=sG;5p#5xMrkD33v^&1~lqj;7pI$4+8&L zjJpjuKbbxDS%+QoYuW;|)x~{?=+~Gy`USap$Rw*s$Nc(6mkSrg`&;APyK;X-4;6$boEL=SSC=GqKb+K}ABWW0)d z6I^P7Cf|Y?5sZw(8Nl02&F8ctC1k2&ql@5o;r*-8J%U8%g+n#z_ngG zY&XDS%jM^Z8@)E#Y=CZLo?Q{H^x9~%0eUT#oktdUZM4|{T^7sEBc5v&HXGn5#~S>4bTD1Mf$kf zU}G4cuUvr`13pGZ`Wun?u2d)iHvnsaJ-8obrmYjmlIonKMn3NrS+O#4g5UCFavd@}!+OT4XN)FGj3v(eyb}DT=An>I*3!0H-H*F(yxj zs9=$`35SbFH{&LKBrE!v=?Pt_(t)I6yEegUv*g^R01xB-EOe=x0t~IEj4Uns zm34sW3sG|dVjnM4fJZG8a0+>@D=eKzmYW}+A#1@k4kI@=zVR!_Afi>-iChfBG7db^ zWdDx3-)8_X(LD3yH-JBJ-wn9Zn@!A#O9{9IY22XrPXqj$0V7=L@pvBlG7lM3go*t- z&{n~If^L>MXt1nvfUkgyLVWgDBy)VIOp56g8Dzv@S&tBG{s^!dNz@&vZ5+UTSKvxZ z0t}17Pfg-XAlI9Y#)Hbp*P&H0wdz$ulGFTH1>A>BO;4i?Y&TSiJYs6WeKztuoEM|O zD}D!7kPF8%hANUbOg%ty5_r#N-%;dF)0Yx5AaLCuyU^>+j7ft^wIXjIQ_`)*LPO=W z(|jp+ACju>OB=F&>H3UJTO*ixp+-;|PoLj`wL#brvLM}RoQ14~JEMLSyq0J)gGFJv z%i#45FpfOge#}u_TyX~uU*Fj{53~=sm4Mr(g3&{hKH4^36LDLC+lV$#o_8K|71(bk z^b6#yIFw>tT+)ZU#5Hw-`o1nC^J`HGb#cvh;Cke?F)E!@Q%ro=LxQJ9%g8Gq2XTMj zZd)tknv@3ZNb;`fx6Wos!${U!rkMrYjl7f7wP7~&1O79cnWGE12N}eLS%l7uYKc{f rq9}@@D2k#eilQirq9}@@D2e$WAv!?Zh9JZ;00000NkvXXu0mjfGm4|# literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/layout/key_keyserver_card_content.xml b/OpenKeychain/src/main/res/layout/key_keyserver_card_content.xml new file mode 100644 index 000000000..bf0636d50 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/key_keyserver_card_content.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 31a81e651..8b075b06e 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -1842,6 +1842,11 @@ "Healthy (Partially Stripped)" "Click for details" + "Published" + "Not Published" + "Unknown" + "Last updated: %s" + "This key uses the %1$s algorithm with a strength of %2$s bits. A secure key should have a strength of 2048 bits." "This key can\'t be upgraded. For secure communication, the owner must generate a new key." diff --git a/graphics/drawables/ic_cloud_off.svg b/graphics/drawables/ic_cloud_off.svg new file mode 100644 index 000000000..b2373ff16 --- /dev/null +++ b/graphics/drawables/ic_cloud_off.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/graphics/drawables/ic_cloud_unknown.svg b/graphics/drawables/ic_cloud_unknown.svg new file mode 100644 index 000000000..8fda8d19f --- /dev/null +++ b/graphics/drawables/ic_cloud_unknown.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/graphics/update-drawables.sh b/graphics/update-drawables.sh index 4407315b7..f4c998e4b 100755 --- a/graphics/update-drawables.sh +++ b/graphics/update-drawables.sh @@ -22,7 +22,7 @@ SRC_DIR=./drawables/ #inkscape -w 512 -h 512 -e "$PLAY_DIR/$NAME.png" $NAME.svg -for NAME in "broken_heart" "ic_cloud_search" "ic_action_encrypt_file" "ic_action_encrypt_text" "ic_action_verified_cutout" "ic_action_encrypt_copy" "ic_action_encrypt_paste" "ic_action_encrypt_save" "ic_action_encrypt_share" "status_lock_closed" "status_lock_error" "status_lock_open" "status_signature_expired_cutout" "status_signature_invalid_cutout" "status_signature_revoked_cutout" "status_signature_unknown_cutout" "status_signature_unverified_cutout" "status_signature_verified_cutout" "key_flag_authenticate" "key_flag_certify" "key_flag_encrypt" "key_flag_sign" "yubi_icon" "ic_stat_notify" "status_signature_verified_inner" "link" "octo_link" +for NAME in "ic_cloud_unknown" "ic_cloud_off" "broken_heart" "ic_cloud_search" "ic_action_encrypt_file" "ic_action_encrypt_text" "ic_action_verified_cutout" "ic_action_encrypt_copy" "ic_action_encrypt_paste" "ic_action_encrypt_save" "ic_action_encrypt_share" "status_lock_closed" "status_lock_error" "status_lock_open" "status_signature_expired_cutout" "status_signature_invalid_cutout" "status_signature_revoked_cutout" "status_signature_unknown_cutout" "status_signature_unverified_cutout" "status_signature_verified_cutout" "key_flag_authenticate" "key_flag_certify" "key_flag_encrypt" "key_flag_sign" "yubi_icon" "ic_stat_notify" "status_signature_verified_inner" "link" "octo_link" do echo $NAME inkscape -w 24 -h 24 -e "$MDPI_DIR/${NAME}_24dp.png" "$SRC_DIR/$NAME.svg" From 25e58943c99885bf0d5f181f6ec6958a1fe48b80 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 26 May 2017 18:28:24 +0200 Subject: [PATCH 04/11] move keyserver status into key status card --- .../keychain/ui/keyview/ViewKeyFragment.java | 11 ++- .../presenter/KeyserverStatusPresenter.java | 17 ---- ...CardView.java => KeyserverStatusView.java} | 27 ++----- .../res/layout/key_health_card_content.xml | 2 +- .../res/layout/key_keyserver_card_content.xml | 78 ------------------- .../layout/key_keyserver_status_layout.xml | 65 ++++++++++++++++ .../src/main/res/layout/view_key_fragment.xml | 11 +++ 7 files changed, 87 insertions(+), 124 deletions(-) rename OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/view/{KeyserverStatusCardView.java => KeyserverStatusView.java} (79%) delete mode 100644 OpenKeychain/src/main/res/layout/key_keyserver_card_content.xml create mode 100644 OpenKeychain/src/main/res/layout/key_keyserver_status_layout.xml diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java index 03e774f34..a5e6af591 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java @@ -35,13 +35,13 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.ui.base.LoaderFragment; import org.sufficientlysecure.keychain.ui.keyview.presenter.IdentitiesPresenter; import org.sufficientlysecure.keychain.ui.keyview.presenter.KeyHealthPresenter; +import org.sufficientlysecure.keychain.ui.keyview.presenter.KeyserverStatusPresenter; import org.sufficientlysecure.keychain.ui.keyview.presenter.SystemContactPresenter; import org.sufficientlysecure.keychain.ui.keyview.presenter.ViewKeyMvpView; import org.sufficientlysecure.keychain.ui.keyview.view.IdentitiesCardView; import org.sufficientlysecure.keychain.ui.keyview.view.KeyHealthView; +import org.sufficientlysecure.keychain.ui.keyview.view.KeyserverStatusView; import org.sufficientlysecure.keychain.ui.keyview.view.SystemContactCardView; -import org.sufficientlysecure.keychain.ui.keyview.view.KeyserverStatusCardView; -import org.sufficientlysecure.keychain.ui.keyview.presenter.KeyserverStatusPresenter; public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView { @@ -62,10 +62,9 @@ public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView { SystemContactPresenter mSystemContactPresenter; KeyHealthView mKeyStatusHealth; + KeyserverStatusView mKeyStatusKeyserver; KeyHealthPresenter mKeyHealthPresenter; - - KeyserverStatusCardView mKeyserverStatusCard; KeyserverStatusPresenter mKeyserverStatusPresenter; /** @@ -91,7 +90,7 @@ public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView { mSystemContactCard = (SystemContactCardView) view.findViewById(R.id.linked_system_contact_card); mKeyStatusHealth = (KeyHealthView) view.findViewById(R.id.key_status_health); - mKeyserverStatusCard = (KeyserverStatusCardView) view.findViewById(R.id.keyserver_status_card); + mKeyStatusKeyserver = (KeyserverStatusView) view.findViewById(R.id.key_status_keyserver); return root; } @@ -116,7 +115,7 @@ public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView { mKeyHealthPresenter.startLoader(getLoaderManager()); mKeyserverStatusPresenter = new KeyserverStatusPresenter( - getContext(), mKeyserverStatusCard, LOADER_ID_KEYSERVER_STATUS, masterKeyId, mIsSecret); + getContext(), mKeyStatusKeyserver, LOADER_ID_KEYSERVER_STATUS, masterKeyId, mIsSecret); mKeyserverStatusPresenter.startLoader(getLoaderManager()); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/KeyserverStatusPresenter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/KeyserverStatusPresenter.java index c3c248a22..3a1070e7f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/KeyserverStatusPresenter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/KeyserverStatusPresenter.java @@ -47,13 +47,6 @@ public class KeyserverStatusPresenter implements LoaderCallbacks + tools:visibility="gone"> - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/OpenKeychain/src/main/res/layout/key_keyserver_status_layout.xml b/OpenKeychain/src/main/res/layout/key_keyserver_status_layout.xml new file mode 100644 index 000000000..103e4eb98 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/key_keyserver_status_layout.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + diff --git a/OpenKeychain/src/main/res/layout/view_key_fragment.xml b/OpenKeychain/src/main/res/layout/view_key_fragment.xml index 93277e953..16fa99c97 100644 --- a/OpenKeychain/src/main/res/layout/view_key_fragment.xml +++ b/OpenKeychain/src/main/res/layout/view_key_fragment.xml @@ -36,6 +36,17 @@ android:id="@+id/key_status_health" /> + + + + From 471d35ebcf1bf239120b4c25aa25b2fb7bc51ec6 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 20 Jul 2017 21:05:42 +0200 Subject: [PATCH 05/11] minor layout fixes --- .../keyview/presenter/KeyHealthPresenter.java | 2 ++ .../res/layout/key_health_card_content.xml | 20 +++++++++++++------ .../res/layout/subkey_status_card_content.xml | 4 +++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/KeyHealthPresenter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/KeyHealthPresenter.java index c3fdb832b..979875c1a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/KeyHealthPresenter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/KeyHealthPresenter.java @@ -115,9 +115,11 @@ public class KeyHealthPresenter implements LoaderCallbacks { view.setKeyStatus(keyHealthStatus); view.setPrimaryExpiryDate(subkeyStatus.keyCertify.mExpiry); view.setShowExpander(false); + view.hideExpandedInfo(); } else { view.setKeyStatus(keyHealthStatus); view.setShowExpander(keyHealthStatus != KeyHealthStatus.REVOKED); + view.hideExpandedInfo(); } } diff --git a/OpenKeychain/src/main/res/layout/key_health_card_content.xml b/OpenKeychain/src/main/res/layout/key_health_card_content.xml index 71a62d86d..31b942a59 100644 --- a/OpenKeychain/src/main/res/layout/key_health_card_content.xml +++ b/OpenKeychain/src/main/res/layout/key_health_card_content.xml @@ -70,7 +70,9 @@ android:layout_width="match_parent" android:layout_height="1dip" android:id="@+id/key_health_divider" - android:background="?android:attr/listDivider" /> + android:background="?android:attr/listDivider" + android:visibility="gone" + tools:visibility="visible" /> + + + tools:parentTag="LinearLayout" + tools:layout_width="match_parent" + tools:layout_height="match_parent"> Date: Wed, 28 Jun 2017 14:41:24 +0200 Subject: [PATCH 06/11] fix content observer notification in IdentityLoader --- .../keychain/ui/keyview/loader/IdentityLoader.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/IdentityLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/IdentityLoader.java index cf62eec0a..a2edbaffe 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/IdentityLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/IdentityLoader.java @@ -34,6 +34,7 @@ import com.google.auto.value.AutoValue; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.linked.LinkedAttribute; import org.sufficientlysecure.keychain.linked.UriAttribute; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.IdentityInfo; @@ -72,6 +73,7 @@ public class IdentityLoader extends AsyncTaskLoader> { private List cachedResult; + private ForceLoadContentObserver identityObserver; public IdentityLoader(Context context, ContentResolver contentResolver, long masterKeyId, boolean showLinkedIds) { super(context); @@ -79,6 +81,8 @@ public class IdentityLoader extends AsyncTaskLoader> { this.contentResolver = contentResolver; this.masterKeyId = masterKeyId; this.showLinkedIds = showLinkedIds; + + this.identityObserver = new ForceLoadContentObserver(); } @Override @@ -169,6 +173,16 @@ public class IdentityLoader extends AsyncTaskLoader> { if (takeContentChanged() || cachedResult == null) { forceLoad(); } + + getContext().getContentResolver().registerContentObserver( + KeyRings.buildGenericKeyRingUri(masterKeyId), true, identityObserver); + } + + @Override + protected void onAbandon() { + super.onAbandon(); + + getContext().getContentResolver().unregisterContentObserver(identityObserver); } public interface IdentityInfo { From 57b0e16ecc188affe7a8441e564c7fdfc59b011c Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 20 Jul 2017 21:30:34 +0200 Subject: [PATCH 07/11] fix content observer for keyserver status --- .../keychain/provider/KeychainProvider.java | 4 ++-- .../ui/keyview/loader/KeyserverStatusLoader.java | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 2fde09b70..5d5916211 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -780,12 +780,12 @@ public class KeychainProvider extends ContentProvider { break; } case UPDATED_KEYS: { + keyId = values.getAsLong(UpdatedKeys.MASTER_KEY_ID); try { db.insertOrThrow(Tables.UPDATED_KEYS, null, values); } catch (SQLiteConstraintException e) { - String masterKeyId = values.getAsString(UpdatedKeys.MASTER_KEY_ID); db.update(Tables.UPDATED_KEYS, values, - UpdatedKeys.MASTER_KEY_ID + " = ?", new String[] { masterKeyId }); + UpdatedKeys.MASTER_KEY_ID + " = ?", new String[] { Long.toString(keyId) }); } rowUri = UpdatedKeys.CONTENT_URI; break; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/KeyserverStatusLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/KeyserverStatusLoader.java index 8cd59f934..cb6bd71ba 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/KeyserverStatusLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/KeyserverStatusLoader.java @@ -27,6 +27,7 @@ import android.support.v4.content.AsyncTaskLoader; import android.util.Log; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys; import org.sufficientlysecure.keychain.ui.keyview.loader.KeyserverStatusLoader.KeyserverStatus; @@ -45,12 +46,15 @@ public class KeyserverStatusLoader extends AsyncTaskLoader { private KeyserverStatus cachedResult; + private ForceLoadContentObserver keyserverStatusObserver; public KeyserverStatusLoader(Context context, ContentResolver contentResolver, long masterKeyId) { super(context); this.contentResolver = contentResolver; this.masterKeyId = masterKeyId; + + this.keyserverStatusObserver = new ForceLoadContentObserver(); } @Override @@ -95,6 +99,16 @@ public class KeyserverStatusLoader extends AsyncTaskLoader { if (takeContentChanged() || cachedResult == null) { forceLoad(); } + + getContext().getContentResolver().registerContentObserver( + KeyRings.buildGenericKeyRingUri(masterKeyId), true, keyserverStatusObserver); + } + + @Override + protected void onAbandon() { + super.onAbandon(); + + getContext().getContentResolver().unregisterContentObserver(keyserverStatusObserver); } public static class KeyserverStatus { From 66465f7c1b6e622ffda0a6ed6c9b6876378e522f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 20 Jul 2017 21:30:47 +0200 Subject: [PATCH 08/11] better handling of unknown status --- .../keychain/ui/keyview/loader/KeyserverStatusLoader.java | 7 +++++-- .../keychain/ui/keyview/view/KeyserverStatusView.java | 2 +- OpenKeychain/src/main/res/values/strings.xml | 5 +++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/KeyserverStatusLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/KeyserverStatusLoader.java index cb6bd71ba..2ae535acf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/KeyserverStatusLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/KeyserverStatusLoader.java @@ -68,9 +68,12 @@ public class KeyserverStatusLoader extends AsyncTaskLoader { try { if (cursor.moveToFirst()) { + if (cursor.isNull(INDEX_SEEN_ON_KEYSERVERS) || cursor.isNull(INDEX_LAST_UPDATED)) { + return new KeyserverStatus(masterKeyId); + } + boolean isPublished = cursor.getInt(INDEX_SEEN_ON_KEYSERVERS) != 0; - Date lastUpdated = cursor.isNull(INDEX_LAST_UPDATED) ? - null : new Date(cursor.getLong(INDEX_LAST_UPDATED) * 1000); + Date lastUpdated = new Date(cursor.getLong(INDEX_LAST_UPDATED) * 1000); return new KeyserverStatus(masterKeyId, isPublished, lastUpdated); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/view/KeyserverStatusView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/view/KeyserverStatusView.java index c6ab221bf..0d0e3491e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/view/KeyserverStatusView.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/view/KeyserverStatusView.java @@ -88,7 +88,7 @@ public class KeyserverStatusView extends FrameLayout implements KeyserverStatusM @Override public void setDisplayStatusUnknown() { setDisplayStatus(KeyserverDisplayStatus.UNKNOWN); - vSubtitle.setVisibility(View.GONE); + vSubtitle.setText(R.string.keyserver_last_updated_never); } @Override diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 8b075b06e..680d27bac 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -1844,8 +1844,9 @@ "Published" "Not Published" - "Unknown" - "Last updated: %s" + "Keyserver status: Unknown" + "Last checked: %s" + Last checked: Never "This key uses the %1$s algorithm with a strength of %2$s bits. A secure key should have a strength of 2048 bits." "This key can\'t be upgraded. For secure communication, the owner must generate a new key." From d78c557cfad6c6495e4f4a3137bca4872f4d9c48 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 20 Jul 2017 21:41:56 +0200 Subject: [PATCH 09/11] correctly set keyserver status on first lookup --- .../keychain/provider/KeyRepository.java | 26 +++++++++++++++++++ .../provider/KeyWritableRepository.java | 6 +++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java index a21db2cce..5277cd393 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java @@ -280,6 +280,32 @@ public class KeyRepository { return lastUpdateTime; } + @Nullable + Boolean getSeenOnKeyservers(long masterKeyId) { + Cursor cursor = mContentResolver.query( + UpdatedKeys.CONTENT_URI, + new String[] { UpdatedKeys.SEEN_ON_KEYSERVERS }, + UpdatedKeys.MASTER_KEY_ID + " = ?", + new String[] { "" + masterKeyId }, + null + ); + if (cursor == null) { + return null; + } + + Boolean seenOnKeyservers; + try { + if (!cursor.moveToNext()) { + return null; + } + seenOnKeyservers = cursor.isNull(0) ? null : cursor.getInt(0) != 0; + } finally { + cursor.close(); + } + return seenOnKeyservers; + } + + public final byte[] loadPublicKeyRingData(long masterKeyId) throws NotFoundException { byte[] data = (byte[]) getGenericDataOrNull(KeyRingData.buildPublicKeyRingUri(masterKeyId), KeyRingData.KEY_RING_DATA, FIELD_TYPE_BLOB); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java index 74b667e91..5adb992fd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java @@ -1349,11 +1349,13 @@ public class KeyWritableRepository extends KeyRepository { } public Uri renewKeyLastUpdatedTime(long masterKeyId, boolean seenOnKeyservers) { + boolean isFirstKeyserverStatusCheck = getSeenOnKeyservers(masterKeyId) == null; + ContentValues values = new ContentValues(); values.put(UpdatedKeys.MASTER_KEY_ID, masterKeyId); values.put(UpdatedKeys.LAST_UPDATED, GregorianCalendar.getInstance().getTimeInMillis() / 1000); - if (seenOnKeyservers) { - values.put(UpdatedKeys.SEEN_ON_KEYSERVERS, true); + if (seenOnKeyservers || isFirstKeyserverStatusCheck) { + values.put(UpdatedKeys.SEEN_ON_KEYSERVERS, seenOnKeyservers); } // this will actually update/replace, doing the right thing™ for seenOnKeyservers value From ef6921210c315b9a07c1316f39a8a57d468a2f57 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 24 Jul 2017 11:42:57 +0200 Subject: [PATCH 10/11] add seen on keyservers column to create table statement --- .../sufficientlysecure/keychain/provider/KeychainDatabase.java | 1 + 1 file changed, 1 insertion(+) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 04a2cf356..8c45264ab 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -151,6 +151,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { "CREATE TABLE IF NOT EXISTS " + Tables.UPDATED_KEYS + " (" + UpdatedKeysColumns.MASTER_KEY_ID + " INTEGER PRIMARY KEY, " + UpdatedKeysColumns.LAST_UPDATED + " INTEGER, " + + UpdatedKeysColumns.SEEN_ON_KEYSERVERS + " INTEGER, " + "FOREIGN KEY(" + UpdatedKeysColumns.MASTER_KEY_ID + ") REFERENCES " + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE" + ")"; From bff6b98e0bcb1a8d236829ab8de811cfdc2b0e40 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 24 Jul 2017 12:11:56 +0200 Subject: [PATCH 11/11] reset last updated and keyserver status on change of keyserver preference --- .../keyimport/ParcelableHkpKeyserver.java | 23 ++++++++++ .../provider/KeyWritableRepository.java | 7 +++ .../keychain/provider/KeychainProvider.java | 13 ++++++ .../ui/SettingsKeyserverFragment.java | 46 +++++++++++++------ 4 files changed, 74 insertions(+), 15 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableHkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableHkpKeyserver.java index 05a058f5b..5a29d2a70 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableHkpKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableHkpKeyserver.java @@ -466,6 +466,29 @@ public class ParcelableHkpKeyserver extends Keyserver implements Parcelable { return getHostID(); } + @Override + public boolean equals(Object obj) { + if (! (obj instanceof ParcelableHkpKeyserver)) { + return false; + } + + ParcelableHkpKeyserver other = (ParcelableHkpKeyserver) obj; + if (other.mUrl == null ^ mUrl == null) { + return false; + } + if (other.mOnion == null ^ mOnion == null) { + return false; + } + if (mUrl != null && !mUrl.equals(other.mUrl)) { + return false; + } + if (mOnion != null && !mOnion.equals(other.mOnion)) { + return false; + } + + return true; + } + /** * Tries to find a server responsible for a given domain * diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java index 5adb992fd..7004a367f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java @@ -1363,4 +1363,11 @@ public class KeyWritableRepository extends KeyRepository { return mContentResolver.insert(UpdatedKeys.CONTENT_URI, values); } + public void resetAllLastUpdatedTimes() { + ContentValues values = new ContentValues(); + values.putNull(UpdatedKeys.LAST_UPDATED); + values.putNull(UpdatedKeys.SEEN_ON_KEYSERVERS); + mContentResolver.update(UpdatedKeys.CONTENT_URI, values, null, null); + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 5d5916211..2e8177d88 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -916,6 +916,19 @@ public class KeychainProvider extends ContentProvider { buildDefaultApiAppsSelection(uri, selection), selectionArgs); break; } + case UPDATED_KEYS: { + if (values.size() != 2 || + !values.containsKey(UpdatedKeys.SEEN_ON_KEYSERVERS) || + !values.containsKey(UpdatedKeys.LAST_UPDATED) || + values.get(UpdatedKeys.LAST_UPDATED) != null || + values.get(UpdatedKeys.SEEN_ON_KEYSERVERS) != null || + selection != null || selectionArgs != null) { + throw new UnsupportedOperationException("can only reset all keys"); + } + + db.update(Tables.UPDATED_KEYS, values, null, null); + break; + } default: { throw new UnsupportedOperationException("Unknown uri: " + uri); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java index 2175b9af9..fd5feef87 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java @@ -18,6 +18,11 @@ package org.sufficientlysecure.keychain.ui; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -38,6 +43,8 @@ import android.widget.ImageView; import android.widget.TextView; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; +import org.sufficientlysecure.keychain.provider.KeyWritableRepository; import org.sufficientlysecure.keychain.ui.dialog.AddEditKeyserverDialogFragment; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; @@ -45,20 +52,20 @@ import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperAdapt import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperDragCallback; import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperViewHolder; import org.sufficientlysecure.keychain.ui.util.recyclerview.RecyclerItemClickListener; -import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.util.Preferences; -import java.util.ArrayList; -import java.util.Collections; public class SettingsKeyserverFragment extends Fragment implements RecyclerItemClickListener.OnItemClickListener { private static final String ARG_KEYSERVER_ARRAY = "arg_keyserver_array"; private ItemTouchHelper mItemTouchHelper; - private ArrayList mKeyservers; + private ArrayList mKeyserversMutable; + private List mKeyservers; private KeyserverListAdapter mAdapter; + private KeyWritableRepository databaseReadWriteInteractor; + public static SettingsKeyserverFragment newInstance(ArrayList keyservers) { Bundle args = new Bundle(); args.putParcelableArrayList(ARG_KEYSERVER_ARRAY, keyservers); @@ -72,6 +79,7 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + databaseReadWriteInteractor = KeyWritableRepository.createDatabaseReadWriteInteractor(getContext()); return inflater.inflate(R.layout.settings_keyserver_fragment, null); } @@ -80,9 +88,10 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - mKeyservers = getArguments().getParcelableArrayList(ARG_KEYSERVER_ARRAY); + mKeyserversMutable = getArguments().getParcelableArrayList(ARG_KEYSERVER_ARRAY); + mKeyservers = Collections.unmodifiableList(new ArrayList<>(mKeyserversMutable)); - mAdapter = new KeyserverListAdapter(mKeyservers); + mAdapter = new KeyserverListAdapter(mKeyserversMutable); RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.keyserver_recycler_view); // recyclerView.setHasFixedSize(true); // the size of the first item changes @@ -143,7 +152,7 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC if (deleted) { Notify.create(getActivity(), getActivity().getString( - R.string.keyserver_preference_deleted, mKeyservers.get(position)), + R.string.keyserver_preference_deleted, mKeyserversMutable.get(position)), Notify.Style.OK) .show(); deleteKeyserver(position); @@ -187,27 +196,27 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC } private void addKeyserver(ParcelableHkpKeyserver keyserver) { - mKeyservers.add(keyserver); - mAdapter.notifyItemInserted(mKeyservers.size() - 1); + mKeyserversMutable.add(keyserver); + mAdapter.notifyItemInserted(mKeyserversMutable.size() - 1); saveKeyserverList(); } private void editKeyserver(ParcelableHkpKeyserver newKeyserver, int position) { - mKeyservers.set(position, newKeyserver); + mKeyserversMutable.set(position, newKeyserver); mAdapter.notifyItemChanged(position); saveKeyserverList(); } private void deleteKeyserver(int position) { - if (mKeyservers.size() == 1) { + if (mKeyserversMutable.size() == 1) { Notify.create(getActivity(), R.string.keyserver_preference_cannot_delete_last, Notify.Style.ERROR).show(); return; } - mKeyservers.remove(position); + mKeyserversMutable.remove(position); // we use this mAdapter.notifyItemRemoved(position); - if (position == 0 && mKeyservers.size() > 0) { + if (position == 0 && mKeyserversMutable.size() > 0) { // if we deleted the first item, we need the adapter to redraw the new first item mAdapter.notifyItemChanged(0); } @@ -215,13 +224,20 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC } private void saveKeyserverList() { - Preferences.getPreferences(getActivity()).setKeyServers(mKeyservers); + if (mKeyserversMutable.equals(mKeyservers)) { + return; + } + + Preferences.getPreferences(getActivity()).setKeyServers(mKeyserversMutable); + mKeyservers = Collections.unmodifiableList(new ArrayList<>(mKeyserversMutable)); + + databaseReadWriteInteractor.resetAllLastUpdatedTimes(); } @Override public void onItemClick(View view, int position) { startEditKeyserverDialog(AddEditKeyserverDialogFragment.DialogAction.EDIT, - mKeyservers.get(position), position); + mKeyserversMutable.get(position), position); } public class KeyserverListAdapter extends RecyclerView.Adapter