use LiveData in ViewKeyAdvActivity
This commit is contained in:
@@ -54,5 +54,9 @@ public abstract class SubKey implements KeysModel {
|
||||
}
|
||||
return autocryptPackageNames;
|
||||
}
|
||||
|
||||
public boolean has_auth_key() {
|
||||
return has_auth_key_int() != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 we’re working on
|
||||
private Uri mDataUri;
|
||||
private long masterKeyId;
|
||||
|
||||
private Proof mProof;
|
||||
|
||||
@@ -89,10 +88,10 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements
|
||||
/**
|
||||
* Creates new instance of this fragment
|
||||
*/
|
||||
public static ViewKeyKeybaseFragment newInstance(Uri dataUri) {
|
||||
public static ViewKeyKeybaseFragment newInstance(long masterKeyId) {
|
||||
ViewKeyKeybaseFragment frag = new ViewKeyKeybaseFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putParcelable(ARG_DATA_URI, dataUri);
|
||||
args.putLong(ARG_MASTER_KEY_ID, masterKeyId);
|
||||
|
||||
frag.setArguments(args);
|
||||
|
||||
@@ -121,13 +120,10 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
Uri dataUri = getArguments().getParcelable(ARG_DATA_URI);
|
||||
if (dataUri == null) {
|
||||
Timber.e("Data missing. Should be Uri of key!");
|
||||
getActivity().finish();
|
||||
return;
|
||||
masterKeyId = getArguments().getLong(ARG_MASTER_KEY_ID);
|
||||
if (masterKeyId == 0L) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
mDataUri = dataUri;
|
||||
|
||||
// retrieve the key from the database
|
||||
getLoaderManager().initLoader(LOADER_ID_DATABASE, null, this);
|
||||
@@ -148,7 +144,7 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements
|
||||
|
||||
switch (id) {
|
||||
case LOADER_ID_DATABASE: {
|
||||
Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
|
||||
Uri baseUri = KeyRings.buildUnifiedKeyRingUri(masterKeyId);
|
||||
return new CursorLoader(getActivity(), baseUri, TRUST_PROJECTION, null, null, null);
|
||||
}
|
||||
// decided to just use an AsyncTask for keybase, but maybe later
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user