From 2d1ff8cdcc703686b9e857129bf51cdc0ff79f1f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 24 Jun 2018 00:02:05 +0200 Subject: [PATCH] use LiveData in ViewKeyAdvActivity --- .../keychain/model/SubKey.java | 4 + .../keychain/provider/KeyRepository.java | 17 +- .../keychain/provider/KeychainContract.java | 8 - .../remote/ApiPendingIntentFactory.java | 3 +- .../keychain/ui/DecryptFragment.java | 3 +- .../keychain/ui/DecryptListFragment.java | 3 +- .../keychain/ui/KeyListFragment.java | 5 +- .../keychain/ui/UploadKeyActivity.java | 34 +- .../keychain/ui/ViewKeyAdvActivity.java | 314 +++++++--------- .../keychain/ui/ViewKeyAdvShareFragment.java | 338 +++++------------- .../ui/ViewKeyAdvSubkeysFragment.java | 176 ++------- .../ui/ViewKeyAdvUserIdsFragment.java | 174 ++------- .../keychain/ui/ViewKeyKeybaseFragment.java | 34 +- .../ui/adapter/ImportKeysAdapter.java | 15 +- .../keychain/ui/keyview/ViewKeyActivity.java | 102 +++--- .../linked/LinkedIdCreateGithubFragment.java | 9 +- .../ui/token/ManageSecurityTokenFragment.java | 6 +- .../keychain/util/ContactHelper.java | 4 +- .../org/sufficientlysecure/keychain/Keys.sq | 19 +- 19 files changed, 409 insertions(+), 859 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/SubKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/SubKey.java index 36102c73c..5505c39c4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/SubKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/SubKey.java @@ -54,5 +54,9 @@ public abstract class SubKey implements KeysModel { } return autocryptPackageNames; } + + public boolean has_auth_key() { + return has_auth_key_int() != 0; + } } } 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 aa51707f4..a88937e11 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java @@ -254,16 +254,19 @@ public class KeyRepository extends AbstractDao { return mapAllRows(query, KeyRingPublic.FACTORY.selectAllMasterKeyIdsMapper()::map); } - public List getUnifiedKeyInfo() { - SqlDelightQuery query = SubKey.FACTORY.selectAllUnifiedKeyInfo(); - List result = new ArrayList<>(); + public UnifiedKeyInfo getUnifiedKeyInfo(long masterKeyId) { + SqlDelightQuery query = SubKey.FACTORY.selectUnifiedKeyInfoByMasterKeyId(masterKeyId); try (Cursor cursor = getReadableDb().query(query)) { - while (cursor.moveToNext()) { - UnifiedKeyInfo unifiedKeyInfo = SubKey.UNIFIED_KEY_INFO_MAPPER.map(cursor); - result.add(unifiedKeyInfo); + if (cursor.moveToNext()) { + return SubKey.UNIFIED_KEY_INFO_MAPPER.map(cursor); } + return null; } - return result; + } + + public List getAllUnifiedKeyInfo() { + SqlDelightQuery query = SubKey.FACTORY.selectAllUnifiedKeyInfo(); + return mapAllRows(query, SubKey.UNIFIED_KEY_INFO_MAPPER::map); } public List getUserIds(long... masterKeyIds) { 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 7c07174d8..661d8efab 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -135,14 +135,6 @@ public class KeychainContract { return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).build(); } - public static Uri buildGenericKeyRingUri(String masterKeyId) { - return CONTENT_URI.buildUpon().appendPath(masterKeyId).build(); - } - - public static Uri buildGenericKeyRingUri(Uri uri) { - return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).build(); - } - public static Uri buildUnifiedKeyRingUri(long masterKeyId) { return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)) .appendPath(PATH_UNIFIED).build(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ApiPendingIntentFactory.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ApiPendingIntentFactory.java index 4f135f14f..7de402e69 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ApiPendingIntentFactory.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ApiPendingIntentFactory.java @@ -130,8 +130,7 @@ public class ApiPendingIntentFactory { } PendingIntent createShowKeyPendingIntent(Intent data, long masterKeyId) { - Intent intent = new Intent(mContext, ViewKeyActivity.class); - intent.setData(KeychainContract.KeyRings.buildGenericKeyRingUri(masterKeyId)); + Intent intent = ViewKeyActivity.getViewKeyActivityIntent(mContext, masterKeyId); return createInternal(data, intent); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java index fe9eaf710..8387ff951 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java @@ -195,12 +195,11 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager. private void showKey(long keyId) { try { - Intent viewKeyIntent = new Intent(getActivity(), ViewKeyActivity.class); KeyRepository keyRepository = KeyRepository.create(requireContext()); long masterKeyId = keyRepository.getCachedPublicKeyRing( KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId) ).getMasterKeyId(); - viewKeyIntent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId)); + Intent viewKeyIntent = ViewKeyActivity.getViewKeyActivityIntent(requireActivity(), masterKeyId); startActivity(viewKeyIntent); } catch (PgpKeyNotFoundException e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java index b1aaecc20..8e2e9604f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java @@ -1043,8 +1043,7 @@ public class DecryptListFragment if (activity == null) { return; } - Intent intent = new Intent(activity, ViewKeyActivity.class); - intent.setData(KeyRings.buildUnifiedKeyRingUri(keyId)); + Intent intent = ViewKeyActivity.getViewKeyActivityIntent(requireActivity(), keyId); activity.startActivity(intent); } }); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 357eb44fd..871a7335b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -267,7 +267,7 @@ public class KeyListFragment extends RecyclerFragment asyncLoadData() { - List unifiedKeyInfo = keyRepository.getUnifiedKeyInfo(); + List unifiedKeyInfo = keyRepository.getAllUnifiedKeyInfo(); return flexibleKeyItemFactory.mapUnifiedKeyInfoToFlexibleKeyItems(unifiedKeyInfo); } } @@ -421,8 +421,7 @@ public class KeyListFragment extends RecyclerFragment mUploadOpHelper; @@ -82,14 +75,6 @@ public class UploadKeyActivity extends BaseActivity uploadKey(); } }); - - mDataUri = getIntent().getData(); - if (mDataUri == null) { - Timber.e("Intent data missing. Should be Uri of key!"); - finish(); - return; - } - } private String[] getKeyserversArray() { @@ -126,19 +111,6 @@ public class UploadKeyActivity extends BaseActivity mUploadOpHelper.cryptoOperation(); } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: { - Intent viewIntent = NavUtils.getParentActivityIntent(this); - viewIntent.setData(KeychainContract.KeyRings.buildGenericKeyRingUri(mDataUri)); - NavUtils.navigateUpTo(this, viewIntent); - return true; - } - } - return super.onOptionsItemSelected(item); - } - @Override public UploadKeyringParcel createOperationInput() { long[] masterKeyIds = getIntent().getLongArrayExtra(MultiUserIdsFragment.EXTRA_KEY_IDS); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java index 55fc4da39..b79645823 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java @@ -18,16 +18,19 @@ package org.sufficientlysecure.keychain.ui; +import java.util.List; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.arch.lifecycle.LiveData; +import android.arch.lifecycle.Transformations; +import android.arch.lifecycle.ViewModel; +import android.arch.lifecycle.ViewModelProviders; +import android.content.Context; import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; import android.os.Bundle; -import android.provider.ContactsContract; -import android.support.v4.app.LoaderManager.LoaderCallbacks; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; +import android.support.annotation.StringRes; +import android.support.v4.app.Fragment; import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager.OnPageChangeListener; import android.view.ActionMode; @@ -37,83 +40,154 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewPropertyAnimator; import android.view.animation.OvershootInterpolator; -import android.widget.Toast; import com.astuetz.PagerSlidingTabStrip; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.livedata.GenericLiveData; +import org.sufficientlysecure.keychain.model.SubKey; +import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; +import org.sufficientlysecure.keychain.model.UserPacket.UserId; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.provider.KeyRepository; -import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; import org.sufficientlysecure.keychain.ui.base.BaseActivity; import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.util.ContactHelper; -import timber.log.Timber; -public class ViewKeyAdvActivity extends BaseActivity implements - LoaderCallbacks, OnPageChangeListener { - - KeyRepository mKeyRepository; - - protected Uri mDataUri; - +public class ViewKeyAdvActivity extends BaseActivity implements OnPageChangeListener { + public static final String EXTRA_MASTER_KEY_ID = "master_key_id"; public static final String EXTRA_SELECTED_TAB = "selected_tab"; - public static final int TAB_START = 0; - public static final int TAB_SHARE = 1; - public static final int TAB_IDENTITIES = 2; - public static final int TAB_SUBKEYS = 3; - public static final int TAB_CERTS = 4; + + KeyRepository keyRepository; // view private ViewPager mViewPager; private PagerSlidingTabStrip mSlidingTabLayout; - private static final int LOADER_ID_UNIFIED = 0; private ActionMode mActionMode; - private boolean mHasSecret; - private PagerTabStripAdapter mTabAdapter; + private boolean hasSecret; private boolean mActionIconShown; - private boolean[] mTabsWithActionMode; + + enum ViewKeyAdvTab { + START(ViewKeyAdvStartFragment.class, R.string.key_view_tab_start, false), + SHARE(ViewKeyAdvShareFragment.class, R.string.key_view_tab_share, false), + IDENTITIES(ViewKeyAdvUserIdsFragment.class, R.string.section_user_ids, true), + SUBKEYS(ViewKeyAdvSubkeysFragment.class, R.string.key_view_tab_keys, true); + + public final Class fragmentClass; + public final int titleRes; + public final boolean hasActionMode; + + ViewKeyAdvTab(Class fragmentClass, @StringRes int titleRes, boolean hasActionMode) { + this.titleRes = titleRes; + this.fragmentClass = fragmentClass; + this.hasActionMode = hasActionMode; + } + } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setFullScreenDialogClose(new View.OnClickListener() { - @Override - public void onClick(View v) { - finish(); - } - }); + setFullScreenDialogClose(v -> finish()); - mKeyRepository = KeyRepository.create(this); + keyRepository = KeyRepository.create(this); mViewPager = findViewById(R.id.pager); mSlidingTabLayout = findViewById(R.id.sliding_tab_layout); - mDataUri = getIntent().getData(); - if (mDataUri == null) { - Timber.e("Data missing. Should be uri of key!"); - finish(); + if (!getIntent().hasExtra(EXTRA_MASTER_KEY_ID)) { + throw new IllegalArgumentException("Missing required extra master_key_id"); + } + + ViewKeyAdvViewModel viewModel = ViewModelProviders.of(this).get(ViewKeyAdvViewModel.class); + viewModel.setMasterKeyId(getIntent().getLongExtra(EXTRA_MASTER_KEY_ID, 0L)); + viewModel.getUnifiedKeyInfoLiveData(getApplicationContext()).observe(this, this::onLoadUnifiedKeyInfo); + + initTabs(); + } + + public static class ViewKeyAdvViewModel extends ViewModel { + private Long masterKeyId; + private LiveData unifiedKeyInfoLiveData; + private LiveData> subKeyLiveData; + private LiveData> userIdsLiveData; + + void setMasterKeyId(long masterKeyId) { + if (this.masterKeyId != null) { + throw new IllegalStateException("cannot change masterKeyId once set!"); + } + this.masterKeyId = masterKeyId; + } + + LiveData getUnifiedKeyInfoLiveData(Context context) { + if (masterKeyId == null) { + throw new IllegalStateException("masterKeyId must be set to retrieve this!"); + } + if (unifiedKeyInfoLiveData == null) { + KeyRepository keyRepository = KeyRepository.create(context); + unifiedKeyInfoLiveData = new GenericLiveData<>(context, null, + () -> keyRepository.getUnifiedKeyInfo(masterKeyId)); + } + return unifiedKeyInfoLiveData; + } + + LiveData> getSubkeyLiveData(Context context) { + if (subKeyLiveData == null) { + KeyRepository keyRepository = KeyRepository.create(context); + subKeyLiveData = Transformations.switchMap(getUnifiedKeyInfoLiveData(context), + (unifiedKeyInfo) -> new GenericLiveData<>(context, null, + () -> keyRepository.getSubKeysByMasterKeyId(unifiedKeyInfo.master_key_id()))); + } + return subKeyLiveData; + } + + LiveData> getUserIdLiveData(Context context) { + if (userIdsLiveData == null) { + KeyRepository keyRepository = KeyRepository.create(context); + userIdsLiveData = Transformations.switchMap(getUnifiedKeyInfoLiveData(context), + (unifiedKeyInfo) -> new GenericLiveData<>(context, null, + () -> keyRepository.getUserIds(unifiedKeyInfo.master_key_id()))); + } + return userIdsLiveData; + } + } + + public void onLoadUnifiedKeyInfo(UnifiedKeyInfo unifiedKeyInfo) { + if (unifiedKeyInfo == null) { return; } - if (mDataUri.getHost().equals(ContactsContract.AUTHORITY)) { - mDataUri = new ContactHelper(this).dataUriFromContactUri(mDataUri); - if (mDataUri == null) { - Timber.e("Contact Data missing. Should be uri of key!"); - Toast.makeText(this, R.string.error_contacts_key_id_missing, Toast.LENGTH_LONG).show(); - finish(); - return; - } + + if (unifiedKeyInfo.name() != null) { + setTitle(unifiedKeyInfo.name()); + } else { + setTitle(R.string.user_id_no_name); } - // Prepare the loaders. Either re-connect with an existing ones, - // or start new ones. - getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); + String formattedKeyId = KeyFormattingUtils.beautifyKeyIdWithPrefix(unifiedKeyInfo.master_key_id()); + mToolbar.setSubtitle(formattedKeyId); - initTabs(mDataUri); + hasSecret = unifiedKeyInfo.has_any_secret(); + + // Note: order is important + int color; + if (unifiedKeyInfo.is_revoked() || unifiedKeyInfo.is_expired()) { + color = getResources().getColor(R.color.key_flag_red); + } else if (unifiedKeyInfo.has_any_secret()) { + color = getResources().getColor(R.color.android_green_light); + } else { + if (unifiedKeyInfo.is_verified()) { + color = getResources().getColor(R.color.android_green_light); + } else { + color = getResources().getColor(R.color.key_flag_orange); + } + } + mToolbar.setBackgroundColor(color); + mStatusBar.setBackgroundColor(ViewKeyActivity.getStatusBarBackgroundColor(color)); + mSlidingTabLayout.setBackgroundColor(color); + + invalidateOptionsMenu(); } @Override @@ -121,34 +195,13 @@ public class ViewKeyAdvActivity extends BaseActivity implements setContentView(R.layout.view_key_adv_activity); } - private void initTabs(Uri dataUri) { - mTabAdapter = new PagerTabStripAdapter(this); - mViewPager.setAdapter(mTabAdapter); + private void initTabs() { + PagerTabStripAdapter tabAdapter = new PagerTabStripAdapter(this); + mViewPager.setAdapter(tabAdapter); - // keep track which of these are action mode enabled! - mTabsWithActionMode = new boolean[4]; - - mTabAdapter.addTab(ViewKeyAdvStartFragment.class, - null, getString(R.string.key_view_tab_start)); - mTabsWithActionMode[0] = false; - - Bundle shareBundle = new Bundle(); - shareBundle.putParcelable(ViewKeyAdvShareFragment.ARG_DATA_URI, dataUri); - mTabAdapter.addTab(ViewKeyAdvShareFragment.class, - shareBundle, getString(R.string.key_view_tab_share)); - mTabsWithActionMode[1] = false; - - Bundle userIdsBundle = new Bundle(); - userIdsBundle.putParcelable(ViewKeyAdvUserIdsFragment.ARG_DATA_URI, dataUri); - mTabAdapter.addTab(ViewKeyAdvUserIdsFragment.class, - userIdsBundle, getString(R.string.section_user_ids)); - mTabsWithActionMode[2] = true; - - Bundle keysBundle = new Bundle(); - keysBundle.putParcelable(ViewKeyAdvSubkeysFragment.ARG_DATA_URI, dataUri); - mTabAdapter.addTab(ViewKeyAdvSubkeysFragment.class, - keysBundle, getString(R.string.key_view_tab_keys)); - mTabsWithActionMode[3] = true; + for (ViewKeyAdvTab tab : ViewKeyAdvTab.values()) { + tabAdapter.addTab(tab.fragmentClass, null, getString(tab.titleRes)); + } // update layout after operations mSlidingTabLayout.setViewPager(mViewPager); @@ -156,108 +209,8 @@ public class ViewKeyAdvActivity extends BaseActivity implements // switch to tab selected by extra Intent intent = getIntent(); - int switchToTab = intent.getIntExtra(EXTRA_SELECTED_TAB, TAB_START); + int switchToTab = intent.getIntExtra(EXTRA_SELECTED_TAB, 0); mViewPager.setCurrentItem(switchToTab); - - } - - // These are the rows that we will retrieve. - static final String[] PROJECTION = new String[]{ - KeychainContract.KeyRings._ID, - KeychainContract.KeyRings.MASTER_KEY_ID, - KeychainContract.KeyRings.USER_ID, - KeychainContract.KeyRings.IS_REVOKED, - KeychainContract.KeyRings.IS_EXPIRED, - KeychainContract.KeyRings.VERIFIED, - KeychainContract.KeyRings.HAS_ANY_SECRET, - KeychainContract.KeyRings.FINGERPRINT, - KeychainContract.KeyRings.NAME, - KeychainContract.KeyRings.EMAIL, - KeychainContract.KeyRings.COMMENT, - }; - - static final int INDEX_MASTER_KEY_ID = 1; - static final int INDEX_USER_ID = 2; - static final int INDEX_IS_REVOKED = 3; - static final int INDEX_IS_EXPIRED = 4; - static final int INDEX_VERIFIED = 5; - static final int INDEX_HAS_ANY_SECRET = 6; - static final int INDEX_FINGERPRINT = 7; - static final int INDEX_NAME = 8; - static final int INDEX_EMAIL = 9; - static final int INDEX_COMMENT = 10; - - @Override - public Loader onCreateLoader(int id, Bundle args) { - switch (id) { - case LOADER_ID_UNIFIED: { - Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri); - return new CursorLoader(this, baseUri, PROJECTION, null, null, null); - } - - default: - return null; - } - } - - @Override - public void onLoadFinished(Loader loader, Cursor data) { - // Avoid NullPointerExceptions... - if (data == null || data.getCount() == 0) { - return; - } - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - switch (loader.getId()) { - case LOADER_ID_UNIFIED: { - if (data.moveToFirst()) { - // get name, email, and comment from USER_ID - String name = data.getString(INDEX_NAME); - - if (name != null) { - setTitle(name); - } else { - setTitle(R.string.user_id_no_name); - } - - byte[] fingerprint = data.getBlob(INDEX_FINGERPRINT); - - // get key id from MASTER_KEY_ID - long masterKeyId = data.getLong(INDEX_MASTER_KEY_ID); - String formattedKeyId = KeyFormattingUtils.beautifyKeyIdWithPrefix(masterKeyId); - getSupportActionBar().setSubtitle(formattedKeyId); - - mHasSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0; - boolean isRevoked = data.getInt(INDEX_IS_REVOKED) > 0; - boolean isExpired = data.getInt(INDEX_IS_EXPIRED) != 0; - boolean isVerified = data.getInt(INDEX_VERIFIED) > 0; - - // Note: order is important - int color; - if (isRevoked || isExpired) { - color = getResources().getColor(R.color.key_flag_red); - } else if (mHasSecret) { - color = getResources().getColor(R.color.android_green_light); - } else { - if (isVerified) { - color = getResources().getColor(R.color.android_green_light); - } else { - color = getResources().getColor(R.color.key_flag_orange); - } - } - mToolbar.setBackgroundColor(color); - mStatusBar.setBackgroundColor(ViewKeyActivity.getStatusBarBackgroundColor(color)); - mSlidingTabLayout.setBackgroundColor(color); - - break; - } - } - } - } - - @Override - public void onLoaderReset(Loader loader) { - } @Override @@ -273,8 +226,7 @@ public class ViewKeyAdvActivity extends BaseActivity implements @Override public boolean onCreateOptionsMenu(Menu menu) { - - if (!mHasSecret) { + if (!hasSecret) { return false; } @@ -282,7 +234,7 @@ public class ViewKeyAdvActivity extends BaseActivity implements getMenuInflater().inflate(R.menu.action_mode_edit, menu); final MenuItem vActionModeItem = menu.findItem(R.id.menu_action_mode_edit); - boolean isCurrentActionFragment = mTabsWithActionMode[mViewPager.getCurrentItem()]; + boolean isCurrentActionFragment = ViewKeyAdvTab.values()[mViewPager.getCurrentItem()].hasActionMode; // if the state is as it should be, never mind if (isCurrentActionFragment == mActionIconShown) { @@ -298,7 +250,6 @@ public class ViewKeyAdvActivity extends BaseActivity implements } private void animateMenuItem(final MenuItem vEditSubkeys, final boolean animateShow) { - View actionView = LayoutInflater.from(this).inflate(R.layout.edit_icon, null); vEditSubkeys.setActionView(actionView); actionView.setTranslationX(animateShow ? 150 : 0); @@ -317,7 +268,6 @@ public class ViewKeyAdvActivity extends BaseActivity implements } }); animator.start(); - } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java index f83ecfc88..bf2370ecb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java @@ -18,45 +18,50 @@ package org.sufficientlysecure.keychain.ui; +import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.security.NoSuchAlgorithmException; + import android.app.Activity; import android.app.ActivityOptions; +import android.arch.lifecycle.LiveData; +import android.arch.lifecycle.Transformations; +import android.arch.lifecycle.ViewModelProviders; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; -import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.PorterDuff; import android.net.Uri; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.ParcelFileDescriptor; +import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; import android.support.v7.widget.CardView; import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnLayoutChangeListener; import android.view.ViewGroup; import android.view.animation.AlphaAnimation; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; -import org.openintents.openpgp.util.OpenPgpUtils; + import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.livedata.GenericLiveData; +import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey; -import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.SshPublicKey; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; -import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.TemporaryFileProvider; +import org.sufficientlysecure.keychain.ui.ViewKeyAdvActivity.ViewKeyAdvViewModel; import org.sufficientlysecure.keychain.ui.base.LoaderFragment; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; @@ -65,28 +70,13 @@ import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.ui.util.QrCodeUtils; import timber.log.Timber; -import java.io.BufferedWriter; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.security.NoSuchAlgorithmException; - -public class ViewKeyAdvShareFragment extends LoaderFragment implements - LoaderManager.LoaderCallbacks { - - public static final String ARG_DATA_URI = "uri"; - +public class ViewKeyAdvShareFragment extends LoaderFragment { private ImageView mQrCode; private CardView mQrCodeLayout; private TextView mFingerprintView; - private static final int LOADER_ID_UNIFIED = 0; - - private Uri mDataUri; - - private byte[] mFingerprint; - private String mUserId; private Bitmap mQrCodeBitmapCache; + private UnifiedKeyInfo unifiedKeyInfo; @Override public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { @@ -102,34 +92,24 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements // just calls requestLayout when it is finished, this way the loader and // background task are disconnected from any layouting the ImageView may // undergo. Please note how these six lines are perfectly right-aligned. - mQrCode.addOnLayoutChangeListener(new OnLayoutChangeListener() { - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, - int oldRight, - int oldBottom) { - // bitmap scaling is expensive, avoid doing it if we already have the correct size! - int mCurrentWidth = 0, mCurrentHeight = 0; - if (mQrCodeBitmapCache != null) { - if (mCurrentWidth == mQrCode.getWidth() && mCurrentHeight == mQrCode.getHeight()) { - return; - } - mCurrentWidth = mQrCode.getWidth(); - mCurrentHeight = mQrCode.getHeight(); - // scale the image up to our actual size. we do this in code rather - // than let the ImageView do this because we don't require filtering. - Bitmap scaled = Bitmap.createScaledBitmap(mQrCodeBitmapCache, - mCurrentWidth, mCurrentHeight, false); - mQrCode.setImageBitmap(scaled); + mQrCode.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { + // bitmap scaling is expensive, avoid doing it if we already have the correct size! + int mCurrentWidth = 0, mCurrentHeight = 0; + if (mQrCodeBitmapCache != null) { + if (mCurrentWidth == mQrCode.getWidth() && mCurrentHeight == mQrCode.getHeight()) { + return; } + mCurrentWidth = mQrCode.getWidth(); + mCurrentHeight = mQrCode.getHeight(); + // scale the image up to our actual size. we do this in code rather + // than let the ImageView do this because we don't require filtering. + Bitmap scaled = Bitmap.createScaledBitmap(mQrCodeBitmapCache, + mCurrentWidth, mCurrentHeight, false); + mQrCode.setImageBitmap(scaled); } }); mQrCodeLayout = view.findViewById(R.id.view_key_qr_code_layout); - mQrCodeLayout.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - showQrCodeDialog(); - } - }); + mQrCodeLayout.setOnClickListener(v -> showQrCodeDialog()); View vFingerprintShareButton = view.findViewById(R.id.view_key_action_fingerprint_share); View vFingerprintClipboardButton = view.findViewById(R.id.view_key_action_fingerprint_clipboard); @@ -139,106 +119,43 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements View vKeySshShareButton = view.findViewById(R.id.view_key_action_key_ssh_share); View vKeySshClipboardButton = view.findViewById(R.id.view_key_action_key_ssh_clipboard); View vKeyUploadButton = view.findViewById(R.id.view_key_action_upload); - vKeySafeSlingerButton.setColorFilter(FormattingUtils.getColorFromAttr(getActivity(), R.attr.colorTertiaryText), + vKeySafeSlingerButton.setColorFilter(FormattingUtils.getColorFromAttr(requireContext(), R.attr.colorTertiaryText), PorterDuff.Mode.SRC_IN); - vFingerprintShareButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - shareFingerprint(false); - } - }); - vFingerprintClipboardButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - shareFingerprint(true); - } - }); - vKeyShareButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - shareKey(false, false); - } - }); - vKeyClipboardButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - shareKey(true, false); - } - }); + vFingerprintShareButton.setOnClickListener(v -> shareFingerprint(false)); + vFingerprintClipboardButton.setOnClickListener(v -> shareFingerprint(true)); + vKeyShareButton.setOnClickListener(v -> shareKey(false, false)); + vKeyClipboardButton.setOnClickListener(v -> shareKey(true, false)); - vKeySafeSlingerButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - startSafeSlinger(mDataUri); - } - }); - vKeySshShareButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - shareKey(false, true); - } - }); - vKeySshClipboardButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - shareKey(true, true); - } - }); - vKeyUploadButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - uploadToKeyserver(); - } - }); + vKeySafeSlingerButton.setOnClickListener(v -> startSafeSlinger()); + vKeySshShareButton.setOnClickListener(v -> shareKey(false, true)); + vKeySshClipboardButton.setOnClickListener(v -> shareKey(true, true)); + vKeyUploadButton.setOnClickListener(v -> uploadToKeyserver()); return root; } - private void startSafeSlinger(Uri dataUri) { - long keyId = 0; - try { - keyId = KeyRepository.create(getContext()) - .getCachedPublicKeyRing(dataUri) - .extractOrGetMasterKeyId(); - } catch (PgpKeyNotFoundException e) { - Timber.e(e, "key not found!"); - } + private void startSafeSlinger() { Intent safeSlingerIntent = new Intent(getActivity(), SafeSlingerActivity.class); - safeSlingerIntent.putExtra(SafeSlingerActivity.EXTRA_MASTER_KEY_ID, keyId); + safeSlingerIntent.putExtra(SafeSlingerActivity.EXTRA_MASTER_KEY_ID, unifiedKeyInfo.master_key_id()); startActivityForResult(safeSlingerIntent, 0); } - private boolean hasAuthenticationKey() { - KeyRepository keyRepository = KeyRepository.create(getContext()); - long masterKeyId = Constants.key.none; - long authSubKeyId = Constants.key.none; - try { - masterKeyId = keyRepository.getCachedPublicKeyRing(mDataUri).extractOrGetMasterKeyId(); - CachedPublicKeyRing cachedPublicKeyRing = keyRepository.getCachedPublicKeyRing(masterKeyId); - authSubKeyId = cachedPublicKeyRing.getAuthenticationId(); - } catch (PgpKeyNotFoundException e) { - Timber.e(e, "key not found!"); - } - return authSubKeyId != Constants.key.none; - } - private String getShareKeyContent(boolean asSshKey) throws PgpKeyNotFoundException, KeyRepository.NotFoundException, IOException, PgpGeneralException, NoSuchAlgorithmException { - KeyRepository keyRepository = KeyRepository.create(getContext()); + KeyRepository keyRepository = KeyRepository.create(requireContext()); String content; - long masterKeyId = keyRepository.getCachedPublicKeyRing(mDataUri).extractOrGetMasterKeyId(); if (asSshKey) { - long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getAuthenticationId(); - CanonicalizedPublicKey publicKey = keyRepository.getCanonicalizedPublicKeyRing(masterKeyId) + long authSubKeyId = keyRepository.getCachedPublicKeyRing(unifiedKeyInfo.master_key_id()).getAuthenticationId(); + CanonicalizedPublicKey publicKey = keyRepository.getCanonicalizedPublicKeyRing(unifiedKeyInfo.master_key_id()) .getPublicKey(authSubKeyId); SshPublicKey sshPublicKey = new SshPublicKey(publicKey); content = sshPublicKey.getEncodedKey(); } else { - content = keyRepository.getPublicKeyRingAsArmoredString(masterKeyId); + content = keyRepository.getPublicKeyRingAsArmoredString(unifiedKeyInfo.master_key_id()); } return content; @@ -246,10 +163,10 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements private void shareKey(boolean toClipboard, boolean asSshKey) { Activity activity = getActivity(); - if (activity == null || mFingerprint == null) { + if (activity == null || unifiedKeyInfo == null) { return; } - if (asSshKey && !hasAuthenticationKey()) { + if (asSshKey && !unifiedKeyInfo.has_auth_key()) { Notify.create(activity, R.string.authentication_subkey_not_found, Style.ERROR).show(); return; } @@ -281,10 +198,11 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements try { TemporaryFileProvider shareFileProv = new TemporaryFileProvider(); - String filename = KeyFormattingUtils.convertFingerprintToHex(mFingerprint); - OpenPgpUtils.UserId mainUserId = KeyRing.splitUserId(mUserId); - if (mainUserId.name != null) { - filename = mainUserId.name; + String filename; + if (unifiedKeyInfo.name() != null) { + filename = unifiedKeyInfo.name(); + } else { + filename = KeyFormattingUtils.convertFingerprintToHex(unifiedKeyInfo.fingerprint()); } Uri contentUri = TemporaryFileProvider.createFile(activity, filename + Constants.FILE_EXTENSION_ASC); @@ -316,12 +234,12 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements private void shareFingerprint(boolean toClipboard) { Activity activity = getActivity(); - if (activity == null || mFingerprint == null) { + if (activity == null || unifiedKeyInfo == null) { return; } String content; - String fingerprint = KeyFormattingUtils.convertFingerprintToHex(mFingerprint); + String fingerprint = KeyFormattingUtils.convertFingerprintToHex(unifiedKeyInfo.fingerprint()); if (!toClipboard) { content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint; } else { @@ -365,141 +283,63 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements opts = options.toBundle(); } - qrCodeIntent.setData(mDataUri); - ActivityCompat.startActivity(getActivity(), qrCodeIntent, opts); + qrCodeIntent.setData(KeyRings.buildUnifiedKeyRingUri(unifiedKeyInfo.master_key_id())); + ActivityCompat.startActivity(requireActivity(), qrCodeIntent, opts); } @Override - public void onViewCreated(View view, Bundle savedInstanceState) { + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); - if (dataUri == null) { - Timber.e("Data missing. Should be Uri of key!"); - getActivity().finish(); + ViewKeyAdvViewModel viewModel = ViewModelProviders.of(requireActivity()).get(ViewKeyAdvViewModel.class); + LiveData unifiedKeyInfoLiveData = viewModel.getUnifiedKeyInfoLiveData(requireContext()); + unifiedKeyInfoLiveData.observe(this, this::onLoadUnifiedKeyInfo); + + LiveData qrCodeLiveData = Transformations.switchMap(unifiedKeyInfoLiveData, + (unifiedKeyInfo) -> new GenericLiveData<>(getContext(), null, + () -> { + String fingerprintHex = KeyFormattingUtils.convertFingerprintToHex(unifiedKeyInfo.fingerprint()); + Uri uri = new Uri.Builder().scheme(Constants.FINGERPRINT_SCHEME).opaquePart(fingerprintHex).build(); + // render with minimal size + return QrCodeUtils.getQRCodeBitmap(uri, 0); + } + )); + qrCodeLiveData.observe(this, this::onLoadQrCode); + } + + public void onLoadUnifiedKeyInfo(UnifiedKeyInfo unifiedKeyInfo) { + if (unifiedKeyInfo == null) { return; } - loadData(dataUri); - } + this.unifiedKeyInfo = unifiedKeyInfo; - private void loadData(Uri dataUri) { - mDataUri = dataUri; + final String fingerprint = KeyFormattingUtils.convertFingerprintToHex(unifiedKeyInfo.fingerprint()); + mFingerprintView.setText(KeyFormattingUtils.formatFingerprint(fingerprint)); - // Prepare the loaders. Either re-connect with an existing ones, - // or start new ones. - getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); - } - - static final String[] UNIFIED_PROJECTION = new String[]{ - KeyRings._ID, KeyRings.FINGERPRINT, KeyRings.USER_ID - }; - - static final int INDEX_UNIFIED_FINGERPRINT = 1; - static final int INDEX_UNIFIED_USER_ID = 2; - - public Loader onCreateLoader(int id, Bundle args) { - setContentShown(false); - switch (id) { - case LOADER_ID_UNIFIED: { - Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri); - return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null); - } - - default: - return null; - } - } - - public void onLoadFinished(Loader loader, Cursor data) { - // Avoid NullPointerExceptions... - if (data == null || data.getCount() == 0) { - return; - } - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - switch (loader.getId()) { - case LOADER_ID_UNIFIED: { - if (data.moveToFirst()) { - - byte[] fingerprintBlob = data.getBlob(INDEX_UNIFIED_FINGERPRINT); - setFingerprint(fingerprintBlob); - - mUserId = data.getString(INDEX_UNIFIED_USER_ID); - - break; - } - } - - } setContentShown(true); } - /** - * This is called when the last Cursor provided to onLoadFinished() above is about to be closed. - * We need to make sure we are no longer using it. - */ - public void onLoaderReset(Loader loader) { - mFingerprint = null; - mQrCodeBitmapCache = null; - } - - /** - * Load QR Code asynchronously and with a fade in animation - */ - private void setFingerprint(byte[] fingerprintBlob) { - mFingerprint = fingerprintBlob; - - final String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fingerprintBlob); - mFingerprintView.setText(KeyFormattingUtils.formatFingerprint(fingerprint)); - + private void onLoadQrCode(Bitmap qrCode) { if (mQrCodeBitmapCache != null) { return; } - AsyncTask loadTask = - new AsyncTask() { - protected Bitmap doInBackground(Void... unused) { - Uri uri = new Uri.Builder() - .scheme(Constants.FINGERPRINT_SCHEME) - .opaquePart(fingerprint) - .build(); - // render with minimal size - return QrCodeUtils.getQRCodeBitmap(uri, 0); - } + mQrCodeBitmapCache = qrCode; + if (ViewKeyAdvShareFragment.this.isAdded()) { + mQrCode.requestLayout(); - protected void onPostExecute(Bitmap qrCode) { - // cache for later, and if we are attached request re-layout - mQrCodeBitmapCache = qrCode; - - if (ViewKeyAdvShareFragment.this.isAdded()) { - mQrCode.requestLayout(); - - // simple fade-in animation - AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f); - anim.setDuration(200); - mQrCode.startAnimation(anim); - } - } - }; - - loadTask.execute(); + // simple fade-in animation + AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f); + anim.setDuration(200); + mQrCode.startAnimation(anim); + } } private void uploadToKeyserver() { - long keyId; - try { - keyId = KeyRepository.create(getContext()) - .getCachedPublicKeyRing(mDataUri) - .extractOrGetMasterKeyId(); - } catch (PgpKeyNotFoundException e) { - Timber.e(e, "key not found!"); - Notify.create(getActivity(), "key not found", Style.ERROR).show(); - return; - } Intent uploadIntent = new Intent(getActivity(), UploadKeyActivity.class); - uploadIntent.setData(mDataUri); - uploadIntent.putExtra(MultiUserIdsFragment.EXTRA_KEY_IDS, new long[]{keyId}); + uploadIntent.setData(KeyRings.buildUnifiedKeyRingUri(unifiedKeyInfo.master_key_id())); + uploadIntent.putExtra(MultiUserIdsFragment.EXTRA_KEY_IDS, new long[]{ unifiedKeyInfo.master_key_id() }); startActivityForResult(uploadIntent, 0); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java index 1d5eff9fe..3ffb268c1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java @@ -20,38 +20,31 @@ package org.sufficientlysecure.keychain.ui; import java.util.List; -import android.arch.lifecycle.LiveData; +import android.arch.lifecycle.ViewModelProviders; import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.support.v4.app.FragmentActivity; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; import android.view.ActionMode; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; import android.widget.ListView; import android.widget.ViewAnimator; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; -import org.sufficientlysecure.keychain.livedata.GenericLiveData; import org.sufficientlysecure.keychain.model.SubKey; +import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; import org.sufficientlysecure.keychain.operations.results.EditKeyResult; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; -import org.sufficientlysecure.keychain.provider.KeyRepository; -import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange; +import org.sufficientlysecure.keychain.ui.ViewKeyAdvActivity.ViewKeyAdvViewModel; import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter; import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; @@ -59,16 +52,9 @@ import org.sufficientlysecure.keychain.ui.base.LoaderFragment; import org.sufficientlysecure.keychain.ui.dialog.AddSubkeyDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyExpiryDialogFragment; -import timber.log.Timber; -public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements - LoaderManager.LoaderCallbacks { - - public static final String ARG_DATA_URI = "data_uri"; - - private static final int LOADER_ID_UNIFIED = 0; - +public class ViewKeyAdvSubkeysFragment extends LoaderFragment { private ListView mSubkeysList; private ListView mSubkeysAddedList; private View mSubkeysAddedLayout; @@ -79,11 +65,8 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements private CryptoOperationHelper mEditKeyHelper; - private Uri mDataUri; - - private long mMasterKeyId; - private byte[] mFingerprint; private SaveKeyringParcel.Builder mEditModeSkpBuilder; + private UnifiedKeyInfo unifiedKeyInfo; @Override public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { @@ -94,12 +77,7 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements mSubkeysAddedList = view.findViewById(R.id.view_key_subkeys_added); mSubkeysAddedLayout = view.findViewById(R.id.view_key_subkeys_add_layout); - mSubkeysList.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - editSubkey(position); - } - }); + mSubkeysList.setOnItemClickListener((parent, view1, position, id) -> editSubkey(position)); View footer = new View(getActivity()); int spacing = (int) android.util.TypedValue.applyDimension( @@ -113,12 +91,7 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements mSubkeysAddedList.addFooterView(footer, null, false); mSubkeyAddFabLayout = view.findViewById(R.id.view_key_subkey_fab_layout); - view.findViewById(R.id.view_key_subkey_fab).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - addSubkey(); - } - }); + view.findViewById(R.id.view_key_subkey_fab).setOnClickListener(v -> addSubkey()); setHasOptionsMenu(true); @@ -129,16 +102,32 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); - if (dataUri == null) { - Timber.e("Data missing. Should be Uri of key!"); - getActivity().finish(); + // Create an empty adapter we will use to display the loaded data. + mSubkeysAdapter = new SubkeysAdapter(requireContext()); + mSubkeysList.setAdapter(mSubkeysAdapter); + + ViewKeyAdvViewModel viewModel = ViewModelProviders.of(requireActivity()).get(ViewKeyAdvViewModel.class); + viewModel.getUnifiedKeyInfoLiveData(requireContext()).observe(this, this::onLoadFinished); + viewModel.getSubkeyLiveData(requireContext()).observe(this, this::onLoadSubKeys); + + setContentShown(false); + } + + public void onLoadFinished(UnifiedKeyInfo unifiedKeyInfo) { + // Avoid NullPointerExceptions, if we get an empty result set. + if (unifiedKeyInfo == null) { return; } - loadData(dataUri); + this.unifiedKeyInfo = unifiedKeyInfo; } + private void onLoadSubKeys(List subKeys) { + mSubkeysAdapter.setData(subKeys); + setContentShown(true); + } + + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (mEditKeyHelper != null) { @@ -148,82 +137,6 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements super.onActivityResult(requestCode, resultCode, data); } - private void loadData(Uri dataUri) { - mDataUri = dataUri; - - // Create an empty adapter we will use to display the loaded data. - mSubkeysAdapter = new SubkeysAdapter(requireContext()); - mSubkeysList.setAdapter(mSubkeysAdapter); - - // Prepare the loaders. Either re-connect with an existing ones, - // or start new ones. - getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); - - setContentShown(false); - } - - // These are the rows that we will retrieve. - static final String[] PROJECTION = new String[]{ - KeychainContract.KeyRings._ID, - KeychainContract.KeyRings.MASTER_KEY_ID, - KeychainContract.KeyRings.HAS_ANY_SECRET, - KeychainContract.KeyRings.FINGERPRINT, - }; - - static final int INDEX_MASTER_KEY_ID = 1; - static final int INDEX_HAS_ANY_SECRET = 2; - static final int INDEX_FINGERPRINT = 3; - - @Override - public Loader onCreateLoader(int id, Bundle args) { - switch (id) { - case LOADER_ID_UNIFIED: { - Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri); - return new CursorLoader(getActivity(), baseUri, - PROJECTION, null, null, null); - } - - default: - return null; - } - } - - public void onLoadFinished(Loader loader, Cursor data) { - // Avoid NullPointerExceptions, if we get an empty result set. - if (data.getCount() == 0) { - return; - } - - switch (loader.getId()) { - case LOADER_ID_UNIFIED: { - data.moveToFirst(); - - mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID); - mFingerprint = data.getBlob(INDEX_FINGERPRINT); - - KeyRepository keyRepository = KeyRepository.create(requireContext()); - LiveData> subKeyLiveData = new GenericLiveData<>(requireContext(), null, - () -> keyRepository.getSubKeysByMasterKeyId(mMasterKeyId)); - subKeyLiveData.observe(this, this::onLoadSubKeys); - break; - } - } - - } - - private void onLoadSubKeys(List subKeys) { - mSubkeysAdapter.setData(subKeys); - setContentShown(true); - } - - /** - * This is called when the last Cursor provided to onLoadFinished() above is about to be closed. - * We need to make sure we are no longer using it. - */ - public void onLoaderReset(Loader loader) { - - } - @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { @@ -244,7 +157,7 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { - mEditModeSkpBuilder = SaveKeyringParcel.buildChangeKeyringParcel(mMasterKeyId, mFingerprint); + mEditModeSkpBuilder = SaveKeyringParcel.buildChangeKeyringParcel(unifiedKeyInfo.master_key_id(), unifiedKeyInfo.fingerprint()); mSubkeysAddedAdapter = new SubkeysAddedAdapter( getActivity(), mEditModeSkpBuilder.getMutableAddSubKeys(), false); @@ -292,15 +205,8 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements AddSubkeyDialogFragment addSubkeyDialogFragment = AddSubkeyDialogFragment.newInstance(willBeMasterKey); addSubkeyDialogFragment - .setOnAlgorithmSelectedListener( - new AddSubkeyDialogFragment.OnAlgorithmSelectedListener() { - @Override - public void onAlgorithmSelected(SaveKeyringParcel.SubkeyAdd newSubkey) { - mSubkeysAddedAdapter.add(newSubkey); - } - } - ); - addSubkeyDialogFragment.show(getActivity().getSupportFragmentManager(), "addSubkeyDialog"); + .setOnAlgorithmSelectedListener(newSubkey -> mSubkeysAddedAdapter.add(newSubkey)); + addSubkeyDialogFragment.show(requireFragmentManager(), "addSubkeyDialog"); } private void editSubkey(final int position) { @@ -343,13 +249,11 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements // Create a new Messenger for the communication back final Messenger messenger = new Messenger(returnHandler); - DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { - public void run() { - EditSubkeyDialogFragment dialogFragment = - EditSubkeyDialogFragment.newInstance(messenger); + DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(() -> { + EditSubkeyDialogFragment dialogFragment = + EditSubkeyDialogFragment.newInstance(messenger); - dialogFragment.show(getActivity().getSupportFragmentManager(), "editSubkeyDialog"); - } + dialogFragment.show(requireFragmentManager(), "editSubkeyDialog"); }); } @@ -377,13 +281,11 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements // Create a new Messenger for the communication back final Messenger messenger = new Messenger(returnHandler); - DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { - public void run() { - EditSubkeyExpiryDialogFragment dialogFragment = - EditSubkeyExpiryDialogFragment.newInstance(messenger, creationDate, expiryDate); + DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(() -> { + EditSubkeyExpiryDialogFragment dialogFragment = + EditSubkeyExpiryDialogFragment.newInstance(messenger, creationDate, expiryDate); - dialogFragment.show(getActivity().getSupportFragmentManager(), "editSubkeyExpiryDialog"); - } + dialogFragment.show(requireFragmentManager(), "editSubkeyExpiryDialog"); }); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java index 9ce7058c8..98b6fe07a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java @@ -20,39 +20,30 @@ package org.sufficientlysecure.keychain.ui; import java.util.List; -import android.arch.lifecycle.LiveData; +import android.arch.lifecycle.ViewModelProviders; import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.support.v4.app.FragmentActivity; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; import android.view.ActionMode; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; import android.widget.ListView; import android.widget.ViewAnimator; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; -import org.sufficientlysecure.keychain.livedata.GenericLiveData; +import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; import org.sufficientlysecure.keychain.model.UserPacket.UserId; import org.sufficientlysecure.keychain.operations.results.EditKeyResult; -import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; -import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; -import org.sufficientlysecure.keychain.provider.KeyRepository; -import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.ui.ViewKeyAdvActivity.ViewKeyAdvViewModel; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAddedAdapter; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; @@ -61,16 +52,9 @@ import org.sufficientlysecure.keychain.ui.dialog.AddUserIdDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; -import timber.log.Timber; -public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements - LoaderManager.LoaderCallbacks { - - public static final String ARG_DATA_URI = "uri"; - - private static final int LOADER_ID_UNIFIED = 0; - +public class ViewKeyAdvUserIdsFragment extends LoaderFragment { private ListView mUserIds; private ListView mUserIdsAddedList; private View mUserIdsAddedLayout; @@ -81,12 +65,8 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements private CryptoOperationHelper mEditKeyHelper; - private Uri mDataUri; - - private long mMasterKeyId; - private byte[] mFingerprint; - private boolean mHasSecret; private SaveKeyringParcel.Builder mSkpBuilder; + private UnifiedKeyInfo unifiedKeyInfo; @Override public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { @@ -97,12 +77,7 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements mUserIdsAddedList = view.findViewById(R.id.view_key_user_ids_added); mUserIdsAddedLayout = view.findViewById(R.id.view_key_user_ids_add_layout); - mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - showOrEditUserIdInfo(position); - } - }); + mUserIds.setOnItemClickListener((parent, view1, position, id) -> showOrEditUserIdInfo(position)); View footer = new View(getActivity()); int spacing = (int) android.util.TypedValue.applyDimension( @@ -116,12 +91,7 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements mUserIdsAddedList.addFooterView(footer, null, false); mUserIdAddFabLayout = view.findViewById(R.id.view_key_subkey_fab_layout); - view.findViewById(R.id.view_key_subkey_fab).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - addUserId(); - } - }); + view.findViewById(R.id.view_key_subkey_fab).setOnClickListener(v -> addUserId()); setHasOptionsMenu(true); @@ -175,12 +145,10 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements // Create a new Messenger for the communication back final Messenger messenger = new Messenger(returnHandler); - DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { - public void run() { - EditUserIdDialogFragment dialogFragment = - EditUserIdDialogFragment.newInstance(messenger, isRevoked, isRevokedPending); - dialogFragment.show(getActivity().getSupportFragmentManager(), "editUserIdDialog"); - } + DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(() -> { + EditUserIdDialogFragment dialogFragment = + EditUserIdDialogFragment.newInstance(messenger, isRevoked, isRevokedPending); + dialogFragment.show(requireFragmentManager(), "editUserIdDialog"); }); } @@ -189,13 +157,11 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements final boolean isRevoked = mUserIdsAdapter.getIsRevoked(position); final boolean isVerified = mUserIdsAdapter.getIsVerified(position) == Certs.VERIFIED_SECRET; - DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { - public void run() { - UserIdInfoDialogFragment dialogFragment = - UserIdInfoDialogFragment.newInstance(isRevoked, isVerified); + DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(() -> { + UserIdInfoDialogFragment dialogFragment = + UserIdInfoDialogFragment.newInstance(isRevoked, isVerified); - dialogFragment.show(getActivity().getSupportFragmentManager(), "userIdInfoDialog"); - } + dialogFragment.show(requireFragmentManager(), "userIdInfoDialog"); }); } @@ -219,21 +185,33 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements // pre-fill out primary name AddUserIdDialogFragment addUserIdDialog = AddUserIdDialogFragment.newInstance(messenger, ""); - addUserIdDialog.show(getActivity().getSupportFragmentManager(), "addUserIdDialog"); + addUserIdDialog.show(requireFragmentManager(), "addUserIdDialog"); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); - if (dataUri == null) { - Timber.e("Data missing. Should be Uri of key!"); - getActivity().finish(); + mUserIdsAdapter = new UserIdsAdapter(getActivity(), false); + mUserIds.setAdapter(mUserIdsAdapter); + + ViewKeyAdvViewModel viewModel = ViewModelProviders.of(requireActivity()).get(ViewKeyAdvViewModel.class); + viewModel.getUnifiedKeyInfoLiveData(requireContext()).observe(this, this::onLoadUnifiedKeyInfo); + viewModel.getUserIdLiveData(requireContext()).observe(this, this::onLoadUserIds); + + setContentShown(false); + } + + public void onLoadUnifiedKeyInfo(UnifiedKeyInfo unifiedKeyInfo) { + if (unifiedKeyInfo == null) { return; } + this.unifiedKeyInfo = unifiedKeyInfo; + } - loadData(dataUri); + private void onLoadUserIds(List userIds) { + mUserIdsAdapter.setData(userIds); + setContentShown(true); } @Override @@ -245,88 +223,6 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements super.onActivityResult(requestCode, resultCode, data); } - private void loadData(Uri dataUri) { - mDataUri = dataUri; - - Timber.i("dataUri: " + mDataUri); - - mUserIdsAdapter = new UserIdsAdapter(getActivity(), false); - mUserIds.setAdapter(mUserIdsAdapter); - - // Prepare the loaders. Either re-connect with an existing ones, - // or start new ones. - getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); - - KeyRepository keyRepository = KeyRepository.create(getContext()); - try { - Uri uri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri); - CachedPublicKeyRing keyRing = keyRepository.getCachedPublicKeyRing(uri); - long masterKeyId = keyRing.getMasterKeyId(); - - LiveData> userIdLiveData = - new GenericLiveData<>(getContext(), null, () -> keyRepository.getUserIds(masterKeyId)); - userIdLiveData.observe(this, this::onUserIdsLoaded); - } catch (PgpKeyNotFoundException e) { - e.printStackTrace(); - } - } - - private void onUserIdsLoaded(List userIds) { - mUserIdsAdapter.setData(userIds); - setContentShown(true); - } - - // These are the rows that we will retrieve. - static final String[] PROJECTION = new String[]{ - KeychainContract.KeyRings._ID, - KeychainContract.KeyRings.MASTER_KEY_ID, - KeychainContract.KeyRings.HAS_ANY_SECRET, - KeychainContract.KeyRings.FINGERPRINT, - }; - - static final int INDEX_MASTER_KEY_ID = 1; - static final int INDEX_HAS_ANY_SECRET = 2; - static final int INDEX_FINGERPRINT = 3; - - public Loader onCreateLoader(int id, Bundle args) { - switch (id) { - case LOADER_ID_UNIFIED: { - Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri); - return new CursorLoader(getActivity(), baseUri, - PROJECTION, null, null, null); - } - - default: - return null; - } - } - - public void onLoadFinished(Loader loader, Cursor data) { - // Avoid NullPointerExceptions, if we get an empty result set. - if (data.getCount() == 0) { - return; - } - - switch (loader.getId()) { - case LOADER_ID_UNIFIED: { - data.moveToFirst(); - - mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID); - mHasSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0; - mFingerprint = data.getBlob(INDEX_FINGERPRINT); - break; - } - } - } - - /** - * This is called when the last Cursor provided to onLoadFinished() above is about to be closed. - * We need to make sure we are no longer using it. - */ - public void onLoaderReset(Loader loader) { - - } - @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { @@ -346,8 +242,8 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements activity.startActionMode(new ActionMode.Callback() { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { - - mSkpBuilder = SaveKeyringParcel.buildChangeKeyringParcel(mMasterKeyId, mFingerprint); + mSkpBuilder = SaveKeyringParcel.buildChangeKeyringParcel( + unifiedKeyInfo.master_key_id(), unifiedKeyInfo.fingerprint()); mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mSkpBuilder.getMutableAddUserIds(), false); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java index f483d09c9..3040a710e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java @@ -17,6 +17,11 @@ package org.sufficientlysecure.keychain.ui; + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; + import android.content.Intent; import android.database.Cursor; import android.graphics.Typeface; @@ -43,29 +48,23 @@ import com.textuality.keybase.lib.KeybaseException; import com.textuality.keybase.lib.KeybaseQuery; import com.textuality.keybase.lib.Proof; import com.textuality.keybase.lib.User; - import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.network.OkHttpKeybaseClient; +import org.sufficientlysecure.keychain.network.orbot.OrbotHelper; import org.sufficientlysecure.keychain.operations.results.KeybaseVerificationResult; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.ui.base.LoaderFragment; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.network.OkHttpKeybaseClient; import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.Preferences; -import org.sufficientlysecure.keychain.network.orbot.OrbotHelper; -import timber.log.Timber; - -import java.util.ArrayList; -import java.util.Hashtable; -import java.util.List; public class ViewKeyKeybaseFragment extends LoaderFragment implements LoaderManager.LoaderCallbacks, CryptoOperationHelper.Callback { - public static final String ARG_DATA_URI = "uri"; + public static final String ARG_MASTER_KEY_ID = "master_key_id"; private TextView mReportHeader; private TableLayout mProofListing; @@ -76,7 +75,7 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements private static final int LOADER_ID_DATABASE = 1; // for retrieving the key we’re working on - private Uri mDataUri; + private long masterKeyId; private Proof mProof; @@ -89,10 +88,10 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements /** * Creates new instance of this fragment */ - public static ViewKeyKeybaseFragment newInstance(Uri dataUri) { + public static ViewKeyKeybaseFragment newInstance(long masterKeyId) { ViewKeyKeybaseFragment frag = new ViewKeyKeybaseFragment(); Bundle args = new Bundle(); - args.putParcelable(ARG_DATA_URI, dataUri); + args.putLong(ARG_MASTER_KEY_ID, masterKeyId); frag.setArguments(args); @@ -121,13 +120,10 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); - if (dataUri == null) { - Timber.e("Data missing. Should be Uri of key!"); - getActivity().finish(); - return; + masterKeyId = getArguments().getLong(ARG_MASTER_KEY_ID); + if (masterKeyId == 0L) { + throw new IllegalArgumentException(); } - mDataUri = dataUri; // retrieve the key from the database getLoaderManager().initLoader(LOADER_ID_DATABASE, null, this); @@ -148,7 +144,7 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements switch (id) { case LOADER_ID_DATABASE: { - Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri); + Uri baseUri = KeyRings.buildUnifiedKeyRingUri(masterKeyId); return new CursorLoader(getActivity(), baseUri, TRUST_PROJECTION, null, null, null); } // decided to just use an AsyncTask for keybase, but maybe later diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index 633300e70..31e595161 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -18,6 +18,11 @@ package org.sufficientlysecure.keychain.ui.adapter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + import android.content.Intent; import android.databinding.DataBindingUtil; import android.support.v4.app.FragmentActivity; @@ -26,6 +31,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; + import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.databinding.ImportKeysListItemBinding; import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress; @@ -40,7 +46,6 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.KeyRepository; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.service.ImportKeyringParcel; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity; @@ -49,11 +54,6 @@ import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.ParcelableFileCache; import timber.log.Timber; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - public class ImportKeysAdapter extends RecyclerView.Adapter implements ImportKeysResultListener { @@ -182,8 +182,7 @@ public class ImportKeysAdapter extends RecyclerView.Adapter importOpHelper; private CryptoOperationHelper editOpHelper; @@ -168,6 +168,12 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements private long masterKeyId; private byte[] fingerprint; + public static Intent getViewKeyActivityIntent(@NonNull Context context, long masterKeyId) { + Intent viewIntent = new Intent(context, ViewKeyActivity.class); + viewIntent.putExtra(ViewKeyActivity.EXTRA_MASTER_KEY_ID, masterKeyId); + return viewIntent; + } + @SuppressLint("InflateParams") @Override protected void onCreate(Bundle savedInstanceState) { @@ -244,30 +250,29 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements }); refreshView = getLayoutInflater().inflate(R.layout.indeterminate_progress, null); - dataUri = getIntent().getData(); - if (dataUri == null) { - Timber.e("Data missing. Should be uri of key!"); - finish(); - return; - } - if (dataUri.getHost().equals(ContactsContract.AUTHORITY)) { - dataUri = new ContactHelper(this).dataUriFromContactUri(dataUri); - if (dataUri == null) { + Intent intent = getIntent(); + Uri dataUri = intent.getData(); + if (intent.hasExtra(EXTRA_MASTER_KEY_ID)) { + masterKeyId = intent.getLongExtra(EXTRA_MASTER_KEY_ID, 0L); + } else if (dataUri != null && dataUri.getHost().equals(ContactsContract.AUTHORITY)) { + Long contactMasterKeyId = new ContactHelper(this).masterKeyIdFromContactsDataUri(dataUri); + if (contactMasterKeyId == null) { Timber.e("Contact Data missing. Should be uri of key!"); Toast.makeText(this, R.string.error_contacts_key_id_missing, Toast.LENGTH_LONG).show(); finish(); return; } + masterKeyId = contactMasterKeyId; + } else { + throw new IllegalArgumentException("Missing required extra master_key_id or contact uri"); } - Timber.i("dataUri: " + dataUri); - - actionEncryptFile.setOnClickListener(v -> encrypt(dataUri, false)); - actionEncryptText.setOnClickListener(v -> encrypt(dataUri, true)); + actionEncryptFile.setOnClickListener(v -> encrypt(false)); + actionEncryptText.setOnClickListener(v -> encrypt(true)); floatingActionButton.setOnClickListener(v -> { if (isSecret) { - startSafeSlinger(dataUri); + startSafeSlinger(); } else { scanQrCode(); } @@ -279,8 +284,8 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements // or start new ones. getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); - if (savedInstanceState == null && getIntent().hasExtra(EXTRA_DISPLAY_RESULT)) { - OperationResult result = getIntent().getParcelableExtra(EXTRA_DISPLAY_RESULT); + if (savedInstanceState == null && intent.hasExtra(EXTRA_DISPLAY_RESULT)) { + OperationResult result = intent.getParcelableExtra(EXTRA_DISPLAY_RESULT); result.createNotify(this).show(); } @@ -291,7 +296,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements if (Preferences.getPreferences(this).getExperimentalEnableKeybase()) { FragmentManager manager = getSupportFragmentManager(); - final ViewKeyKeybaseFragment keybaseFrag = ViewKeyKeybaseFragment.newInstance(dataUri); + final ViewKeyKeybaseFragment keybaseFrag = ViewKeyKeybaseFragment.newInstance(masterKeyId); manager.beginTransaction() .replace(R.id.view_key_keybase_fragment, keybaseFrag) .commit(); @@ -341,7 +346,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements } case R.id.menu_key_view_advanced: { Intent advancedIntent = new Intent(this, ViewKeyAdvActivity.class); - advancedIntent.setData(dataUri); + advancedIntent.putExtra(ViewKeyAdvActivity.EXTRA_MASTER_KEY_ID, masterKeyId); startActivity(advancedIntent); return true; } @@ -350,7 +355,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements return true; } case R.id.menu_key_view_certify_fingerprint: { - certifyFingerprint(dataUri); + certifyFingerprint(); return true; } } @@ -440,9 +445,9 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements startActivityForResult(scanQrCode, REQUEST_QR_FINGERPRINT); } - private void certifyFingerprint(Uri dataUri) { + private void certifyFingerprint() { Intent intent = new Intent(this, CertifyFingerprintActivity.class); - intent.setData(dataUri); + intent.setData(KeyRings.buildUnifiedKeyRingUri(masterKeyId)); startActivityForResult(intent, REQUEST_CERTIFY); } @@ -466,7 +471,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements opts = options.toBundle(); } - qrCodeIntent.setData(dataUri); + qrCodeIntent.setData(KeyRings.buildUnifiedKeyRingUri(masterKeyId)); ActivityCompat.startActivity(this, qrCodeIntent, opts); } @@ -615,45 +620,30 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements }); } - private void encrypt(Uri dataUri, boolean text) { + private void encrypt(boolean text) { // If there is no encryption key, don't bother. if (!hasEncrypt) { Notify.create(this, R.string.error_no_encrypt_subkey, Notify.Style.ERROR).show(); return; } - try { - long keyId = KeyRepository.create(this) - .getCachedPublicKeyRing(dataUri) - .extractOrGetMasterKeyId(); - long[] encryptionKeyIds = new long[]{keyId}; - Intent intent; - if (text) { - intent = new Intent(this, EncryptTextActivity.class); - intent.setAction(EncryptTextActivity.ACTION_ENCRYPT_TEXT); - intent.putExtra(EncryptTextActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds); - } else { - intent = new Intent(this, EncryptFilesActivity.class); - intent.setAction(EncryptFilesActivity.ACTION_ENCRYPT_DATA); - intent.putExtra(EncryptFilesActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds); - } - // used instead of startActivity set actionbar based on callingPackage - startActivityForResult(intent, 0); - } catch (PgpKeyNotFoundException e) { - Timber.e(e, "key not found!"); + long[] encryptionKeyIds = new long[] { masterKeyId }; + Intent intent; + if (text) { + intent = new Intent(this, EncryptTextActivity.class); + intent.setAction(EncryptTextActivity.ACTION_ENCRYPT_TEXT); + intent.putExtra(EncryptTextActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds); + } else { + intent = new Intent(this, EncryptFilesActivity.class); + intent.setAction(EncryptFilesActivity.ACTION_ENCRYPT_DATA); + intent.putExtra(EncryptFilesActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds); } + // used instead of startActivity set actionbar based on callingPackage + startActivityForResult(intent, 0); } - private void startSafeSlinger(Uri dataUri) { - long keyId = 0; - try { - keyId = KeyRepository.create(this) - .getCachedPublicKeyRing(dataUri) - .extractOrGetMasterKeyId(); - } catch (PgpKeyNotFoundException e) { - Timber.e(e, "key not found!"); - } + private void startSafeSlinger() { Intent safeSlingerIntent = new Intent(this, SafeSlingerActivity.class); - safeSlingerIntent.putExtra(SafeSlingerActivity.EXTRA_MASTER_KEY_ID, keyId); + safeSlingerIntent.putExtra(SafeSlingerActivity.EXTRA_MASTER_KEY_ID, masterKeyId); startActivityForResult(safeSlingerIntent, 0); } @@ -727,7 +717,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements public Loader onCreateLoader(int id, Bundle args) { switch (id) { case LOADER_ID_UNIFIED: { - Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri); + Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(masterKeyId); return new CursorLoader(this, baseUri, PROJECTION, null, null, null); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java index 624a51055..bc6c79dae 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -17,6 +17,7 @@ package org.sufficientlysecure.keychain.ui.linked; + import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -61,19 +62,18 @@ import android.widget.TextView; import android.widget.ViewAnimator; import javax.net.ssl.HttpsURLConnection; +import org.bouncycastle.util.encoders.Hex; import org.json.JSONException; import org.json.JSONObject; -import org.bouncycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.BuildConfig; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.linked.LinkedAttribute; import org.sufficientlysecure.keychain.linked.resources.GithubResource; import org.sufficientlysecure.keychain.operations.results.EditKeyResult; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; -import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity; import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; +import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.ui.widget.StatusIndicator; @@ -433,8 +433,7 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment= Build.VERSION_CODES.LOLLIPOP) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenFragment.java index 7371e486d..bcbac6e14 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenFragment.java @@ -183,11 +183,7 @@ public class ManageSecurityTokenFragment extends Fragment implements ManageSecur public void finishAndShowKey(long masterKeyId) { Activity activity = getActivity(); - Intent viewKeyIntent = new Intent(activity, ViewKeyActivity.class); - // use the imported masterKeyId, not the one from the token, because - // that one might* just have been a subkey of the imported key - viewKeyIntent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId)); - + Intent viewKeyIntent = ViewKeyActivity.getViewKeyActivityIntent(requireActivity(), masterKeyId); if (activity instanceof CreateKeyActivity) { ((CreateKeyActivity) activity).finishWithFirstTimeHandling(viewKeyIntent); } else { 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 985880b75..5a57412d1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java @@ -322,7 +322,7 @@ public class ContactHelper { return new ArrayList<>(names); } - public Uri dataUriFromContactUri(Uri contactUri) { + public Long masterKeyIdFromContactsDataUri(Uri contactUri) { if (!isContactsPermissionGranted()) { return null; } @@ -332,7 +332,7 @@ public class ContactHelper { if (contactMasterKey != null) { try { if (contactMasterKey.moveToNext()) { - return KeychainContract.KeyRings.buildGenericKeyRingUri(contactMasterKey.getLong(0)); + return contactMasterKey.getLong(0); } } finally { contactMasterKey.close(); diff --git a/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/Keys.sq b/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/Keys.sq index c147c0d1f..6235a5b73 100644 --- a/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/Keys.sq +++ b/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/Keys.sq @@ -24,9 +24,10 @@ CREATE TABLE IF NOT EXISTS keys ( ); selectAllUnifiedKeyInfo: -SELECT keys.master_key_id, MIN(user_packets.rank), user_packets.name, user_packets.email, user_packets.comment, keys.creation, keys.expiry, keys.is_revoked, keys.is_secure, certs.verified, +SELECT keys.master_key_id, keys.fingerprint, MIN(user_packets.rank), user_packets.name, user_packets.email, user_packets.comment, keys.creation, keys.expiry, keys.is_revoked, keys.is_secure, certs.verified, (EXISTS (SELECT * FROM user_packets AS dups WHERE dups.master_key_id != keys.master_key_id AND dups.rank = 0 AND dups.name = user_packets.name COLLATE NOCASE AND dups.email = user_packets.email COLLATE NOCASE )) AS has_duplicate_int, - (EXISTS (SELECT * FROM keys AS k WHERE k.has_secret != 0 AND k.master_key_id = keys.master_key_id )) AS has_any_secret_int, + (EXISTS (SELECT * FROM keys AS k WHERE k.master_key_id = keys.master_key_id AND k.has_secret != 0 )) AS has_any_secret_int, + (EXISTS (SELECT * FROM keys AS k WHERE k.master_key_id = keys.master_key_id AND k.can_authenticate != 0 )) AS has_auth_key_int, GROUP_CONCAT(DISTINCT aTI.package_name) AS autocrypt_package_names_csv, GROUP_CONCAT(user_packets.user_id, '|||') AS user_id_list FROM keys @@ -37,6 +38,20 @@ SELECT keys.master_key_id, MIN(user_packets.rank), user_packets.name, user_packe GROUP BY keys.master_key_id ORDER BY has_secret DESC, user_packets.name COLLATE NOCASE ASC; +selectUnifiedKeyInfoByMasterKeyId: +SELECT keys.master_key_id, keys.fingerprint, MIN(user_packets.rank), user_packets.name, user_packets.email, user_packets.comment, keys.creation, keys.expiry, keys.is_revoked, keys.is_secure, certs.verified, + (EXISTS (SELECT * FROM user_packets AS dups WHERE dups.master_key_id != keys.master_key_id AND dups.rank = 0 AND dups.name = user_packets.name COLLATE NOCASE AND dups.email = user_packets.email COLLATE NOCASE )) AS has_duplicate_int, + (EXISTS (SELECT * FROM keys AS k WHERE k.master_key_id = keys.master_key_id AND k.has_secret != 0 )) AS has_any_secret_int, + (EXISTS (SELECT * FROM keys AS k WHERE k.master_key_id = keys.master_key_id AND k.can_authenticate != 0 )) AS has_auth_key_int, + GROUP_CONCAT(DISTINCT aTI.package_name) AS autocrypt_package_names_csv, + GROUP_CONCAT(user_packets.user_id, '|||') AS user_id_list + FROM keys + INNER JOIN user_packets ON ( keys.master_key_id = user_packets.master_key_id AND user_packets.type IS NULL ) + LEFT JOIN certs ON ( keys.master_key_id = certs.master_key_id AND certs.verified = 1 ) + LEFT JOIN autocrypt_peers AS aTI ON ( aTI.master_key_id = keys.master_key_id ) + WHERE keys.rank = 0 AND keys.master_key_id = ? + GROUP BY keys.master_key_id; + selectSubkeysByMasterKeyId: SELECT * FROM keys