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;
}
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);
}
public List<UnifiedKeyInfo> getUnifiedKeyInfo() {
SqlDelightQuery query = SubKey.FACTORY.selectAllUnifiedKeyInfo();
List<UnifiedKeyInfo> result = new ArrayList<>();
public UnifiedKeyInfo getUnifiedKeyInfo(long masterKeyId) {
SqlDelightQuery query = SubKey.FACTORY.selectUnifiedKeyInfoByMasterKeyId(masterKeyId);
try (Cursor cursor = getReadableDb().query(query)) {
while (cursor.moveToNext()) {
UnifiedKeyInfo unifiedKeyInfo = SubKey.UNIFIED_KEY_INFO_MAPPER.map(cursor);
result.add(unifiedKeyInfo);
if (cursor.moveToNext()) {
return SubKey.UNIFIED_KEY_INFO_MAPPER.map(cursor);
}
return null;
}
return result;
}
public List<UnifiedKeyInfo> getAllUnifiedKeyInfo() {
SqlDelightQuery query = SubKey.FACTORY.selectAllUnifiedKeyInfo();
return mapAllRows(query, SubKey.UNIFIED_KEY_INFO_MAPPER::map);
}
public List<UserId> getUserIds(long... masterKeyIds) {

View File

@@ -135,14 +135,6 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).build();
}
public static Uri buildGenericKeyRingUri(String masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(masterKeyId).build();
}
public static Uri buildGenericKeyRingUri(Uri uri) {
return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).build();
}
public static Uri buildUnifiedKeyRingUri(long masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId))
.appendPath(PATH_UNIFIED).build();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,16 +18,19 @@
package org.sufficientlysecure.keychain.ui;
import java.util.List;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Transformations;
import android.arch.lifecycle.ViewModel;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.annotation.StringRes;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.view.ActionMode;
@@ -37,83 +40,154 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.view.animation.OvershootInterpolator;
import android.widget.Toast;
import com.astuetz.PagerSlidingTabStrip;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.livedata.GenericLiveData;
import org.sufficientlysecure.keychain.model.SubKey;
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
import org.sufficientlysecure.keychain.model.UserPacket.UserId;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ContactHelper;
import timber.log.Timber;
public class ViewKeyAdvActivity extends BaseActivity implements
LoaderCallbacks<Cursor>, OnPageChangeListener {
KeyRepository mKeyRepository;
protected Uri mDataUri;
public class ViewKeyAdvActivity extends BaseActivity implements OnPageChangeListener {
public static final String EXTRA_MASTER_KEY_ID = "master_key_id";
public static final String EXTRA_SELECTED_TAB = "selected_tab";
public static final int TAB_START = 0;
public static final int TAB_SHARE = 1;
public static final int TAB_IDENTITIES = 2;
public static final int TAB_SUBKEYS = 3;
public static final int TAB_CERTS = 4;
KeyRepository keyRepository;
// view
private ViewPager mViewPager;
private PagerSlidingTabStrip mSlidingTabLayout;
private static final int LOADER_ID_UNIFIED = 0;
private ActionMode mActionMode;
private boolean mHasSecret;
private PagerTabStripAdapter mTabAdapter;
private boolean hasSecret;
private boolean mActionIconShown;
private boolean[] mTabsWithActionMode;
enum ViewKeyAdvTab {
START(ViewKeyAdvStartFragment.class, R.string.key_view_tab_start, false),
SHARE(ViewKeyAdvShareFragment.class, R.string.key_view_tab_share, false),
IDENTITIES(ViewKeyAdvUserIdsFragment.class, R.string.section_user_ids, true),
SUBKEYS(ViewKeyAdvSubkeysFragment.class, R.string.key_view_tab_keys, true);
public final Class<? 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
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setFullScreenDialogClose(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
setFullScreenDialogClose(v -> finish());
mKeyRepository = KeyRepository.create(this);
keyRepository = KeyRepository.create(this);
mViewPager = findViewById(R.id.pager);
mSlidingTabLayout = findViewById(R.id.sliding_tab_layout);
mDataUri = getIntent().getData();
if (mDataUri == null) {
Timber.e("Data missing. Should be uri of key!");
finish();
if (!getIntent().hasExtra(EXTRA_MASTER_KEY_ID)) {
throw new IllegalArgumentException("Missing required extra master_key_id");
}
ViewKeyAdvViewModel viewModel = ViewModelProviders.of(this).get(ViewKeyAdvViewModel.class);
viewModel.setMasterKeyId(getIntent().getLongExtra(EXTRA_MASTER_KEY_ID, 0L));
viewModel.getUnifiedKeyInfoLiveData(getApplicationContext()).observe(this, this::onLoadUnifiedKeyInfo);
initTabs();
}
public static class ViewKeyAdvViewModel extends ViewModel {
private Long masterKeyId;
private LiveData<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;
}
if (mDataUri.getHost().equals(ContactsContract.AUTHORITY)) {
mDataUri = new ContactHelper(this).dataUriFromContactUri(mDataUri);
if (mDataUri == null) {
Timber.e("Contact Data missing. Should be uri of key!");
Toast.makeText(this, R.string.error_contacts_key_id_missing, Toast.LENGTH_LONG).show();
finish();
return;
}
if (unifiedKeyInfo.name() != null) {
setTitle(unifiedKeyInfo.name());
} else {
setTitle(R.string.user_id_no_name);
}
// Prepare the loaders. Either re-connect with an existing ones,
// or start new ones.
getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
String formattedKeyId = KeyFormattingUtils.beautifyKeyIdWithPrefix(unifiedKeyInfo.master_key_id());
mToolbar.setSubtitle(formattedKeyId);
initTabs(mDataUri);
hasSecret = unifiedKeyInfo.has_any_secret();
// Note: order is important
int color;
if (unifiedKeyInfo.is_revoked() || unifiedKeyInfo.is_expired()) {
color = getResources().getColor(R.color.key_flag_red);
} else if (unifiedKeyInfo.has_any_secret()) {
color = getResources().getColor(R.color.android_green_light);
} else {
if (unifiedKeyInfo.is_verified()) {
color = getResources().getColor(R.color.android_green_light);
} else {
color = getResources().getColor(R.color.key_flag_orange);
}
}
mToolbar.setBackgroundColor(color);
mStatusBar.setBackgroundColor(ViewKeyActivity.getStatusBarBackgroundColor(color));
mSlidingTabLayout.setBackgroundColor(color);
invalidateOptionsMenu();
}
@Override
@@ -121,34 +195,13 @@ public class ViewKeyAdvActivity extends BaseActivity implements
setContentView(R.layout.view_key_adv_activity);
}
private void initTabs(Uri dataUri) {
mTabAdapter = new PagerTabStripAdapter(this);
mViewPager.setAdapter(mTabAdapter);
private void initTabs() {
PagerTabStripAdapter tabAdapter = new PagerTabStripAdapter(this);
mViewPager.setAdapter(tabAdapter);
// keep track which of these are action mode enabled!
mTabsWithActionMode = new boolean[4];
mTabAdapter.addTab(ViewKeyAdvStartFragment.class,
null, getString(R.string.key_view_tab_start));
mTabsWithActionMode[0] = false;
Bundle shareBundle = new Bundle();
shareBundle.putParcelable(ViewKeyAdvShareFragment.ARG_DATA_URI, dataUri);
mTabAdapter.addTab(ViewKeyAdvShareFragment.class,
shareBundle, getString(R.string.key_view_tab_share));
mTabsWithActionMode[1] = false;
Bundle userIdsBundle = new Bundle();
userIdsBundle.putParcelable(ViewKeyAdvUserIdsFragment.ARG_DATA_URI, dataUri);
mTabAdapter.addTab(ViewKeyAdvUserIdsFragment.class,
userIdsBundle, getString(R.string.section_user_ids));
mTabsWithActionMode[2] = true;
Bundle keysBundle = new Bundle();
keysBundle.putParcelable(ViewKeyAdvSubkeysFragment.ARG_DATA_URI, dataUri);
mTabAdapter.addTab(ViewKeyAdvSubkeysFragment.class,
keysBundle, getString(R.string.key_view_tab_keys));
mTabsWithActionMode[3] = true;
for (ViewKeyAdvTab tab : ViewKeyAdvTab.values()) {
tabAdapter.addTab(tab.fragmentClass, null, getString(tab.titleRes));
}
// update layout after operations
mSlidingTabLayout.setViewPager(mViewPager);
@@ -156,108 +209,8 @@ public class ViewKeyAdvActivity extends BaseActivity implements
// switch to tab selected by extra
Intent intent = getIntent();
int switchToTab = intent.getIntExtra(EXTRA_SELECTED_TAB, TAB_START);
int switchToTab = intent.getIntExtra(EXTRA_SELECTED_TAB, 0);
mViewPager.setCurrentItem(switchToTab);
}
// These are the rows that we will retrieve.
static final String[] PROJECTION = new String[]{
KeychainContract.KeyRings._ID,
KeychainContract.KeyRings.MASTER_KEY_ID,
KeychainContract.KeyRings.USER_ID,
KeychainContract.KeyRings.IS_REVOKED,
KeychainContract.KeyRings.IS_EXPIRED,
KeychainContract.KeyRings.VERIFIED,
KeychainContract.KeyRings.HAS_ANY_SECRET,
KeychainContract.KeyRings.FINGERPRINT,
KeychainContract.KeyRings.NAME,
KeychainContract.KeyRings.EMAIL,
KeychainContract.KeyRings.COMMENT,
};
static final int INDEX_MASTER_KEY_ID = 1;
static final int INDEX_USER_ID = 2;
static final int INDEX_IS_REVOKED = 3;
static final int INDEX_IS_EXPIRED = 4;
static final int INDEX_VERIFIED = 5;
static final int INDEX_HAS_ANY_SECRET = 6;
static final int INDEX_FINGERPRINT = 7;
static final int INDEX_NAME = 8;
static final int INDEX_EMAIL = 9;
static final int INDEX_COMMENT = 10;
@Override
public Loader<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
@@ -273,8 +226,7 @@ public class ViewKeyAdvActivity extends BaseActivity implements
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (!mHasSecret) {
if (!hasSecret) {
return false;
}
@@ -282,7 +234,7 @@ public class ViewKeyAdvActivity extends BaseActivity implements
getMenuInflater().inflate(R.menu.action_mode_edit, menu);
final MenuItem vActionModeItem = menu.findItem(R.id.menu_action_mode_edit);
boolean isCurrentActionFragment = mTabsWithActionMode[mViewPager.getCurrentItem()];
boolean isCurrentActionFragment = ViewKeyAdvTab.values()[mViewPager.getCurrentItem()].hasActionMode;
// if the state is as it should be, never mind
if (isCurrentActionFragment == mActionIconShown) {
@@ -298,7 +250,6 @@ public class ViewKeyAdvActivity extends BaseActivity implements
}
private void animateMenuItem(final MenuItem vEditSubkeys, final boolean animateShow) {
View actionView = LayoutInflater.from(this).inflate(R.layout.edit_icon, null);
vEditSubkeys.setActionView(actionView);
actionView.setTranslationX(animateShow ? 150 : 0);
@@ -317,7 +268,6 @@ public class ViewKeyAdvActivity extends BaseActivity implements
}
});
animator.start();
}
@Override

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain.ui.linked;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@@ -61,19 +62,18 @@ import android.widget.TextView;
import android.widget.ViewAnimator;
import javax.net.ssl.HttpsURLConnection;
import org.bouncycastle.util.encoders.Hex;
import org.json.JSONException;
import org.json.JSONObject;
import org.bouncycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.BuildConfig;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.linked.LinkedAttribute;
import org.sufficientlysecure.keychain.linked.resources.GithubResource;
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.ui.widget.StatusIndicator;
@@ -433,8 +433,7 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment<SaveKe
@Override
public void run() {
FragmentActivity activity = getActivity();
Intent intent = new Intent(activity, ViewKeyActivity.class);
intent.setData(KeyRings.buildGenericKeyRingUri(mMasterKeyId));
Intent intent = ViewKeyActivity.getViewKeyActivityIntent(requireActivity(), mMasterKeyId);
// intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
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) {
Activity activity = getActivity();
Intent viewKeyIntent = new Intent(activity, ViewKeyActivity.class);
// use the imported masterKeyId, not the one from the token, because
// that one might* just have been a subkey of the imported key
viewKeyIntent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
Intent viewKeyIntent = ViewKeyActivity.getViewKeyActivityIntent(requireActivity(), masterKeyId);
if (activity instanceof CreateKeyActivity) {
((CreateKeyActivity) activity).finishWithFirstTimeHandling(viewKeyIntent);
} else {

View File

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