use LiveData in ViewKeyAdvActivity

This commit is contained in:
Vincent Breitmoser
2018-06-24 00:02:05 +02:00
parent d57a409fac
commit 2d1ff8cdcc
19 changed files with 409 additions and 859 deletions

View File

@@ -54,5 +54,9 @@ public abstract class SubKey implements KeysModel {
} }
return autocryptPackageNames; return autocryptPackageNames;
} }
public boolean has_auth_key() {
return has_auth_key_int() != 0;
}
} }
} }

View File

@@ -254,16 +254,19 @@ public class KeyRepository extends AbstractDao {
return mapAllRows(query, KeyRingPublic.FACTORY.selectAllMasterKeyIdsMapper()::map); return mapAllRows(query, KeyRingPublic.FACTORY.selectAllMasterKeyIdsMapper()::map);
} }
public List<UnifiedKeyInfo> getUnifiedKeyInfo() { public UnifiedKeyInfo getUnifiedKeyInfo(long masterKeyId) {
SqlDelightQuery query = SubKey.FACTORY.selectAllUnifiedKeyInfo(); SqlDelightQuery query = SubKey.FACTORY.selectUnifiedKeyInfoByMasterKeyId(masterKeyId);
List<UnifiedKeyInfo> result = new ArrayList<>();
try (Cursor cursor = getReadableDb().query(query)) { try (Cursor cursor = getReadableDb().query(query)) {
while (cursor.moveToNext()) { if (cursor.moveToNext()) {
UnifiedKeyInfo unifiedKeyInfo = SubKey.UNIFIED_KEY_INFO_MAPPER.map(cursor); return SubKey.UNIFIED_KEY_INFO_MAPPER.map(cursor);
result.add(unifiedKeyInfo);
} }
return null;
} }
return result; }
public List<UnifiedKeyInfo> getAllUnifiedKeyInfo() {
SqlDelightQuery query = SubKey.FACTORY.selectAllUnifiedKeyInfo();
return mapAllRows(query, SubKey.UNIFIED_KEY_INFO_MAPPER::map);
} }
public List<UserId> getUserIds(long... masterKeyIds) { public List<UserId> getUserIds(long... masterKeyIds) {

View File

@@ -135,14 +135,6 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).build(); 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) { public static Uri buildUnifiedKeyRingUri(long masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)) return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId))
.appendPath(PATH_UNIFIED).build(); .appendPath(PATH_UNIFIED).build();

View File

@@ -130,8 +130,7 @@ public class ApiPendingIntentFactory {
} }
PendingIntent createShowKeyPendingIntent(Intent data, long masterKeyId) { PendingIntent createShowKeyPendingIntent(Intent data, long masterKeyId) {
Intent intent = new Intent(mContext, ViewKeyActivity.class); Intent intent = ViewKeyActivity.getViewKeyActivityIntent(mContext, masterKeyId);
intent.setData(KeychainContract.KeyRings.buildGenericKeyRingUri(masterKeyId));
return createInternal(data, intent); return createInternal(data, intent);
} }

View File

@@ -195,12 +195,11 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
private void showKey(long keyId) { private void showKey(long keyId) {
try { try {
Intent viewKeyIntent = new Intent(getActivity(), ViewKeyActivity.class);
KeyRepository keyRepository = KeyRepository.create(requireContext()); KeyRepository keyRepository = KeyRepository.create(requireContext());
long masterKeyId = keyRepository.getCachedPublicKeyRing( long masterKeyId = keyRepository.getCachedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId) KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId)
).getMasterKeyId(); ).getMasterKeyId();
viewKeyIntent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId)); Intent viewKeyIntent = ViewKeyActivity.getViewKeyActivityIntent(requireActivity(), masterKeyId);
startActivity(viewKeyIntent); startActivity(viewKeyIntent);
} catch (PgpKeyNotFoundException e) { } catch (PgpKeyNotFoundException e) {

View File

@@ -1043,8 +1043,7 @@ public class DecryptListFragment
if (activity == null) { if (activity == null) {
return; return;
} }
Intent intent = new Intent(activity, ViewKeyActivity.class); Intent intent = ViewKeyActivity.getViewKeyActivityIntent(requireActivity(), keyId);
intent.setData(KeyRings.buildUnifiedKeyRingUri(keyId));
activity.startActivity(intent); activity.startActivity(intent);
} }
}); });

View File

@@ -267,7 +267,7 @@ public class KeyListFragment extends RecyclerFragment<FlexibleAdapter<FlexibleKe
@Override @Override
protected List<FlexibleKeyItem> asyncLoadData() { protected List<FlexibleKeyItem> asyncLoadData() {
List<UnifiedKeyInfo> unifiedKeyInfo = keyRepository.getUnifiedKeyInfo(); List<UnifiedKeyInfo> unifiedKeyInfo = keyRepository.getAllUnifiedKeyInfo();
return flexibleKeyItemFactory.mapUnifiedKeyInfoToFlexibleKeyItems(unifiedKeyInfo); return flexibleKeyItemFactory.mapUnifiedKeyInfoToFlexibleKeyItems(unifiedKeyInfo);
} }
} }
@@ -421,8 +421,7 @@ public class KeyListFragment extends RecyclerFragment<FlexibleAdapter<FlexibleKe
} }
long masterKeyId = ((FlexibleKeyDetailsItem) item).keyInfo.master_key_id(); long masterKeyId = ((FlexibleKeyDetailsItem) item).keyInfo.master_key_id();
Intent viewIntent = new Intent(getActivity(), ViewKeyActivity.class); Intent viewIntent = ViewKeyActivity.getViewKeyActivityIntent(requireActivity(), masterKeyId);
viewIntent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
startActivityForResult(viewIntent, REQUEST_VIEW_KEY); startActivityForResult(viewIntent, REQUEST_VIEW_KEY);
return false; return false;
} }

View File

@@ -18,27 +18,22 @@
package org.sufficientlysecure.keychain.ui; package org.sufficientlysecure.keychain.ui;
import java.util.ArrayList;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.NavUtils;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.Spinner; import android.widget.Spinner;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress;
import org.sufficientlysecure.keychain.operations.results.UploadResult; import org.sufficientlysecure.keychain.operations.results.UploadResult;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.service.UploadKeyringParcel; import org.sufficientlysecure.keychain.service.UploadKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.BaseActivity; import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import timber.log.Timber;
import java.util.ArrayList;
/** /**
* Sends the selected public key to a keyserver * Sends the selected public key to a keyserver
@@ -48,8 +43,6 @@ public class UploadKeyActivity extends BaseActivity
private View mUploadButton; private View mUploadButton;
private Spinner mKeyServerSpinner; private Spinner mKeyServerSpinner;
private Uri mDataUri;
// CryptoOperationHelper.Callback vars // CryptoOperationHelper.Callback vars
private HkpKeyserverAddress mKeyserver; private HkpKeyserverAddress mKeyserver;
private CryptoOperationHelper<UploadKeyringParcel, UploadResult> mUploadOpHelper; private CryptoOperationHelper<UploadKeyringParcel, UploadResult> mUploadOpHelper;
@@ -82,14 +75,6 @@ public class UploadKeyActivity extends BaseActivity
uploadKey(); uploadKey();
} }
}); });
mDataUri = getIntent().getData();
if (mDataUri == null) {
Timber.e("Intent data missing. Should be Uri of key!");
finish();
return;
}
} }
private String[] getKeyserversArray() { private String[] getKeyserversArray() {
@@ -126,19 +111,6 @@ public class UploadKeyActivity extends BaseActivity
mUploadOpHelper.cryptoOperation(); 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 @Override
public UploadKeyringParcel createOperationInput() { public UploadKeyringParcel createOperationInput() {
long[] masterKeyIds = getIntent().getLongArrayExtra(MultiUserIdsFragment.EXTRA_KEY_IDS); long[] masterKeyIds = getIntent().getLongArrayExtra(MultiUserIdsFragment.EXTRA_KEY_IDS);

View File

@@ -18,16 +18,19 @@
package org.sufficientlysecure.keychain.ui; package org.sufficientlysecure.keychain.ui;
import java.util.List;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; 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.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.provider.ContactsContract; import android.support.annotation.StringRes;
import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.app.Fragment;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener; import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.view.ActionMode; import android.view.ActionMode;
@@ -37,83 +40,154 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewPropertyAnimator; import android.view.ViewPropertyAnimator;
import android.view.animation.OvershootInterpolator; import android.view.animation.OvershootInterpolator;
import android.widget.Toast;
import com.astuetz.PagerSlidingTabStrip; import com.astuetz.PagerSlidingTabStrip;
import org.sufficientlysecure.keychain.R; 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.operations.results.OperationResult;
import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.ui.base.BaseActivity; import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity; import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ContactHelper;
import timber.log.Timber;
public class ViewKeyAdvActivity extends BaseActivity implements public class ViewKeyAdvActivity extends BaseActivity implements OnPageChangeListener {
LoaderCallbacks<Cursor>, OnPageChangeListener { public static final String EXTRA_MASTER_KEY_ID = "master_key_id";
KeyRepository mKeyRepository;
protected Uri mDataUri;
public static final String EXTRA_SELECTED_TAB = "selected_tab"; public static final String EXTRA_SELECTED_TAB = "selected_tab";
public static final int TAB_START = 0;
public static final int TAB_SHARE = 1; KeyRepository keyRepository;
public static final int TAB_IDENTITIES = 2;
public static final int TAB_SUBKEYS = 3;
public static final int TAB_CERTS = 4;
// view // view
private ViewPager mViewPager; private ViewPager mViewPager;
private PagerSlidingTabStrip mSlidingTabLayout; private PagerSlidingTabStrip mSlidingTabLayout;
private static final int LOADER_ID_UNIFIED = 0;
private ActionMode mActionMode; private ActionMode mActionMode;
private boolean mHasSecret; private boolean hasSecret;
private PagerTabStripAdapter mTabAdapter;
private boolean mActionIconShown; 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<? extends Fragment> fragmentClass;
public final int titleRes;
public final boolean hasActionMode;
ViewKeyAdvTab(Class<? extends Fragment> fragmentClass, @StringRes int titleRes, boolean hasActionMode) {
this.titleRes = titleRes;
this.fragmentClass = fragmentClass;
this.hasActionMode = hasActionMode;
}
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setFullScreenDialogClose(new View.OnClickListener() { setFullScreenDialogClose(v -> finish());
@Override
public void onClick(View v) {
finish();
}
});
mKeyRepository = KeyRepository.create(this); keyRepository = KeyRepository.create(this);
mViewPager = findViewById(R.id.pager); mViewPager = findViewById(R.id.pager);
mSlidingTabLayout = findViewById(R.id.sliding_tab_layout); mSlidingTabLayout = findViewById(R.id.sliding_tab_layout);
mDataUri = getIntent().getData(); if (!getIntent().hasExtra(EXTRA_MASTER_KEY_ID)) {
if (mDataUri == null) { throw new IllegalArgumentException("Missing required extra master_key_id");
Timber.e("Data missing. Should be uri of key!"); }
finish();
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<UnifiedKeyInfo> unifiedKeyInfoLiveData;
private LiveData<List<SubKey>> subKeyLiveData;
private LiveData<List<UserId>> userIdsLiveData;
void setMasterKeyId(long masterKeyId) {
if (this.masterKeyId != null) {
throw new IllegalStateException("cannot change masterKeyId once set!");
}
this.masterKeyId = masterKeyId;
}
LiveData<UnifiedKeyInfo> 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<List<SubKey>> 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<List<UserId>> 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; return;
} }
if (mDataUri.getHost().equals(ContactsContract.AUTHORITY)) {
mDataUri = new ContactHelper(this).dataUriFromContactUri(mDataUri); if (unifiedKeyInfo.name() != null) {
if (mDataUri == null) { setTitle(unifiedKeyInfo.name());
Timber.e("Contact Data missing. Should be uri of key!"); } else {
Toast.makeText(this, R.string.error_contacts_key_id_missing, Toast.LENGTH_LONG).show(); setTitle(R.string.user_id_no_name);
finish();
return;
}
} }
// Prepare the loaders. Either re-connect with an existing ones, String formattedKeyId = KeyFormattingUtils.beautifyKeyIdWithPrefix(unifiedKeyInfo.master_key_id());
// or start new ones. mToolbar.setSubtitle(formattedKeyId);
getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
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 @Override
@@ -121,34 +195,13 @@ public class ViewKeyAdvActivity extends BaseActivity implements
setContentView(R.layout.view_key_adv_activity); setContentView(R.layout.view_key_adv_activity);
} }
private void initTabs(Uri dataUri) { private void initTabs() {
mTabAdapter = new PagerTabStripAdapter(this); PagerTabStripAdapter tabAdapter = new PagerTabStripAdapter(this);
mViewPager.setAdapter(mTabAdapter); mViewPager.setAdapter(tabAdapter);
// keep track which of these are action mode enabled! for (ViewKeyAdvTab tab : ViewKeyAdvTab.values()) {
mTabsWithActionMode = new boolean[4]; tabAdapter.addTab(tab.fragmentClass, null, getString(tab.titleRes));
}
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;
// update layout after operations // update layout after operations
mSlidingTabLayout.setViewPager(mViewPager); mSlidingTabLayout.setViewPager(mViewPager);
@@ -156,108 +209,8 @@ public class ViewKeyAdvActivity extends BaseActivity implements
// switch to tab selected by extra // switch to tab selected by extra
Intent intent = getIntent(); Intent intent = getIntent();
int switchToTab = intent.getIntExtra(EXTRA_SELECTED_TAB, TAB_START); int switchToTab = intent.getIntExtra(EXTRA_SELECTED_TAB, 0);
mViewPager.setCurrentItem(switchToTab); 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<Cursor> 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<Cursor> 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<Cursor> loader) {
} }
@Override @Override
@@ -273,8 +226,7 @@ public class ViewKeyAdvActivity extends BaseActivity implements
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
if (!hasSecret) {
if (!mHasSecret) {
return false; return false;
} }
@@ -282,7 +234,7 @@ public class ViewKeyAdvActivity extends BaseActivity implements
getMenuInflater().inflate(R.menu.action_mode_edit, menu); getMenuInflater().inflate(R.menu.action_mode_edit, menu);
final MenuItem vActionModeItem = menu.findItem(R.id.menu_action_mode_edit); 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 the state is as it should be, never mind
if (isCurrentActionFragment == mActionIconShown) { if (isCurrentActionFragment == mActionIconShown) {
@@ -298,7 +250,6 @@ public class ViewKeyAdvActivity extends BaseActivity implements
} }
private void animateMenuItem(final MenuItem vEditSubkeys, final boolean animateShow) { private void animateMenuItem(final MenuItem vEditSubkeys, final boolean animateShow) {
View actionView = LayoutInflater.from(this).inflate(R.layout.edit_icon, null); View actionView = LayoutInflater.from(this).inflate(R.layout.edit_icon, null);
vEditSubkeys.setActionView(actionView); vEditSubkeys.setActionView(actionView);
actionView.setTranslationX(animateShow ? 150 : 0); actionView.setTranslationX(animateShow ? 150 : 0);
@@ -317,7 +268,6 @@ public class ViewKeyAdvActivity extends BaseActivity implements
} }
}); });
animator.start(); animator.start();
} }
@Override @Override

View File

@@ -18,45 +18,50 @@
package org.sufficientlysecure.keychain.ui; 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.Activity;
import android.app.ActivityOptions; 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.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat; 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.support.v7.widget.CardView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.animation.AlphaAnimation; import android.view.animation.AlphaAnimation;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import org.openintents.openpgp.util.OpenPgpUtils;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; 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.CanonicalizedPublicKey;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.SshPublicKey; import org.sufficientlysecure.keychain.pgp.SshPublicKey;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.TemporaryFileProvider; 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.base.LoaderFragment;
import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; 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 org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
import timber.log.Timber; import timber.log.Timber;
import java.io.BufferedWriter; public class ViewKeyAdvShareFragment extends LoaderFragment {
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.security.NoSuchAlgorithmException;
public class ViewKeyAdvShareFragment extends LoaderFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
public static final String ARG_DATA_URI = "uri";
private ImageView mQrCode; private ImageView mQrCode;
private CardView mQrCodeLayout; private CardView mQrCodeLayout;
private TextView mFingerprintView; private TextView mFingerprintView;
private static final int LOADER_ID_UNIFIED = 0;
private Uri mDataUri;
private byte[] mFingerprint;
private String mUserId;
private Bitmap mQrCodeBitmapCache; private Bitmap mQrCodeBitmapCache;
private UnifiedKeyInfo unifiedKeyInfo;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { 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 // just calls requestLayout when it is finished, this way the loader and
// background task are disconnected from any layouting the ImageView may // background task are disconnected from any layouting the ImageView may
// undergo. Please note how these six lines are perfectly right-aligned. // undergo. Please note how these six lines are perfectly right-aligned.
mQrCode.addOnLayoutChangeListener(new OnLayoutChangeListener() { mQrCode.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
@Override // bitmap scaling is expensive, avoid doing it if we already have the correct size!
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int mCurrentWidth = 0, mCurrentHeight = 0;
int oldRight, if (mQrCodeBitmapCache != null) {
int oldBottom) { if (mCurrentWidth == mQrCode.getWidth() && mCurrentHeight == mQrCode.getHeight()) {
// bitmap scaling is expensive, avoid doing it if we already have the correct size! return;
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);
} }
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 = view.findViewById(R.id.view_key_qr_code_layout);
mQrCodeLayout.setOnClickListener(new View.OnClickListener() { mQrCodeLayout.setOnClickListener(v -> showQrCodeDialog());
@Override
public void onClick(View v) {
showQrCodeDialog();
}
});
View vFingerprintShareButton = view.findViewById(R.id.view_key_action_fingerprint_share); View vFingerprintShareButton = view.findViewById(R.id.view_key_action_fingerprint_share);
View vFingerprintClipboardButton = view.findViewById(R.id.view_key_action_fingerprint_clipboard); 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 vKeySshShareButton = view.findViewById(R.id.view_key_action_key_ssh_share);
View vKeySshClipboardButton = view.findViewById(R.id.view_key_action_key_ssh_clipboard); View vKeySshClipboardButton = view.findViewById(R.id.view_key_action_key_ssh_clipboard);
View vKeyUploadButton = view.findViewById(R.id.view_key_action_upload); 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); PorterDuff.Mode.SRC_IN);
vFingerprintShareButton.setOnClickListener(new View.OnClickListener() { vFingerprintShareButton.setOnClickListener(v -> shareFingerprint(false));
@Override vFingerprintClipboardButton.setOnClickListener(v -> shareFingerprint(true));
public void onClick(View v) { vKeyShareButton.setOnClickListener(v -> shareKey(false, false));
shareFingerprint(false); vKeyClipboardButton.setOnClickListener(v -> shareKey(true, 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);
}
});
vKeySafeSlingerButton.setOnClickListener(new View.OnClickListener() { vKeySafeSlingerButton.setOnClickListener(v -> startSafeSlinger());
@Override vKeySshShareButton.setOnClickListener(v -> shareKey(false, true));
public void onClick(View v) { vKeySshClipboardButton.setOnClickListener(v -> shareKey(true, true));
startSafeSlinger(mDataUri); vKeyUploadButton.setOnClickListener(v -> uploadToKeyserver());
}
});
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();
}
});
return root; return root;
} }
private void startSafeSlinger(Uri dataUri) { private void startSafeSlinger() {
long keyId = 0;
try {
keyId = KeyRepository.create(getContext())
.getCachedPublicKeyRing(dataUri)
.extractOrGetMasterKeyId();
} catch (PgpKeyNotFoundException e) {
Timber.e(e, "key not found!");
}
Intent safeSlingerIntent = new Intent(getActivity(), SafeSlingerActivity.class); 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); 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) private String getShareKeyContent(boolean asSshKey)
throws PgpKeyNotFoundException, KeyRepository.NotFoundException, IOException, PgpGeneralException, throws PgpKeyNotFoundException, KeyRepository.NotFoundException, IOException, PgpGeneralException,
NoSuchAlgorithmException { NoSuchAlgorithmException {
KeyRepository keyRepository = KeyRepository.create(getContext()); KeyRepository keyRepository = KeyRepository.create(requireContext());
String content; String content;
long masterKeyId = keyRepository.getCachedPublicKeyRing(mDataUri).extractOrGetMasterKeyId();
if (asSshKey) { if (asSshKey) {
long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getAuthenticationId(); long authSubKeyId = keyRepository.getCachedPublicKeyRing(unifiedKeyInfo.master_key_id()).getAuthenticationId();
CanonicalizedPublicKey publicKey = keyRepository.getCanonicalizedPublicKeyRing(masterKeyId) CanonicalizedPublicKey publicKey = keyRepository.getCanonicalizedPublicKeyRing(unifiedKeyInfo.master_key_id())
.getPublicKey(authSubKeyId); .getPublicKey(authSubKeyId);
SshPublicKey sshPublicKey = new SshPublicKey(publicKey); SshPublicKey sshPublicKey = new SshPublicKey(publicKey);
content = sshPublicKey.getEncodedKey(); content = sshPublicKey.getEncodedKey();
} else { } else {
content = keyRepository.getPublicKeyRingAsArmoredString(masterKeyId); content = keyRepository.getPublicKeyRingAsArmoredString(unifiedKeyInfo.master_key_id());
} }
return content; return content;
@@ -246,10 +163,10 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
private void shareKey(boolean toClipboard, boolean asSshKey) { private void shareKey(boolean toClipboard, boolean asSshKey) {
Activity activity = getActivity(); Activity activity = getActivity();
if (activity == null || mFingerprint == null) { if (activity == null || unifiedKeyInfo == null) {
return; return;
} }
if (asSshKey && !hasAuthenticationKey()) { if (asSshKey && !unifiedKeyInfo.has_auth_key()) {
Notify.create(activity, R.string.authentication_subkey_not_found, Style.ERROR).show(); Notify.create(activity, R.string.authentication_subkey_not_found, Style.ERROR).show();
return; return;
} }
@@ -281,10 +198,11 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
try { try {
TemporaryFileProvider shareFileProv = new TemporaryFileProvider(); TemporaryFileProvider shareFileProv = new TemporaryFileProvider();
String filename = KeyFormattingUtils.convertFingerprintToHex(mFingerprint); String filename;
OpenPgpUtils.UserId mainUserId = KeyRing.splitUserId(mUserId); if (unifiedKeyInfo.name() != null) {
if (mainUserId.name != null) { filename = unifiedKeyInfo.name();
filename = mainUserId.name; } else {
filename = KeyFormattingUtils.convertFingerprintToHex(unifiedKeyInfo.fingerprint());
} }
Uri contentUri = TemporaryFileProvider.createFile(activity, filename + Constants.FILE_EXTENSION_ASC); 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) { private void shareFingerprint(boolean toClipboard) {
Activity activity = getActivity(); Activity activity = getActivity();
if (activity == null || mFingerprint == null) { if (activity == null || unifiedKeyInfo == null) {
return; return;
} }
String content; String content;
String fingerprint = KeyFormattingUtils.convertFingerprintToHex(mFingerprint); String fingerprint = KeyFormattingUtils.convertFingerprintToHex(unifiedKeyInfo.fingerprint());
if (!toClipboard) { if (!toClipboard) {
content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint; content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
} else { } else {
@@ -365,141 +283,63 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
opts = options.toBundle(); opts = options.toBundle();
} }
qrCodeIntent.setData(mDataUri); qrCodeIntent.setData(KeyRings.buildUnifiedKeyRingUri(unifiedKeyInfo.master_key_id()));
ActivityCompat.startActivity(getActivity(), qrCodeIntent, opts); ActivityCompat.startActivity(requireActivity(), qrCodeIntent, opts);
} }
@Override @Override
public void onViewCreated(View view, Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); ViewKeyAdvViewModel viewModel = ViewModelProviders.of(requireActivity()).get(ViewKeyAdvViewModel.class);
if (dataUri == null) { LiveData<UnifiedKeyInfo> unifiedKeyInfoLiveData = viewModel.getUnifiedKeyInfoLiveData(requireContext());
Timber.e("Data missing. Should be Uri of key!"); unifiedKeyInfoLiveData.observe(this, this::onLoadUnifiedKeyInfo);
getActivity().finish();
LiveData<Bitmap> 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; return;
} }
loadData(dataUri); this.unifiedKeyInfo = unifiedKeyInfo;
}
private void loadData(Uri dataUri) { final String fingerprint = KeyFormattingUtils.convertFingerprintToHex(unifiedKeyInfo.fingerprint());
mDataUri = dataUri; 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<Cursor> 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<Cursor> 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); setContentShown(true);
} }
/** private void onLoadQrCode(Bitmap qrCode) {
* 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<Cursor> 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));
if (mQrCodeBitmapCache != null) { if (mQrCodeBitmapCache != null) {
return; return;
} }
AsyncTask<Void, Void, Bitmap> loadTask = mQrCodeBitmapCache = qrCode;
new AsyncTask<Void, Void, Bitmap>() { if (ViewKeyAdvShareFragment.this.isAdded()) {
protected Bitmap doInBackground(Void... unused) { mQrCode.requestLayout();
Uri uri = new Uri.Builder()
.scheme(Constants.FINGERPRINT_SCHEME)
.opaquePart(fingerprint)
.build();
// render with minimal size
return QrCodeUtils.getQRCodeBitmap(uri, 0);
}
protected void onPostExecute(Bitmap qrCode) { // simple fade-in animation
// cache for later, and if we are attached request re-layout AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f);
mQrCodeBitmapCache = qrCode; anim.setDuration(200);
mQrCode.startAnimation(anim);
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();
} }
private void uploadToKeyserver() { 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); Intent uploadIntent = new Intent(getActivity(), UploadKeyActivity.class);
uploadIntent.setData(mDataUri); uploadIntent.setData(KeyRings.buildUnifiedKeyRingUri(unifiedKeyInfo.master_key_id()));
uploadIntent.putExtra(MultiUserIdsFragment.EXTRA_KEY_IDS, new long[]{keyId}); uploadIntent.putExtra(MultiUserIdsFragment.EXTRA_KEY_IDS, new long[]{ unifiedKeyInfo.master_key_id() });
startActivityForResult(uploadIntent, 0); startActivityForResult(uploadIntent, 0);
} }

View File

@@ -20,38 +20,31 @@ package org.sufficientlysecure.keychain.ui;
import java.util.List; import java.util.List;
import android.arch.lifecycle.LiveData; import android.arch.lifecycle.ViewModelProviders;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.support.v4.app.FragmentActivity; 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.ActionMode;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView; import android.widget.ListView;
import android.widget.ViewAnimator; import android.widget.ViewAnimator;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.livedata.GenericLiveData;
import org.sufficientlysecure.keychain.model.SubKey; import org.sufficientlysecure.keychain.model.SubKey;
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
import org.sufficientlysecure.keychain.operations.results.EditKeyResult; import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; 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;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange; 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.SubkeysAdapter;
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter; import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; 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.AddSubkeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyExpiryDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyExpiryDialogFragment;
import timber.log.Timber;
public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements public class ViewKeyAdvSubkeysFragment extends LoaderFragment {
LoaderManager.LoaderCallbacks<Cursor> {
public static final String ARG_DATA_URI = "data_uri";
private static final int LOADER_ID_UNIFIED = 0;
private ListView mSubkeysList; private ListView mSubkeysList;
private ListView mSubkeysAddedList; private ListView mSubkeysAddedList;
private View mSubkeysAddedLayout; private View mSubkeysAddedLayout;
@@ -79,11 +65,8 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
private CryptoOperationHelper<SaveKeyringParcel, EditKeyResult> mEditKeyHelper; private CryptoOperationHelper<SaveKeyringParcel, EditKeyResult> mEditKeyHelper;
private Uri mDataUri;
private long mMasterKeyId;
private byte[] mFingerprint;
private SaveKeyringParcel.Builder mEditModeSkpBuilder; private SaveKeyringParcel.Builder mEditModeSkpBuilder;
private UnifiedKeyInfo unifiedKeyInfo;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { 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); mSubkeysAddedList = view.findViewById(R.id.view_key_subkeys_added);
mSubkeysAddedLayout = view.findViewById(R.id.view_key_subkeys_add_layout); mSubkeysAddedLayout = view.findViewById(R.id.view_key_subkeys_add_layout);
mSubkeysList.setOnItemClickListener(new AdapterView.OnItemClickListener() { mSubkeysList.setOnItemClickListener((parent, view1, position, id) -> editSubkey(position));
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
editSubkey(position);
}
});
View footer = new View(getActivity()); View footer = new View(getActivity());
int spacing = (int) android.util.TypedValue.applyDimension( int spacing = (int) android.util.TypedValue.applyDimension(
@@ -113,12 +91,7 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
mSubkeysAddedList.addFooterView(footer, null, false); mSubkeysAddedList.addFooterView(footer, null, false);
mSubkeyAddFabLayout = view.findViewById(R.id.view_key_subkey_fab_layout); mSubkeyAddFabLayout = view.findViewById(R.id.view_key_subkey_fab_layout);
view.findViewById(R.id.view_key_subkey_fab).setOnClickListener(new View.OnClickListener() { view.findViewById(R.id.view_key_subkey_fab).setOnClickListener(v -> addSubkey());
@Override
public void onClick(View v) {
addSubkey();
}
});
setHasOptionsMenu(true); setHasOptionsMenu(true);
@@ -129,16 +102,32 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); // Create an empty adapter we will use to display the loaded data.
if (dataUri == null) { mSubkeysAdapter = new SubkeysAdapter(requireContext());
Timber.e("Data missing. Should be Uri of key!"); mSubkeysList.setAdapter(mSubkeysAdapter);
getActivity().finish();
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; return;
} }
loadData(dataUri); this.unifiedKeyInfo = unifiedKeyInfo;
} }
private void onLoadSubKeys(List<SubKey> subKeys) {
mSubkeysAdapter.setData(subKeys);
setContentShown(true);
}
@Override @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mEditKeyHelper != null) { if (mEditKeyHelper != null) {
@@ -148,82 +137,6 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
super.onActivityResult(requestCode, resultCode, data); 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<Cursor> 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<Cursor> 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<List<SubKey>> subKeyLiveData = new GenericLiveData<>(requireContext(), null,
() -> keyRepository.getSubKeysByMasterKeyId(mMasterKeyId));
subKeyLiveData.observe(this, this::onLoadSubKeys);
break;
}
}
}
private void onLoadSubKeys(List<SubKey> 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<Cursor> loader) {
}
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
@@ -244,7 +157,7 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
@Override @Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) { public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mEditModeSkpBuilder = SaveKeyringParcel.buildChangeKeyringParcel(mMasterKeyId, mFingerprint); mEditModeSkpBuilder = SaveKeyringParcel.buildChangeKeyringParcel(unifiedKeyInfo.master_key_id(), unifiedKeyInfo.fingerprint());
mSubkeysAddedAdapter = new SubkeysAddedAdapter( mSubkeysAddedAdapter = new SubkeysAddedAdapter(
getActivity(), mEditModeSkpBuilder.getMutableAddSubKeys(), false); getActivity(), mEditModeSkpBuilder.getMutableAddSubKeys(), false);
@@ -292,15 +205,8 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
AddSubkeyDialogFragment addSubkeyDialogFragment = AddSubkeyDialogFragment addSubkeyDialogFragment =
AddSubkeyDialogFragment.newInstance(willBeMasterKey); AddSubkeyDialogFragment.newInstance(willBeMasterKey);
addSubkeyDialogFragment addSubkeyDialogFragment
.setOnAlgorithmSelectedListener( .setOnAlgorithmSelectedListener(newSubkey -> mSubkeysAddedAdapter.add(newSubkey));
new AddSubkeyDialogFragment.OnAlgorithmSelectedListener() { addSubkeyDialogFragment.show(requireFragmentManager(), "addSubkeyDialog");
@Override
public void onAlgorithmSelected(SaveKeyringParcel.SubkeyAdd newSubkey) {
mSubkeysAddedAdapter.add(newSubkey);
}
}
);
addSubkeyDialogFragment.show(getActivity().getSupportFragmentManager(), "addSubkeyDialog");
} }
private void editSubkey(final int position) { private void editSubkey(final int position) {
@@ -343,13 +249,11 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
// Create a new Messenger for the communication back // Create a new Messenger for the communication back
final Messenger messenger = new Messenger(returnHandler); final Messenger messenger = new Messenger(returnHandler);
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(() -> {
public void run() { EditSubkeyDialogFragment dialogFragment =
EditSubkeyDialogFragment dialogFragment = EditSubkeyDialogFragment.newInstance(messenger);
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 // Create a new Messenger for the communication back
final Messenger messenger = new Messenger(returnHandler); final Messenger messenger = new Messenger(returnHandler);
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(() -> {
public void run() { EditSubkeyExpiryDialogFragment dialogFragment =
EditSubkeyExpiryDialogFragment dialogFragment = EditSubkeyExpiryDialogFragment.newInstance(messenger, creationDate, expiryDate);
EditSubkeyExpiryDialogFragment.newInstance(messenger, creationDate, expiryDate);
dialogFragment.show(getActivity().getSupportFragmentManager(), "editSubkeyExpiryDialog"); dialogFragment.show(requireFragmentManager(), "editSubkeyExpiryDialog");
}
}); });
} }

View File

@@ -20,39 +20,30 @@ package org.sufficientlysecure.keychain.ui;
import java.util.List; import java.util.List;
import android.arch.lifecycle.LiveData; import android.arch.lifecycle.ViewModelProviders;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.support.v4.app.FragmentActivity; 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.ActionMode;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView; import android.widget.ListView;
import android.widget.ViewAnimator; import android.widget.ViewAnimator;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; 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.model.UserPacket.UserId;
import org.sufficientlysecure.keychain.operations.results.EditKeyResult; 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.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; 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.UserIdsAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAddedAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAddedAdapter;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; 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.EditUserIdDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment;
import timber.log.Timber;
public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements public class ViewKeyAdvUserIdsFragment extends LoaderFragment {
LoaderManager.LoaderCallbacks<Cursor> {
public static final String ARG_DATA_URI = "uri";
private static final int LOADER_ID_UNIFIED = 0;
private ListView mUserIds; private ListView mUserIds;
private ListView mUserIdsAddedList; private ListView mUserIdsAddedList;
private View mUserIdsAddedLayout; private View mUserIdsAddedLayout;
@@ -81,12 +65,8 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements
private CryptoOperationHelper<SaveKeyringParcel, EditKeyResult> mEditKeyHelper; private CryptoOperationHelper<SaveKeyringParcel, EditKeyResult> mEditKeyHelper;
private Uri mDataUri;
private long mMasterKeyId;
private byte[] mFingerprint;
private boolean mHasSecret;
private SaveKeyringParcel.Builder mSkpBuilder; private SaveKeyringParcel.Builder mSkpBuilder;
private UnifiedKeyInfo unifiedKeyInfo;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { 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); mUserIdsAddedList = view.findViewById(R.id.view_key_user_ids_added);
mUserIdsAddedLayout = view.findViewById(R.id.view_key_user_ids_add_layout); mUserIdsAddedLayout = view.findViewById(R.id.view_key_user_ids_add_layout);
mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() { mUserIds.setOnItemClickListener((parent, view1, position, id) -> showOrEditUserIdInfo(position));
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
showOrEditUserIdInfo(position);
}
});
View footer = new View(getActivity()); View footer = new View(getActivity());
int spacing = (int) android.util.TypedValue.applyDimension( int spacing = (int) android.util.TypedValue.applyDimension(
@@ -116,12 +91,7 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements
mUserIdsAddedList.addFooterView(footer, null, false); mUserIdsAddedList.addFooterView(footer, null, false);
mUserIdAddFabLayout = view.findViewById(R.id.view_key_subkey_fab_layout); mUserIdAddFabLayout = view.findViewById(R.id.view_key_subkey_fab_layout);
view.findViewById(R.id.view_key_subkey_fab).setOnClickListener(new View.OnClickListener() { view.findViewById(R.id.view_key_subkey_fab).setOnClickListener(v -> addUserId());
@Override
public void onClick(View v) {
addUserId();
}
});
setHasOptionsMenu(true); setHasOptionsMenu(true);
@@ -175,12 +145,10 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements
// Create a new Messenger for the communication back // Create a new Messenger for the communication back
final Messenger messenger = new Messenger(returnHandler); final Messenger messenger = new Messenger(returnHandler);
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(() -> {
public void run() { EditUserIdDialogFragment dialogFragment =
EditUserIdDialogFragment dialogFragment = EditUserIdDialogFragment.newInstance(messenger, isRevoked, isRevokedPending);
EditUserIdDialogFragment.newInstance(messenger, isRevoked, isRevokedPending); dialogFragment.show(requireFragmentManager(), "editUserIdDialog");
dialogFragment.show(getActivity().getSupportFragmentManager(), "editUserIdDialog");
}
}); });
} }
@@ -189,13 +157,11 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements
final boolean isRevoked = mUserIdsAdapter.getIsRevoked(position); final boolean isRevoked = mUserIdsAdapter.getIsRevoked(position);
final boolean isVerified = mUserIdsAdapter.getIsVerified(position) == Certs.VERIFIED_SECRET; final boolean isVerified = mUserIdsAdapter.getIsVerified(position) == Certs.VERIFIED_SECRET;
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(() -> {
public void run() { UserIdInfoDialogFragment dialogFragment =
UserIdInfoDialogFragment dialogFragment = UserIdInfoDialogFragment.newInstance(isRevoked, isVerified);
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 // pre-fill out primary name
AddUserIdDialogFragment addUserIdDialog = AddUserIdDialogFragment.newInstance(messenger, ""); AddUserIdDialogFragment addUserIdDialog = AddUserIdDialogFragment.newInstance(messenger, "");
addUserIdDialog.show(getActivity().getSupportFragmentManager(), "addUserIdDialog"); addUserIdDialog.show(requireFragmentManager(), "addUserIdDialog");
} }
@Override @Override
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); mUserIdsAdapter = new UserIdsAdapter(getActivity(), false);
if (dataUri == null) { mUserIds.setAdapter(mUserIdsAdapter);
Timber.e("Data missing. Should be Uri of key!");
getActivity().finish(); 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; return;
} }
this.unifiedKeyInfo = unifiedKeyInfo;
}
loadData(dataUri); private void onLoadUserIds(List<UserId> userIds) {
mUserIdsAdapter.setData(userIds);
setContentShown(true);
} }
@Override @Override
@@ -245,88 +223,6 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements
super.onActivityResult(requestCode, resultCode, data); 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<List<UserId>> userIdLiveData =
new GenericLiveData<>(getContext(), null, () -> keyRepository.getUserIds(masterKeyId));
userIdLiveData.observe(this, this::onUserIdsLoaded);
} catch (PgpKeyNotFoundException e) {
e.printStackTrace();
}
}
private void onUserIdsLoaded(List<UserId> 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<Cursor> 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<Cursor> 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<Cursor> loader) {
}
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
@@ -346,8 +242,8 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements
activity.startActionMode(new ActionMode.Callback() { activity.startActionMode(new ActionMode.Callback() {
@Override @Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) { public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mSkpBuilder = SaveKeyringParcel.buildChangeKeyringParcel(
mSkpBuilder = SaveKeyringParcel.buildChangeKeyringParcel(mMasterKeyId, mFingerprint); unifiedKeyInfo.master_key_id(), unifiedKeyInfo.fingerprint());
mUserIdsAddedAdapter = mUserIdsAddedAdapter =
new UserIdsAddedAdapter(getActivity(), mSkpBuilder.getMutableAddUserIds(), false); new UserIdsAddedAdapter(getActivity(), mSkpBuilder.getMutableAddUserIds(), false);

View File

@@ -17,6 +17,11 @@
package org.sufficientlysecure.keychain.ui; package org.sufficientlysecure.keychain.ui;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Typeface; 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.KeybaseQuery;
import com.textuality.keybase.lib.Proof; import com.textuality.keybase.lib.Proof;
import com.textuality.keybase.lib.User; import com.textuality.keybase.lib.User;
import org.sufficientlysecure.keychain.R; 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.operations.results.KeybaseVerificationResult;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel; import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.base.LoaderFragment; import org.sufficientlysecure.keychain.ui.base.LoaderFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.network.OkHttpKeybaseClient;
import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences; 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 public class ViewKeyKeybaseFragment extends LoaderFragment implements
LoaderManager.LoaderCallbacks<Cursor>, LoaderManager.LoaderCallbacks<Cursor>,
CryptoOperationHelper.Callback<KeybaseVerificationParcel, KeybaseVerificationResult> { CryptoOperationHelper.Callback<KeybaseVerificationParcel, KeybaseVerificationResult> {
public static final String ARG_DATA_URI = "uri"; public static final String ARG_MASTER_KEY_ID = "master_key_id";
private TextView mReportHeader; private TextView mReportHeader;
private TableLayout mProofListing; private TableLayout mProofListing;
@@ -76,7 +75,7 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements
private static final int LOADER_ID_DATABASE = 1; private static final int LOADER_ID_DATABASE = 1;
// for retrieving the key were working on // for retrieving the key were working on
private Uri mDataUri; private long masterKeyId;
private Proof mProof; private Proof mProof;
@@ -89,10 +88,10 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements
/** /**
* Creates new instance of this fragment * Creates new instance of this fragment
*/ */
public static ViewKeyKeybaseFragment newInstance(Uri dataUri) { public static ViewKeyKeybaseFragment newInstance(long masterKeyId) {
ViewKeyKeybaseFragment frag = new ViewKeyKeybaseFragment(); ViewKeyKeybaseFragment frag = new ViewKeyKeybaseFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putParcelable(ARG_DATA_URI, dataUri); args.putLong(ARG_MASTER_KEY_ID, masterKeyId);
frag.setArguments(args); frag.setArguments(args);
@@ -121,13 +120,10 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); masterKeyId = getArguments().getLong(ARG_MASTER_KEY_ID);
if (dataUri == null) { if (masterKeyId == 0L) {
Timber.e("Data missing. Should be Uri of key!"); throw new IllegalArgumentException();
getActivity().finish();
return;
} }
mDataUri = dataUri;
// retrieve the key from the database // retrieve the key from the database
getLoaderManager().initLoader(LOADER_ID_DATABASE, null, this); getLoaderManager().initLoader(LOADER_ID_DATABASE, null, this);
@@ -148,7 +144,7 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements
switch (id) { switch (id) {
case LOADER_ID_DATABASE: { case LOADER_ID_DATABASE: {
Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri); Uri baseUri = KeyRings.buildUnifiedKeyRingUri(masterKeyId);
return new CursorLoader(getActivity(), baseUri, TRUST_PROJECTION, null, null, null); return new CursorLoader(getActivity(), baseUri, TRUST_PROJECTION, null, null, null);
} }
// decided to just use an AsyncTask for keybase, but maybe later // decided to just use an AsyncTask for keybase, but maybe later

View File

@@ -18,6 +18,11 @@
package org.sufficientlysecure.keychain.ui.adapter; 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.content.Intent;
import android.databinding.DataBindingUtil; import android.databinding.DataBindingUtil;
import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentActivity;
@@ -26,6 +31,7 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.databinding.ImportKeysListItemBinding; import org.sufficientlysecure.keychain.databinding.ImportKeysListItemBinding;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress; 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.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel; import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity; 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 org.sufficientlysecure.keychain.util.ParcelableFileCache;
import timber.log.Timber; 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<ImportKeysAdapter.ViewHolder> public class ImportKeysAdapter extends RecyclerView.Adapter<ImportKeysAdapter.ViewHolder>
implements ImportKeysResultListener { implements ImportKeysResultListener {
@@ -182,8 +182,7 @@ public class ImportKeysAdapter extends RecyclerView.Adapter<ImportKeysAdapter.Vi
@Override @Override
public void onClick(View v) { public void onClick(View v) {
long keyId = KeyFormattingUtils.convertKeyIdHexToKeyId(entry.getKeyIdHex()); long keyId = KeyFormattingUtils.convertKeyIdHexToKeyId(entry.getKeyIdHex());
Intent intent = new Intent(mActivity, ViewKeyActivity.class); Intent intent = ViewKeyActivity.getViewKeyActivityIntent(mActivity, keyId);
intent.setData(KeyRings.buildGenericKeyRingUri(keyId));
mActivity.startActivity(intent); mActivity.startActivity(intent);
} }
}); });

View File

@@ -28,6 +28,7 @@ import android.animation.ObjectAnimator;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.app.ActivityOptions; import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@@ -41,6 +42,7 @@ import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.design.widget.AppBarLayout; import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CollapsingToolbarLayout; import android.support.design.widget.CollapsingToolbarLayout;
import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.CoordinatorLayout;
@@ -73,7 +75,6 @@ import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
@@ -124,13 +125,12 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
static final int REQUEST_CERTIFY = 3; static final int REQUEST_CERTIFY = 3;
static final int REQUEST_DELETE = 4; static final int REQUEST_DELETE = 4;
public static final String EXTRA_MASTER_KEY_ID = "master_key_id";
public static final String EXTRA_DISPLAY_RESULT = "display_result"; public static final String EXTRA_DISPLAY_RESULT = "display_result";
public static final String EXTRA_LINKED_TRANSITION = "linked_transition"; public static final String EXTRA_LINKED_TRANSITION = "linked_transition";
KeyRepository keyRepository; KeyRepository keyRepository;
protected Uri dataUri;
// For CryptoOperationHelper.Callback // For CryptoOperationHelper.Callback
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> importOpHelper; private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> importOpHelper;
private CryptoOperationHelper<ChangeUnlockParcel, EditKeyResult> editOpHelper; private CryptoOperationHelper<ChangeUnlockParcel, EditKeyResult> editOpHelper;
@@ -168,6 +168,12 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
private long masterKeyId; private long masterKeyId;
private byte[] fingerprint; 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") @SuppressLint("InflateParams")
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@@ -244,30 +250,29 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
}); });
refreshView = getLayoutInflater().inflate(R.layout.indeterminate_progress, null); refreshView = getLayoutInflater().inflate(R.layout.indeterminate_progress, null);
dataUri = getIntent().getData(); Intent intent = getIntent();
if (dataUri == null) { Uri dataUri = intent.getData();
Timber.e("Data missing. Should be uri of key!"); if (intent.hasExtra(EXTRA_MASTER_KEY_ID)) {
finish(); masterKeyId = intent.getLongExtra(EXTRA_MASTER_KEY_ID, 0L);
return; } else if (dataUri != null && dataUri.getHost().equals(ContactsContract.AUTHORITY)) {
} Long contactMasterKeyId = new ContactHelper(this).masterKeyIdFromContactsDataUri(dataUri);
if (dataUri.getHost().equals(ContactsContract.AUTHORITY)) { if (contactMasterKeyId == null) {
dataUri = new ContactHelper(this).dataUriFromContactUri(dataUri);
if (dataUri == null) {
Timber.e("Contact Data missing. Should be uri of key!"); Timber.e("Contact Data missing. Should be uri of key!");
Toast.makeText(this, R.string.error_contacts_key_id_missing, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.error_contacts_key_id_missing, Toast.LENGTH_LONG).show();
finish(); finish();
return; return;
} }
masterKeyId = contactMasterKeyId;
} else {
throw new IllegalArgumentException("Missing required extra master_key_id or contact uri");
} }
Timber.i("dataUri: " + dataUri); actionEncryptFile.setOnClickListener(v -> encrypt(false));
actionEncryptText.setOnClickListener(v -> encrypt(true));
actionEncryptFile.setOnClickListener(v -> encrypt(dataUri, false));
actionEncryptText.setOnClickListener(v -> encrypt(dataUri, true));
floatingActionButton.setOnClickListener(v -> { floatingActionButton.setOnClickListener(v -> {
if (isSecret) { if (isSecret) {
startSafeSlinger(dataUri); startSafeSlinger();
} else { } else {
scanQrCode(); scanQrCode();
} }
@@ -279,8 +284,8 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
// or start new ones. // or start new ones.
getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
if (savedInstanceState == null && getIntent().hasExtra(EXTRA_DISPLAY_RESULT)) { if (savedInstanceState == null && intent.hasExtra(EXTRA_DISPLAY_RESULT)) {
OperationResult result = getIntent().getParcelableExtra(EXTRA_DISPLAY_RESULT); OperationResult result = intent.getParcelableExtra(EXTRA_DISPLAY_RESULT);
result.createNotify(this).show(); result.createNotify(this).show();
} }
@@ -291,7 +296,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
if (Preferences.getPreferences(this).getExperimentalEnableKeybase()) { if (Preferences.getPreferences(this).getExperimentalEnableKeybase()) {
FragmentManager manager = getSupportFragmentManager(); FragmentManager manager = getSupportFragmentManager();
final ViewKeyKeybaseFragment keybaseFrag = ViewKeyKeybaseFragment.newInstance(dataUri); final ViewKeyKeybaseFragment keybaseFrag = ViewKeyKeybaseFragment.newInstance(masterKeyId);
manager.beginTransaction() manager.beginTransaction()
.replace(R.id.view_key_keybase_fragment, keybaseFrag) .replace(R.id.view_key_keybase_fragment, keybaseFrag)
.commit(); .commit();
@@ -341,7 +346,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
} }
case R.id.menu_key_view_advanced: { case R.id.menu_key_view_advanced: {
Intent advancedIntent = new Intent(this, ViewKeyAdvActivity.class); Intent advancedIntent = new Intent(this, ViewKeyAdvActivity.class);
advancedIntent.setData(dataUri); advancedIntent.putExtra(ViewKeyAdvActivity.EXTRA_MASTER_KEY_ID, masterKeyId);
startActivity(advancedIntent); startActivity(advancedIntent);
return true; return true;
} }
@@ -350,7 +355,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
return true; return true;
} }
case R.id.menu_key_view_certify_fingerprint: { case R.id.menu_key_view_certify_fingerprint: {
certifyFingerprint(dataUri); certifyFingerprint();
return true; return true;
} }
} }
@@ -440,9 +445,9 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
startActivityForResult(scanQrCode, REQUEST_QR_FINGERPRINT); startActivityForResult(scanQrCode, REQUEST_QR_FINGERPRINT);
} }
private void certifyFingerprint(Uri dataUri) { private void certifyFingerprint() {
Intent intent = new Intent(this, CertifyFingerprintActivity.class); Intent intent = new Intent(this, CertifyFingerprintActivity.class);
intent.setData(dataUri); intent.setData(KeyRings.buildUnifiedKeyRingUri(masterKeyId));
startActivityForResult(intent, REQUEST_CERTIFY); startActivityForResult(intent, REQUEST_CERTIFY);
} }
@@ -466,7 +471,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
opts = options.toBundle(); opts = options.toBundle();
} }
qrCodeIntent.setData(dataUri); qrCodeIntent.setData(KeyRings.buildUnifiedKeyRingUri(masterKeyId));
ActivityCompat.startActivity(this, qrCodeIntent, opts); 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 there is no encryption key, don't bother.
if (!hasEncrypt) { if (!hasEncrypt) {
Notify.create(this, R.string.error_no_encrypt_subkey, Notify.Style.ERROR).show(); Notify.create(this, R.string.error_no_encrypt_subkey, Notify.Style.ERROR).show();
return; return;
} }
try { long[] encryptionKeyIds = new long[] { masterKeyId };
long keyId = KeyRepository.create(this) Intent intent;
.getCachedPublicKeyRing(dataUri) if (text) {
.extractOrGetMasterKeyId(); intent = new Intent(this, EncryptTextActivity.class);
long[] encryptionKeyIds = new long[]{keyId}; intent.setAction(EncryptTextActivity.ACTION_ENCRYPT_TEXT);
Intent intent; intent.putExtra(EncryptTextActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
if (text) { } else {
intent = new Intent(this, EncryptTextActivity.class); intent = new Intent(this, EncryptFilesActivity.class);
intent.setAction(EncryptTextActivity.ACTION_ENCRYPT_TEXT); intent.setAction(EncryptFilesActivity.ACTION_ENCRYPT_DATA);
intent.putExtra(EncryptTextActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds); intent.putExtra(EncryptFilesActivity.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!");
} }
// used instead of startActivity set actionbar based on callingPackage
startActivityForResult(intent, 0);
} }
private void startSafeSlinger(Uri dataUri) { private void startSafeSlinger() {
long keyId = 0;
try {
keyId = KeyRepository.create(this)
.getCachedPublicKeyRing(dataUri)
.extractOrGetMasterKeyId();
} catch (PgpKeyNotFoundException e) {
Timber.e(e, "key not found!");
}
Intent safeSlingerIntent = new Intent(this, SafeSlingerActivity.class); 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); startActivityForResult(safeSlingerIntent, 0);
} }
@@ -727,7 +717,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
public Loader<Cursor> onCreateLoader(int id, Bundle args) { public Loader<Cursor> onCreateLoader(int id, Bundle args) {
switch (id) { switch (id) {
case LOADER_ID_UNIFIED: { 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); return new CursorLoader(this, baseUri, PROJECTION, null, null, null);
} }

View File

@@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain.ui.linked; package org.sufficientlysecure.keychain.ui.linked;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.BufferedWriter; import java.io.BufferedWriter;
@@ -61,19 +62,18 @@ import android.widget.TextView;
import android.widget.ViewAnimator; import android.widget.ViewAnimator;
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.HttpsURLConnection;
import org.bouncycastle.util.encoders.Hex;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.bouncycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.BuildConfig; import org.sufficientlysecure.keychain.BuildConfig;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.linked.LinkedAttribute; import org.sufficientlysecure.keychain.linked.LinkedAttribute;
import org.sufficientlysecure.keychain.linked.resources.GithubResource; import org.sufficientlysecure.keychain.linked.resources.GithubResource;
import org.sufficientlysecure.keychain.operations.results.EditKeyResult; import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; 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.base.CryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity;
import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.ui.widget.StatusIndicator; import org.sufficientlysecure.keychain.ui.widget.StatusIndicator;
@@ -433,8 +433,7 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment<SaveKe
@Override @Override
public void run() { public void run() {
FragmentActivity activity = getActivity(); FragmentActivity activity = getActivity();
Intent intent = new Intent(activity, ViewKeyActivity.class); Intent intent = ViewKeyActivity.getViewKeyActivityIntent(requireActivity(), mMasterKeyId);
intent.setData(KeyRings.buildGenericKeyRingUri(mMasterKeyId));
// intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

View File

@@ -183,11 +183,7 @@ public class ManageSecurityTokenFragment extends Fragment implements ManageSecur
public void finishAndShowKey(long masterKeyId) { public void finishAndShowKey(long masterKeyId) {
Activity activity = getActivity(); Activity activity = getActivity();
Intent viewKeyIntent = new Intent(activity, ViewKeyActivity.class); Intent viewKeyIntent = ViewKeyActivity.getViewKeyActivityIntent(requireActivity(), masterKeyId);
// 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));
if (activity instanceof CreateKeyActivity) { if (activity instanceof CreateKeyActivity) {
((CreateKeyActivity) activity).finishWithFirstTimeHandling(viewKeyIntent); ((CreateKeyActivity) activity).finishWithFirstTimeHandling(viewKeyIntent);
} else { } else {

View File

@@ -322,7 +322,7 @@ public class ContactHelper {
return new ArrayList<>(names); return new ArrayList<>(names);
} }
public Uri dataUriFromContactUri(Uri contactUri) { public Long masterKeyIdFromContactsDataUri(Uri contactUri) {
if (!isContactsPermissionGranted()) { if (!isContactsPermissionGranted()) {
return null; return null;
} }
@@ -332,7 +332,7 @@ public class ContactHelper {
if (contactMasterKey != null) { if (contactMasterKey != null) {
try { try {
if (contactMasterKey.moveToNext()) { if (contactMasterKey.moveToNext()) {
return KeychainContract.KeyRings.buildGenericKeyRingUri(contactMasterKey.getLong(0)); return contactMasterKey.getLong(0);
} }
} finally { } finally {
contactMasterKey.close(); contactMasterKey.close();

View File

@@ -24,9 +24,10 @@ CREATE TABLE IF NOT EXISTS keys (
); );
selectAllUnifiedKeyInfo: 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 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(DISTINCT aTI.package_name) AS autocrypt_package_names_csv,
GROUP_CONCAT(user_packets.user_id, '|||') AS user_id_list GROUP_CONCAT(user_packets.user_id, '|||') AS user_id_list
FROM keys 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 GROUP BY keys.master_key_id
ORDER BY has_secret DESC, user_packets.name COLLATE NOCASE ASC; 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: selectSubkeysByMasterKeyId:
SELECT * SELECT *
FROM keys FROM keys