simplify ViewKeyFragment

This commit is contained in:
Vincent Breitmoser
2018-06-24 03:10:10 +02:00
parent 8d584df44c
commit 38bf421023
17 changed files with 466 additions and 880 deletions

View File

@@ -25,10 +25,10 @@ import android.content.Context;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
@@ -52,28 +52,28 @@ public class IdentityAdapter extends RecyclerView.Adapter<ViewHolder> {
private final Context context;
private final LayoutInflater layoutInflater;
private final boolean isSecret;
private final IdentityClickListener identityClickListener;
private List<IdentityInfo> data;
private boolean isSecret;
public IdentityAdapter(Context context, boolean isSecret, IdentityClickListener identityClickListener) {
public IdentityAdapter(Context context, IdentityClickListener identityClickListener) {
super();
this.layoutInflater = LayoutInflater.from(context);
this.context = context;
this.isSecret = isSecret;
this.identityClickListener = identityClickListener;
}
public void setData(List<IdentityInfo> data) {
public void setData(List<IdentityInfo> data, boolean isSecret) {
this.data = data;
this.isSecret = isSecret;
notifyDataSetChanged();
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
IdentityInfo info = data.get(position);
int viewType = getItemViewType(position);
@@ -90,8 +90,9 @@ public class IdentityAdapter extends RecyclerView.Adapter<ViewHolder> {
}
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_USER_ID) {
return new UserIdViewHolder(
layoutInflater.inflate(R.layout.view_key_identity_user_id, parent, false), identityClickListener);
@@ -144,12 +145,9 @@ public class IdentityAdapter extends RecyclerView.Adapter<ViewHolder> {
vTitle = view.findViewById(R.id.linked_id_title);
vComment = view.findViewById(R.id.linked_id_comment);
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (identityClickListener != null) {
identityClickListener.onClickIdentity(getAdapterPosition());
}
view.setOnClickListener(v -> {
if (identityClickListener != null) {
identityClickListener.onClickIdentity(getAdapterPosition());
}
});
}
@@ -213,19 +211,8 @@ public class IdentityAdapter extends RecyclerView.Adapter<ViewHolder> {
vIcon = view.findViewById(R.id.trust_id_app_icon);
vMore = view.findViewById(R.id.user_id_item_more);
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
identityClickListener.onClickIdentity(getAdapterPosition());
}
});
vMore.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
identityClickListener.onClickIdentityMore(getAdapterPosition(), v);
}
});
view.setOnClickListener(v -> identityClickListener.onClickIdentity(getAdapterPosition()));
vMore.setOnClickListener(v -> identityClickListener.onClickIdentityMore(getAdapterPosition(), v));
}
public void bind(AutocryptPeerInfo info) {

View File

@@ -0,0 +1,70 @@
package org.sufficientlysecure.keychain.ui.keyview;
import java.util.List;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Transformations;
import android.arch.lifecycle.ViewModel;
import android.content.Context;
import org.sufficientlysecure.keychain.livedata.GenericLiveData;
import org.sufficientlysecure.keychain.model.KeyMetadata;
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
import org.sufficientlysecure.keychain.provider.KeyMetadataDao;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.IdentityInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.KeySubkeyStatus;
import org.sufficientlysecure.keychain.ui.keyview.loader.SystemContactDao;
import org.sufficientlysecure.keychain.ui.keyview.loader.SystemContactDao.SystemContactInfo;
public class KeyFragmentViewModel extends ViewModel {
private LiveData<List<IdentityInfo>> identityInfo;
private LiveData<KeySubkeyStatus> subkeyStatus;
private LiveData<SystemContactInfo> systemContactInfo;
private LiveData<KeyMetadata> keyserverStatus;
LiveData<List<IdentityInfo>> getIdentityInfo(Context context, LiveData<UnifiedKeyInfo> unifiedKeyInfoLiveData,
boolean showLinkedIds) {
if (identityInfo == null) {
IdentityDao identityDao = IdentityDao.getInstance(context);
identityInfo = Transformations.switchMap(unifiedKeyInfoLiveData,
(unifiedKeyInfo) -> new GenericLiveData<>(context, null,
() -> identityDao.getIdentityInfos(unifiedKeyInfo.master_key_id(), showLinkedIds)));
}
return identityInfo;
}
LiveData<KeySubkeyStatus> getSubkeyStatus(Context context, LiveData<UnifiedKeyInfo> unifiedKeyInfoLiveData) {
if (subkeyStatus == null) {
SubkeyStatusDao subkeyStatusDao = SubkeyStatusDao.getInstance(context);
subkeyStatus = Transformations.switchMap(unifiedKeyInfoLiveData,
(unifiedKeyInfo) -> new GenericLiveData<>(context, null,
() -> subkeyStatusDao.getSubkeyStatus(unifiedKeyInfo.master_key_id())));
}
return subkeyStatus;
}
LiveData<SystemContactInfo> getSystemContactInfo(Context context, LiveData<UnifiedKeyInfo> unifiedKeyInfoLiveData) {
if (systemContactInfo == null) {
SystemContactDao systemContactDao = SystemContactDao.getInstance(context);
systemContactInfo = Transformations.switchMap(unifiedKeyInfoLiveData,
(unifiedKeyInfo) -> new GenericLiveData<>(context, null,
() -> systemContactDao.getSystemContactInfo(unifiedKeyInfo.master_key_id(),
unifiedKeyInfo.has_any_secret())));
}
return systemContactInfo;
}
LiveData<KeyMetadata> getKeyserverStatus(Context context, LiveData<UnifiedKeyInfo> unifiedKeyInfoLiveData) {
if (keyserverStatus == null) {
KeyMetadataDao keyMetadataDao = KeyMetadataDao.create(context);
keyserverStatus = Transformations.switchMap(unifiedKeyInfoLiveData,
(unifiedKeyInfo) -> new GenericLiveData<>(context, null,
() -> keyMetadataDao.getKeyMetadata(unifiedKeyInfo.master_key_id())));
}
return keyserverStatus;
}
}

View File

@@ -313,7 +313,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
}
if (unifiedKeyInfoLiveData == null) {
KeyRepository keyRepository = KeyRepository.create(context);
unifiedKeyInfoLiveData = new GenericLiveData<>(context, null,
unifiedKeyInfoLiveData = new GenericLiveData<>(context, KeyRings.buildGenericKeyRingUri(masterKeyId),
() -> keyRepository.getUnifiedKeyInfo(masterKeyId));
}
return unifiedKeyInfoLiveData;

View File

@@ -21,11 +21,13 @@ package org.sufficientlysecure.keychain.ui.keyview;
import java.util.List;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.ViewModel;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
@@ -42,35 +44,43 @@ import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.model.KeyMetadata;
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDao;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter;
import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter.IdentityClickListener;
import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment;
import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity.ViewKeyViewModel;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.AutocryptPeerInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.IdentityInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.LinkedIdInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.UserIdInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.KeyHealthStatus;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.KeySubkeyStatus;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.SubKeyItem;
import org.sufficientlysecure.keychain.ui.keyview.loader.SystemContactDao.SystemContactInfo;
import org.sufficientlysecure.keychain.ui.keyview.presenter.IdentitiesPresenter;
import org.sufficientlysecure.keychain.ui.keyview.presenter.KeyHealthPresenter;
import org.sufficientlysecure.keychain.ui.keyview.presenter.KeyserverStatusPresenter;
import org.sufficientlysecure.keychain.ui.keyview.presenter.SystemContactPresenter;
import org.sufficientlysecure.keychain.ui.keyview.presenter.ViewKeyMvpView;
import org.sufficientlysecure.keychain.ui.keyview.view.IdentitiesCardView;
import org.sufficientlysecure.keychain.ui.keyview.view.KeyHealthView;
import org.sufficientlysecure.keychain.ui.keyview.view.KeyStatusList.KeyDisplayStatus;
import org.sufficientlysecure.keychain.ui.keyview.view.KeyserverStatusView;
import org.sufficientlysecure.keychain.ui.keyview.view.SystemContactCardView;
import org.sufficientlysecure.keychain.ui.linked.LinkedIdWizard;
import org.sufficientlysecure.keychain.util.Preferences;
import timber.log.Timber;
public class ViewKeyFragment extends Fragment implements ViewKeyMvpView, OnMenuItemClickListener {
public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener {
private IdentitiesCardView identitiesCardView;
private IdentitiesPresenter identitiesPresenter;
private SystemContactCardView systemContactCard;
private KeyHealthView keyStatusHealth;
private KeyserverStatusView keyserverStatusView;
SystemContactCardView systemContactCard;
SystemContactPresenter systemContactPresenter;
KeyHealthView keyStatusHealth;
KeyserverStatusView keyStatusKeyserver;
KeyHealthPresenter keyHealthPresenter;
KeyserverStatusPresenter keyserverStatusPresenter;
IdentityAdapter identitiesAdapter;
private Integer displayedContextMenuPosition;
private UnifiedKeyInfo unifiedKeyInfo;
private KeySubkeyStatus subkeyStatus;
private boolean showingExpandedInfo;
public static ViewKeyFragment newInstance() {
return new ViewKeyFragment();
@@ -83,74 +93,233 @@ public class ViewKeyFragment extends Fragment implements ViewKeyMvpView, OnMenuI
identitiesCardView = view.findViewById(R.id.card_identities);
systemContactCard = view.findViewById(R.id.linked_system_contact_card);
keyStatusHealth = view.findViewById(R.id.key_status_health);
keyStatusKeyserver = view.findViewById(R.id.key_status_keyserver);
keyserverStatusView = view.findViewById(R.id.key_status_keyserver);
identitiesAdapter = new IdentityAdapter(requireContext(), new IdentityClickListener() {
@Override
public void onClickIdentity(int position) {
showIdentityInfo(position);
}
@Override
public void onClickIdentityMore(int position, View anchor) {
showIdentityContextMenu(position, anchor);
}
});
identitiesCardView.setIdentitiesAdapter(identitiesAdapter);
keyStatusHealth.setOnHealthClickListener((v) -> onKeyHealthClick());
return view;
}
public static class KeyFragmentViewModel extends ViewModel {
private LiveData<List<IdentityInfo>> identityInfo;
private LiveData<KeySubkeyStatus> subkeyStatus;
private LiveData<SystemContactInfo> systemContactInfo;
private LiveData<KeyMetadata> keyserverStatus;
LiveData<List<IdentityInfo>> getIdentityInfo(IdentitiesPresenter identitiesPresenter) {
if (identityInfo == null) {
identityInfo = identitiesPresenter.getLiveDataInstance();
}
return identityInfo;
}
LiveData<KeySubkeyStatus> getSubkeyStatus(KeyHealthPresenter keyHealthPresenter) {
if (subkeyStatus == null) {
subkeyStatus = keyHealthPresenter.getLiveDataInstance();
}
return subkeyStatus;
}
LiveData<SystemContactInfo> getSystemContactInfo(SystemContactPresenter systemContactPresenter) {
if (systemContactInfo == null) {
systemContactInfo = systemContactPresenter.getLiveDataInstance();
}
return systemContactInfo;
}
LiveData<KeyMetadata> getKeyserverStatus(KeyserverStatusPresenter keyserverStatusPresenter) {
if (keyserverStatus == null) {
keyserverStatus = keyserverStatusPresenter.getLiveDataInstance();
}
return keyserverStatus;
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Context context = requireContext();
ViewKeyViewModel viewKeyViewModel = ViewModelProviders.of(requireActivity()).get(ViewKeyViewModel.class);
viewKeyViewModel.getUnifiedKeyInfoLiveData(requireContext()).observe(this, this::onLoadUnifiedKeyInfo);
LiveData<UnifiedKeyInfo> unifiedKeyInfoLiveData = viewKeyViewModel.getUnifiedKeyInfoLiveData(requireContext());
unifiedKeyInfoLiveData.observe(this, this::onLoadUnifiedKeyInfo);
KeyFragmentViewModel model = ViewModelProviders.of(this).get(KeyFragmentViewModel.class);
boolean showLinkedIds = Preferences.getPreferences(context).getExperimentalEnableLinkedIdentities();
model.getIdentityInfo(context, unifiedKeyInfoLiveData, showLinkedIds).observe(this, this::onLoadIdentityInfo);
model.getKeyserverStatus(context, unifiedKeyInfoLiveData).observe(this, this::onLoadKeyMetadata);
model.getSystemContactInfo(context, unifiedKeyInfoLiveData).observe(this, this::onLoadSystemContact);
model.getSubkeyStatus(context, unifiedKeyInfoLiveData).observe(this, this::onLoadSubkeyStatus);
}
private void onLoadSubkeyStatus(KeySubkeyStatus subkeyStatus) {
if (subkeyStatus == null) {
return;
}
this.subkeyStatus = subkeyStatus;
KeyHealthStatus keyHealthStatus = subkeyStatus.keyHealthStatus;
boolean isInsecure = keyHealthStatus == KeyHealthStatus.INSECURE;
boolean isExpired = keyHealthStatus == KeyHealthStatus.EXPIRED;
if (isInsecure) {
boolean primaryKeySecurityProblem = subkeyStatus.keyCertify.mSecurityProblem != null;
if (primaryKeySecurityProblem) {
keyStatusHealth.setKeyStatus(keyHealthStatus);
keyStatusHealth.setPrimarySecurityProblem(subkeyStatus.keyCertify.mSecurityProblem);
keyStatusHealth.setShowExpander(false);
} else {
keyStatusHealth.setKeyStatus(keyHealthStatus);
keyStatusHealth.setShowExpander(false);
displayExpandedInfo(false);
}
} else if (isExpired) {
keyStatusHealth.setKeyStatus(keyHealthStatus);
keyStatusHealth.setPrimaryExpiryDate(subkeyStatus.keyCertify.mExpiry);
keyStatusHealth.setShowExpander(false);
keyStatusHealth.hideExpandedInfo();
} else {
keyStatusHealth.setKeyStatus(keyHealthStatus);
keyStatusHealth.setShowExpander(keyHealthStatus != KeyHealthStatus.REVOKED);
keyStatusHealth.hideExpandedInfo();
}
}
private void displayExpandedInfo(boolean displayAll) {
SubKeyItem keyCertify = subkeyStatus.keyCertify;
SubKeyItem keySign = subkeyStatus.keysSign.isEmpty() ? null : subkeyStatus.keysSign.get(0);
SubKeyItem keyEncrypt = subkeyStatus.keysEncrypt.isEmpty() ? null : subkeyStatus.keysEncrypt.get(0);
KeyDisplayStatus certDisplayStatus = getKeyDisplayStatus(keyCertify);
KeyDisplayStatus signDisplayStatus = getKeyDisplayStatus(keySign);
KeyDisplayStatus encryptDisplayStatus = getKeyDisplayStatus(keyEncrypt);
if (!displayAll) {
if (certDisplayStatus == KeyDisplayStatus.OK) {
certDisplayStatus = null;
}
if (certDisplayStatus == KeyDisplayStatus.INSECURE) {
signDisplayStatus = null;
encryptDisplayStatus = null;
}
if (signDisplayStatus == KeyDisplayStatus.OK) {
signDisplayStatus = null;
}
if (encryptDisplayStatus == KeyDisplayStatus.OK) {
encryptDisplayStatus = null;
}
}
keyStatusHealth.showExpandedState(certDisplayStatus, signDisplayStatus, encryptDisplayStatus);
}
private void onKeyHealthClick() {
if (showingExpandedInfo) {
showingExpandedInfo = false;
keyStatusHealth.hideExpandedInfo();
} else {
showingExpandedInfo = true;
displayExpandedInfo(true);
}
}
private KeyDisplayStatus getKeyDisplayStatus(SubKeyItem subKeyItem) {
if (subKeyItem == null) {
return KeyDisplayStatus.UNAVAILABLE;
}
if (subKeyItem.mIsRevoked) {
return KeyDisplayStatus.REVOKED;
}
if (subKeyItem.mIsExpired) {
return KeyDisplayStatus.EXPIRED;
}
if (subKeyItem.mSecurityProblem != null) {
return KeyDisplayStatus.INSECURE;
}
if (subKeyItem.mSecretKeyType == SecretKeyType.GNU_DUMMY) {
return KeyDisplayStatus.STRIPPED;
}
if (subKeyItem.mSecretKeyType == SecretKeyType.DIVERT_TO_CARD) {
return KeyDisplayStatus.DIVERT;
}
return KeyDisplayStatus.OK;
}
private void onLoadUnifiedKeyInfo(UnifiedKeyInfo unifiedKeyInfo) {
KeyFragmentViewModel model = ViewModelProviders.of(this).get(KeyFragmentViewModel.class);
if (unifiedKeyInfo == null) {
return;
}
identitiesPresenter = new IdentitiesPresenter(
getContext(), identitiesCardView, this, unifiedKeyInfo.master_key_id(), unifiedKeyInfo.has_any_secret());
model.getIdentityInfo(identitiesPresenter).observe(this, identitiesPresenter);
Context context = requireContext();
systemContactPresenter = new SystemContactPresenter(
getContext(), systemContactCard, unifiedKeyInfo.master_key_id(), unifiedKeyInfo.has_any_secret());
model.getSystemContactInfo(systemContactPresenter).observe(this, systemContactPresenter);
this.unifiedKeyInfo = unifiedKeyInfo;
keyHealthPresenter = new KeyHealthPresenter(getContext(), keyStatusHealth, unifiedKeyInfo.master_key_id());
model.getSubkeyStatus(keyHealthPresenter).observe(this, keyHealthPresenter);
keyserverStatusPresenter = new KeyserverStatusPresenter(
getContext(), keyStatusKeyserver, unifiedKeyInfo.master_key_id(), unifiedKeyInfo.has_any_secret());
model.getKeyserverStatus(keyserverStatusPresenter).observe(this, keyserverStatusPresenter);
boolean showLinkedIds = Preferences.getPreferences(context).getExperimentalEnableLinkedIdentities();
boolean isSecret = unifiedKeyInfo.has_any_secret();
identitiesCardView.setAddLinkedIdButtonVisible(showLinkedIds && isSecret);
identitiesCardView.setIdentitiesCardListener((v) -> addLinkedIdentity());
}
private void showIdentityInfo(final int position) {
IdentityInfo info = identitiesAdapter.getInfo(position);
if (info instanceof LinkedIdInfo) {
showLinkedId((LinkedIdInfo) info);
} else if (info instanceof UserIdInfo) {
showUserIdInfo((UserIdInfo) info);
} else if (info instanceof AutocryptPeerInfo) {
Intent autocryptPeerIntent = ((AutocryptPeerInfo) info).getAutocryptPeerIntent();
if (autocryptPeerIntent != null) {
startActivity(autocryptPeerIntent);
}
}
}
private void showIdentityContextMenu(int position, View anchor) {
showContextMenu(position, anchor);
}
private void showLinkedId(final LinkedIdInfo info) {
LinkedIdViewFragment frag = LinkedIdViewFragment.newInstance(info.getMasterKeyId(), info.getRank(), unifiedKeyInfo.has_any_secret());
switchToFragment(frag, "linked_id");
}
private void showUserIdInfo(UserIdInfo info) {
if (!unifiedKeyInfo.has_any_secret()) {
UserIdInfoDialogFragment dialogFragment = UserIdInfoDialogFragment.newInstance(false, info.isVerified());
showDialogFragment(dialogFragment, "userIdInfoDialog");
}
}
private void addLinkedIdentity() {
Intent intent = new Intent(requireContext(), LinkedIdWizard.class);
intent.setData(KeyRings.buildUnifiedKeyRingUri(unifiedKeyInfo.master_key_id()));
startActivity(intent);
}
public void onClickForgetIdentity(int position) {
AutocryptPeerInfo info = (AutocryptPeerInfo) identitiesAdapter.getInfo(position);
if (info == null) {
Timber.e("got a 'forget' click on a bad trust id");
return;
}
AutocryptPeerDao.getInstance(requireContext()).deleteByIdentifier(info.getPackageName(), info.getIdentity());
}
private void onLoadIdentityInfo(List<IdentityInfo> identityInfos) {
identitiesAdapter.setData(identityInfos, unifiedKeyInfo.has_any_secret());
}
private void onLoadSystemContact(SystemContactInfo systemContactInfo) {
if (systemContactInfo == null) {
systemContactCard.hideLinkedSystemContact();
return;
}
systemContactCard.showLinkedSystemContact(systemContactInfo.contactName, systemContactInfo.contactPicture);
systemContactCard.setSystemContactClickListener((v) -> launchAndroidContactActivity(systemContactInfo.contactId));
}
private void onLoadKeyMetadata(KeyMetadata keyMetadata) {
if (keyMetadata == null) {
keyserverStatusView.setDisplayStatusUnknown();
} else if (keyMetadata.hasBeenUpdated()) {
if (keyMetadata.isPublished()) {
keyserverStatusView.setDisplayStatusPublished();
} else {
keyserverStatusView.setDisplayStatusNotPublished();
}
keyserverStatusView.setLastUpdated(keyMetadata.last_updated());
} else {
keyserverStatusView.setDisplayStatusUnknown();
}
}
@Override
public void switchToFragment(final Fragment frag, final String backStackName) {
new Handler().post(() -> requireFragmentManager().beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
@@ -170,18 +339,11 @@ public class ViewKeyFragment extends Fragment implements ViewKeyMvpView, OnMenuI
}
}
@Override
public void startActivityAndShowResultSnackbar(Intent intent) {
startActivityForResult(intent, 0);
}
@Override
public void showDialogFragment(final DialogFragment dialogFragment, final String tag) {
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(
() -> dialogFragment.show(requireFragmentManager(), tag));
}
@Override
public void showContextMenu(int position, View anchor) {
displayedContextMenuPosition = position;
@@ -202,10 +364,17 @@ public class ViewKeyFragment extends Fragment implements ViewKeyMvpView, OnMenuI
case R.id.autocrypt_forget:
int position = displayedContextMenuPosition;
displayedContextMenuPosition = null;
identitiesPresenter.onClickForgetIdentity(position);
onClickForgetIdentity(position);
return true;
}
return false;
}
private void launchAndroidContactActivity(long contactId) {
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactId));
intent.setData(uri);
startActivity(intent);
}
}

View File

@@ -71,7 +71,7 @@ public class IdentityDao {
this.autocryptPeerDao = autocryptPeerDao;
}
List<IdentityInfo> getIdentityInfos(long masterKeyId, boolean showLinkedIds) {
public List<IdentityInfo> getIdentityInfos(long masterKeyId, boolean showLinkedIds) {
ArrayList<IdentityInfo> identities = new ArrayList<>();
if (showLinkedIds) {
@@ -95,11 +95,11 @@ public class IdentityDao {
if (associatedUserIdInfo != null) {
int position = identities.indexOf(associatedUserIdInfo);
AutocryptPeerInfo autocryptPeerInfo = AutocryptPeerInfo
.create(associatedUserIdInfo, autocryptId, packageName, drawable, autocryptPeerIntent);
.create(masterKeyId, associatedUserIdInfo, autocryptId, packageName, drawable, autocryptPeerIntent);
identities.set(position, autocryptPeerInfo);
} else {
AutocryptPeerInfo autocryptPeerInfo = AutocryptPeerInfo
.create(autocryptId, packageName, drawable, autocryptPeerIntent);
.create(masterKeyId, autocryptId, packageName, drawable, autocryptPeerIntent);
identities.add(autocryptPeerInfo);
}
}
@@ -163,7 +163,7 @@ public class IdentityDao {
try {
UriAttribute uriAttribute = LinkedAttribute.fromAttributeData(userAttribute.attribute_data());
if (uriAttribute instanceof LinkedAttribute) {
return LinkedIdInfo.create(userAttribute.rank(),
return LinkedIdInfo.create(userAttribute.master_key_id(), userAttribute.rank(),
userAttribute.isVerified(), userAttribute.is_primary(), (LinkedAttribute) uriAttribute);
}
} catch (IOException e) {
@@ -180,7 +180,7 @@ public class IdentityDao {
if (userId.name() != null || userId.email() != null) {
IdentityInfo identityInfo = UserIdInfo.create(
userId.rank(), userId.isVerified(), userId.is_primary(), userId.name(), userId.email(), userId.comment());
userId.master_key_id(), userId.rank(), userId.isVerified(), userId.is_primary(), userId.name(), userId.email(), userId.comment());
identities.add(identityInfo);
}
}
@@ -188,6 +188,7 @@ public class IdentityDao {
}
public interface IdentityInfo {
long getMasterKeyId();
int getRank();
boolean isVerified();
boolean isPrimary();
@@ -195,6 +196,7 @@ public class IdentityDao {
@AutoValue
public abstract static class UserIdInfo implements IdentityInfo {
public abstract long getMasterKeyId();
public abstract int getRank();
public abstract boolean isVerified();
public abstract boolean isPrimary();
@@ -206,27 +208,29 @@ public class IdentityDao {
@Nullable
public abstract String getComment();
static UserIdInfo create(int rank, boolean isVerified, boolean isPrimary, String name, String email,
static UserIdInfo create(long masterKeyId, int rank, boolean isVerified, boolean isPrimary, String name, String email,
String comment) {
return new AutoValue_IdentityDao_UserIdInfo(rank, isVerified, isPrimary, name, email, comment);
return new AutoValue_IdentityDao_UserIdInfo(masterKeyId, rank, isVerified, isPrimary, name, email, comment);
}
}
@AutoValue
public abstract static class LinkedIdInfo implements IdentityInfo {
public abstract long getMasterKeyId();
public abstract int getRank();
public abstract boolean isVerified();
public abstract boolean isPrimary();
public abstract LinkedAttribute getLinkedAttribute();
static LinkedIdInfo create(int rank, boolean isVerified, boolean isPrimary, LinkedAttribute linkedAttribute) {
return new AutoValue_IdentityDao_LinkedIdInfo(rank, isVerified, isPrimary, linkedAttribute);
static LinkedIdInfo create(long masterKeyId, int rank, boolean isVerified, boolean isPrimary, LinkedAttribute linkedAttribute) {
return new AutoValue_IdentityDao_LinkedIdInfo(masterKeyId, rank, isVerified, isPrimary, linkedAttribute);
}
}
@AutoValue
public abstract static class AutocryptPeerInfo implements IdentityInfo {
public abstract long getMasterKeyId();
public abstract int getRank();
public abstract boolean isVerified();
public abstract boolean isPrimary();
@@ -240,15 +244,14 @@ public class IdentityDao {
@Nullable
public abstract Intent getAutocryptPeerIntent();
static AutocryptPeerInfo create(UserIdInfo userIdInfo, String autocryptPeer, String packageName,
static AutocryptPeerInfo create(long masterKeyId, UserIdInfo userIdInfo, String autocryptPeer, String packageName,
Drawable appIcon, Intent autocryptPeerIntent) {
return new AutoValue_IdentityDao_AutocryptPeerInfo(userIdInfo.getRank(), userIdInfo.isVerified(),
return new AutoValue_IdentityDao_AutocryptPeerInfo(masterKeyId, userIdInfo.getRank(), userIdInfo.isVerified(),
userIdInfo.isPrimary(), autocryptPeer, packageName, appIcon, userIdInfo, autocryptPeerIntent);
}
static AutocryptPeerInfo create(String autocryptPeer, String packageName, Drawable appIcon, Intent autocryptPeerIntent) {
return new AutoValue_IdentityDao_AutocryptPeerInfo(
0, false, false, autocryptPeer, packageName, appIcon, null, autocryptPeerIntent);
static AutocryptPeerInfo create(long masterKeyId, String autocryptPeer, String packageName, Drawable appIcon, Intent autocryptPeerIntent) {
return new AutoValue_IdentityDao_AutocryptPeerInfo(masterKeyId,0, false, false, autocryptPeer, packageName, appIcon, null, autocryptPeerIntent);
}
}

View File

@@ -47,7 +47,7 @@ public class SubkeyStatusDao {
this.keyRepository = keyRepository;
}
KeySubkeyStatus getSubkeyStatus(long masterKeyId, Comparator<SubKeyItem> comparator) {
public KeySubkeyStatus getSubkeyStatus(long masterKeyId) {
SubKeyItem keyCertify = null;
ArrayList<SubKeyItem> keysSign = new ArrayList<>();
ArrayList<SubKeyItem> keysEncrypt = new ArrayList<>();
@@ -73,10 +73,89 @@ public class SubkeyStatusDao {
return null;
}
Collections.sort(keysSign, comparator);
Collections.sort(keysEncrypt, comparator);
Collections.sort(keysSign, SUBKEY_COMPARATOR);
Collections.sort(keysEncrypt, SUBKEY_COMPARATOR);
return new KeySubkeyStatus(keyCertify, keysSign, keysEncrypt);
KeyHealthStatus keyHealthStatus = determineKeyHealthStatus(keyCertify, keysSign, keysEncrypt);
return new KeySubkeyStatus(keyCertify, keysSign, keysEncrypt, keyHealthStatus);
}
private KeyHealthStatus determineKeyHealthStatus(SubKeyItem keyCertify,
ArrayList<SubKeyItem> keysSign,
ArrayList<SubKeyItem> keysEncrypt) {
if (keyCertify.mIsRevoked) {
return KeyHealthStatus.REVOKED;
}
if (keyCertify.mIsExpired) {
return KeyHealthStatus.EXPIRED;
}
if (keyCertify.mSecurityProblem != null) {
return KeyHealthStatus.INSECURE;
}
if (!keysSign.isEmpty() && keysEncrypt.isEmpty()) {
SubKeyItem keySign = keysSign.get(0);
if (!keySign.isValid()) {
return KeyHealthStatus.BROKEN;
}
if (keySign.mSecurityProblem != null) {
return KeyHealthStatus.INSECURE;
}
return KeyHealthStatus.SIGN_ONLY;
}
if (keysSign.isEmpty() || keysEncrypt.isEmpty()) {
return KeyHealthStatus.BROKEN;
}
SubKeyItem keySign = keysSign.get(0);
SubKeyItem keyEncrypt = keysEncrypt.get(0);
if (keySign.mSecurityProblem != null && keySign.isValid()
|| keyEncrypt.mSecurityProblem != null && keyEncrypt.isValid()) {
return KeyHealthStatus.INSECURE;
}
if (!keySign.isValid() || !keyEncrypt.isValid()) {
return KeyHealthStatus.BROKEN;
}
if (keyCertify.mSecretKeyType == SecretKeyType.GNU_DUMMY
&& keySign.mSecretKeyType == SecretKeyType.GNU_DUMMY
&& keyEncrypt.mSecretKeyType == SecretKeyType.GNU_DUMMY) {
return KeyHealthStatus.STRIPPED;
}
if (keyCertify.mSecretKeyType == SecretKeyType.DIVERT_TO_CARD
&& keySign.mSecretKeyType == SecretKeyType.DIVERT_TO_CARD
&& keyEncrypt.mSecretKeyType == SecretKeyType.DIVERT_TO_CARD) {
return KeyHealthStatus.DIVERT;
}
boolean containsDivertKeys = keyCertify.mSecretKeyType == SecretKeyType.DIVERT_TO_CARD ||
keySign.mSecretKeyType == SecretKeyType.DIVERT_TO_CARD ||
keyEncrypt.mSecretKeyType == SecretKeyType.DIVERT_TO_CARD;
if (containsDivertKeys) {
return KeyHealthStatus.DIVERT_PARTIAL;
}
boolean containsStrippedKeys = keyCertify.mSecretKeyType == SecretKeyType.GNU_DUMMY
|| keySign.mSecretKeyType == SecretKeyType.GNU_DUMMY
|| keyEncrypt.mSecretKeyType == SecretKeyType.GNU_DUMMY;
if (containsStrippedKeys) {
return KeyHealthStatus.PARTIAL_STRIPPED;
}
return KeyHealthStatus.OK;
}
public enum KeyHealthStatus {
OK, DIVERT, DIVERT_PARTIAL, REVOKED, EXPIRED, INSECURE, SIGN_ONLY, STRIPPED, PARTIAL_STRIPPED, BROKEN
}
public static class KeySubkeyStatus {
@@ -84,11 +163,14 @@ public class SubkeyStatusDao {
public final SubKeyItem keyCertify;
public final List<SubKeyItem> keysSign;
public final List<SubKeyItem> keysEncrypt;
public final KeyHealthStatus keyHealthStatus;
KeySubkeyStatus(@NonNull SubKeyItem keyCertify, List<SubKeyItem> keysSign, List<SubKeyItem> keysEncrypt) {
KeySubkeyStatus(@NonNull SubKeyItem keyCertify, List<SubKeyItem> keysSign, List<SubKeyItem> keysEncrypt,
KeyHealthStatus keyHealthStatus) {
this.keyCertify = keyCertify;
this.keysSign = keysSign;
this.keysEncrypt = keysEncrypt;
this.keyHealthStatus = keyHealthStatus;
}
}
@@ -131,4 +213,24 @@ public class SubkeyStatusDao {
return !mIsRevoked && !mIsExpired;
}
}
private static final Comparator<SubKeyItem> SUBKEY_COMPARATOR = (one, two) -> {
if (one == two) {
return 0;
}
// if one is valid and the other isn't, the valid one always comes first
if (one.isValid() ^ two.isValid()) {
return one.isValid() ? -1 : 1;
}
// compare usability, if one is "more usable" than the other, that one comes first
int usability = one.mSecretKeyType.compareUsability(two.mSecretKeyType);
if (usability != 0) {
return usability;
}
if ((one.mSecurityProblem == null) ^ (two.mSecurityProblem == null)) {
return one.mSecurityProblem == null ? -1 : 1;
}
// otherwise, the newer one comes first
return one.newerThan(two) ? -1 : 1;
};
}

View File

@@ -60,7 +60,7 @@ public class SystemContactDao {
this.contentResolver = contentResolver;
}
SystemContactInfo getSystemContactInfo(long masterKeyId, boolean isSecret) {
public SystemContactInfo getSystemContactInfo(long masterKeyId, boolean isSecret) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS)
== PackageManager.PERMISSION_DENIED) {
Timber.w(Constants.TAG, "loading linked system contact not possible READ_CONTACTS permission denied!");

View File

@@ -1,99 +0,0 @@
package org.sufficientlysecure.keychain.ui.keyview.loader;
import java.util.Comparator;
import java.util.List;
import android.content.Context;
import org.sufficientlysecure.keychain.model.KeyMetadata;
import org.sufficientlysecure.keychain.provider.KeyMetadataDao;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.IdentityInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.KeySubkeyStatus;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.SubKeyItem;
import org.sufficientlysecure.keychain.ui.keyview.loader.SystemContactDao.SystemContactInfo;
public class ViewKeyLiveData {
public static class IdentityLiveData extends AsyncTaskLiveData<List<IdentityInfo>> {
private final IdentityDao identityDao;
private final long masterKeyId;
private final boolean showLinkedIds;
public IdentityLiveData(Context context, long masterKeyId, boolean showLinkedIds) {
super(context, KeyRings.buildGenericKeyRingUri(masterKeyId));
this.identityDao = IdentityDao.getInstance(context);
this.masterKeyId = masterKeyId;
this.showLinkedIds = showLinkedIds;
}
@Override
public List<IdentityInfo> asyncLoadData() {
return identityDao.getIdentityInfos(masterKeyId, showLinkedIds);
}
}
public static class SubkeyStatusLiveData extends AsyncTaskLiveData<KeySubkeyStatus> {
private final SubkeyStatusDao subkeyStatusDao;
private final long masterKeyId;
private final Comparator<SubKeyItem> comparator;
public SubkeyStatusLiveData(Context context, long masterKeyId, Comparator<SubKeyItem> comparator) {
super(context, KeyRings.buildGenericKeyRingUri(masterKeyId));
this.subkeyStatusDao = SubkeyStatusDao.getInstance(context);
this.masterKeyId = masterKeyId;
this.comparator = comparator;
}
@Override
public KeySubkeyStatus asyncLoadData() {
return subkeyStatusDao.getSubkeyStatus(masterKeyId, comparator);
}
}
public static class SystemContactInfoLiveData extends AsyncTaskLiveData<SystemContactInfo> {
private final SystemContactDao systemContactDao;
private final long masterKeyId;
private final boolean isSecret;
public SystemContactInfoLiveData(Context context, long masterKeyId, boolean isSecret) {
super(context, null);
this.systemContactDao = SystemContactDao.getInstance(context);
this.masterKeyId = masterKeyId;
this.isSecret = isSecret;
}
@Override
public SystemContactInfo asyncLoadData() {
return systemContactDao.getSystemContactInfo(masterKeyId, isSecret);
}
}
public static class KeyserverStatusLiveData extends AsyncTaskLiveData<KeyMetadata> {
private final KeyMetadataDao keyMetadataDao;
private final long masterKeyId;
public KeyserverStatusLiveData(Context context, long masterKeyId) {
super(context, KeyRings.buildGenericKeyRingUri(masterKeyId));
this.keyMetadataDao = KeyMetadataDao.create(context);
this.masterKeyId = masterKeyId;
}
@Override
public KeyMetadata asyncLoadData() {
return keyMetadataDao.getKeyMetadata(masterKeyId);
}
}
}

View File

@@ -1,154 +0,0 @@
/*
* Copyright (C) 2017 Schürmann & Breitmoser GbR
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui.keyview.presenter;
import java.util.List;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.Nullable;
import android.view.View;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDao;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter;
import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter.IdentityClickListener;
import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment;
import org.sufficientlysecure.keychain.ui.keyview.LinkedIdViewFragment;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.AutocryptPeerInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.IdentityInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.LinkedIdInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.UserIdInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.ViewKeyLiveData.IdentityLiveData;
import org.sufficientlysecure.keychain.ui.linked.LinkedIdWizard;
import org.sufficientlysecure.keychain.util.Preferences;
import timber.log.Timber;
public class IdentitiesPresenter implements Observer<List<IdentityInfo>> {
private final Context context;
private final IdentitiesMvpView view;
private final ViewKeyMvpView viewKeyMvpView;
private final IdentityAdapter identitiesAdapter;
private final long masterKeyId;
private final boolean isSecret;
private final boolean showLinkedIds;
private AutocryptPeerDao autocryptPeerDao;
public IdentitiesPresenter(Context context, IdentitiesMvpView view, ViewKeyMvpView viewKeyMvpView,
long masterKeyId, boolean isSecret) {
this.context = context;
this.view = view;
this.viewKeyMvpView = viewKeyMvpView;
this.autocryptPeerDao = AutocryptPeerDao.getInstance(context);
this.masterKeyId = masterKeyId;
this.isSecret = isSecret;
showLinkedIds = Preferences.getPreferences(context).getExperimentalEnableLinkedIdentities();
identitiesAdapter = new IdentityAdapter(context, isSecret, new IdentityClickListener() {
@Override
public void onClickIdentity(int position) {
showIdentityInfo(position);
}
@Override
public void onClickIdentityMore(int position, View anchor) {
showIdentityContextMenu(position, anchor);
}
});
view.setIdentitiesAdapter(identitiesAdapter);
view.setAddLinkedIdButtonVisible(showLinkedIds && isSecret);
view.setIdentitiesCardListener(() -> addLinkedIdentity());
}
@Override
public void onChanged(@Nullable List<IdentityInfo> identityInfos) {
identitiesAdapter.setData(identityInfos);
}
private void showIdentityInfo(final int position) {
IdentityInfo info = identitiesAdapter.getInfo(position);
if (info instanceof LinkedIdInfo) {
showLinkedId((LinkedIdInfo) info);
} else if (info instanceof UserIdInfo) {
showUserIdInfo((UserIdInfo) info);
} else if (info instanceof AutocryptPeerInfo) {
Intent autocryptPeerIntent = ((AutocryptPeerInfo) info).getAutocryptPeerIntent();
if (autocryptPeerIntent != null) {
viewKeyMvpView.startActivity(autocryptPeerIntent);
}
}
}
private void showIdentityContextMenu(int position, View anchor) {
viewKeyMvpView.showContextMenu(position, anchor);
}
private void showLinkedId(final LinkedIdInfo info) {
LinkedIdViewFragment frag = LinkedIdViewFragment.newInstance(masterKeyId, info.getRank(), isSecret);
viewKeyMvpView.switchToFragment(frag, "linked_id");
}
private void showUserIdInfo(UserIdInfo info) {
if (!isSecret) {
UserIdInfoDialogFragment dialogFragment = UserIdInfoDialogFragment.newInstance(false, info.isVerified());
viewKeyMvpView.showDialogFragment(dialogFragment, "userIdInfoDialog");
}
}
private void addLinkedIdentity() {
Intent intent = new Intent(context, LinkedIdWizard.class);
intent.setData(KeyRings.buildUnifiedKeyRingUri(masterKeyId));
context.startActivity(intent);
}
public void onClickForgetIdentity(int position) {
AutocryptPeerInfo info = (AutocryptPeerInfo) identitiesAdapter.getInfo(position);
if (info == null) {
Timber.e("got a 'forget' click on a bad trust id");
return;
}
autocryptPeerDao.deleteByIdentifier(info.getPackageName(), info.getIdentity());
}
public LiveData<List<IdentityInfo>> getLiveDataInstance() {
return new IdentityLiveData(context, masterKeyId, showLinkedIds);
}
public interface IdentitiesMvpView {
void setIdentitiesAdapter(IdentityAdapter userIdsAdapter);
void setIdentitiesCardListener(IdentitiesCardListener identitiesCardListener);
void setAddLinkedIdButtonVisible(boolean showLinkedIds);
}
public interface IdentitiesCardListener {
void onClickAddIdentity();
}
}

View File

@@ -1,278 +0,0 @@
/*
* Copyright (C) 2017 Schürmann & Breitmoser GbR
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui.keyview.presenter;
import java.util.Comparator;
import java.util.Date;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.content.Context;
import android.support.annotation.Nullable;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.KeySubkeyStatus;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.SubKeyItem;
import org.sufficientlysecure.keychain.ui.keyview.loader.ViewKeyLiveData.SubkeyStatusLiveData;
import org.sufficientlysecure.keychain.ui.keyview.view.KeyStatusList.KeyDisplayStatus;
public class KeyHealthPresenter implements Observer<KeySubkeyStatus> {
private static final Comparator<SubKeyItem> SUBKEY_COMPARATOR = new Comparator<SubKeyItem>() {
@Override
public int compare(SubKeyItem one, SubKeyItem two) {
// if one is valid and the other isn't, the valid one always comes first
if (one.isValid() ^ two.isValid()) {
return one.isValid() ? -1 : 1;
}
// compare usability, if one is "more usable" than the other, that one comes first
int usability = one.mSecretKeyType.compareUsability(two.mSecretKeyType);
if (usability != 0) {
return usability;
}
if ((one.mSecurityProblem == null) ^ (two.mSecurityProblem == null)) {
return one.mSecurityProblem == null ? -1 : 1;
}
// otherwise, the newer one comes first
return one.newerThan(two) ? -1 : 1;
}
};
private final Context context;
private final KeyHealthMvpView view;
private final long masterKeyId;
private KeySubkeyStatus subkeyStatus;
private boolean showingExpandedInfo;
public KeyHealthPresenter(Context context, KeyHealthMvpView view, long masterKeyId) {
this.context = context;
this.view = view;
this.masterKeyId = masterKeyId;
view.setOnHealthClickListener(new KeyHealthClickListener() {
@Override
public void onKeyHealthClick() {
KeyHealthPresenter.this.onKeyHealthClick();
}
});
}
@Override
public void onChanged(@Nullable KeySubkeyStatus subkeyStatus) {
this.subkeyStatus = subkeyStatus;
if (subkeyStatus == null) {
return;
}
KeyHealthStatus keyHealthStatus = determineKeyHealthStatus(subkeyStatus);
boolean isInsecure = keyHealthStatus == KeyHealthStatus.INSECURE;
boolean isExpired = keyHealthStatus == KeyHealthStatus.EXPIRED;
if (isInsecure) {
boolean primaryKeySecurityProblem = subkeyStatus.keyCertify.mSecurityProblem != null;
if (primaryKeySecurityProblem) {
view.setKeyStatus(keyHealthStatus);
view.setPrimarySecurityProblem(subkeyStatus.keyCertify.mSecurityProblem);
view.setShowExpander(false);
} else {
view.setKeyStatus(keyHealthStatus);
view.setShowExpander(false);
displayExpandedInfo(false);
}
} else if (isExpired) {
view.setKeyStatus(keyHealthStatus);
view.setPrimaryExpiryDate(subkeyStatus.keyCertify.mExpiry);
view.setShowExpander(false);
view.hideExpandedInfo();
} else {
view.setKeyStatus(keyHealthStatus);
view.setShowExpander(keyHealthStatus != KeyHealthStatus.REVOKED);
view.hideExpandedInfo();
}
}
private KeyHealthStatus determineKeyHealthStatus(KeySubkeyStatus subkeyStatus) {
SubKeyItem keyCertify = subkeyStatus.keyCertify;
if (keyCertify.mIsRevoked) {
return KeyHealthStatus.REVOKED;
}
if (keyCertify.mIsExpired) {
return KeyHealthStatus.EXPIRED;
}
if (keyCertify.mSecurityProblem != null) {
return KeyHealthStatus.INSECURE;
}
if (!subkeyStatus.keysSign.isEmpty() && subkeyStatus.keysEncrypt.isEmpty()) {
SubKeyItem keySign = subkeyStatus.keysSign.get(0);
if (!keySign.isValid()) {
return KeyHealthStatus.BROKEN;
}
if (keySign.mSecurityProblem != null) {
return KeyHealthStatus.INSECURE;
}
return KeyHealthStatus.SIGN_ONLY;
}
if (subkeyStatus.keysSign.isEmpty() || subkeyStatus.keysEncrypt.isEmpty()) {
return KeyHealthStatus.BROKEN;
}
SubKeyItem keySign = subkeyStatus.keysSign.get(0);
SubKeyItem keyEncrypt = subkeyStatus.keysEncrypt.get(0);
if (keySign.mSecurityProblem != null && keySign.isValid()
|| keyEncrypt.mSecurityProblem != null && keyEncrypt.isValid()) {
return KeyHealthStatus.INSECURE;
}
if (!keySign.isValid() || !keyEncrypt.isValid()) {
return KeyHealthStatus.BROKEN;
}
if (keyCertify.mSecretKeyType == SecretKeyType.GNU_DUMMY
&& keySign.mSecretKeyType == SecretKeyType.GNU_DUMMY
&& keyEncrypt.mSecretKeyType == SecretKeyType.GNU_DUMMY) {
return KeyHealthStatus.STRIPPED;
}
if (keyCertify.mSecretKeyType == SecretKeyType.DIVERT_TO_CARD
&& keySign.mSecretKeyType == SecretKeyType.DIVERT_TO_CARD
&& keyEncrypt.mSecretKeyType == SecretKeyType.DIVERT_TO_CARD) {
return KeyHealthStatus.DIVERT;
}
boolean containsDivertKeys = keyCertify.mSecretKeyType == SecretKeyType.DIVERT_TO_CARD ||
keySign.mSecretKeyType == SecretKeyType.DIVERT_TO_CARD ||
keyEncrypt.mSecretKeyType == SecretKeyType.DIVERT_TO_CARD;
if (containsDivertKeys) {
return KeyHealthStatus.DIVERT_PARTIAL;
}
boolean containsStrippedKeys = keyCertify.mSecretKeyType == SecretKeyType.GNU_DUMMY
|| keySign.mSecretKeyType == SecretKeyType.GNU_DUMMY
|| keyEncrypt.mSecretKeyType == SecretKeyType.GNU_DUMMY;
if (containsStrippedKeys) {
return KeyHealthStatus.PARTIAL_STRIPPED;
}
return KeyHealthStatus.OK;
}
private void onKeyHealthClick() {
if (showingExpandedInfo) {
showingExpandedInfo = false;
view.hideExpandedInfo();
} else {
showingExpandedInfo = true;
displayExpandedInfo(true);
}
}
private void displayExpandedInfo(boolean displayAll) {
SubKeyItem keyCertify = subkeyStatus.keyCertify;
SubKeyItem keySign = subkeyStatus.keysSign.isEmpty() ? null : subkeyStatus.keysSign.get(0);
SubKeyItem keyEncrypt = subkeyStatus.keysEncrypt.isEmpty() ? null : subkeyStatus.keysEncrypt.get(0);
KeyDisplayStatus certDisplayStatus = getKeyDisplayStatus(keyCertify);
KeyDisplayStatus signDisplayStatus = getKeyDisplayStatus(keySign);
KeyDisplayStatus encryptDisplayStatus = getKeyDisplayStatus(keyEncrypt);
if (!displayAll) {
if (certDisplayStatus == KeyDisplayStatus.OK) {
certDisplayStatus = null;
}
if (certDisplayStatus == KeyDisplayStatus.INSECURE) {
signDisplayStatus = null;
encryptDisplayStatus = null;
}
if (signDisplayStatus == KeyDisplayStatus.OK) {
signDisplayStatus = null;
}
if (encryptDisplayStatus == KeyDisplayStatus.OK) {
encryptDisplayStatus = null;
}
}
view.showExpandedState(certDisplayStatus, signDisplayStatus, encryptDisplayStatus);
}
private KeyDisplayStatus getKeyDisplayStatus(SubKeyItem subKeyItem) {
if (subKeyItem == null) {
return KeyDisplayStatus.UNAVAILABLE;
}
if (subKeyItem.mIsRevoked) {
return KeyDisplayStatus.REVOKED;
}
if (subKeyItem.mIsExpired) {
return KeyDisplayStatus.EXPIRED;
}
if (subKeyItem.mSecurityProblem != null) {
return KeyDisplayStatus.INSECURE;
}
if (subKeyItem.mSecretKeyType == SecretKeyType.GNU_DUMMY) {
return KeyDisplayStatus.STRIPPED;
}
if (subKeyItem.mSecretKeyType == SecretKeyType.DIVERT_TO_CARD) {
return KeyDisplayStatus.DIVERT;
}
return KeyDisplayStatus.OK;
}
public LiveData<KeySubkeyStatus> getLiveDataInstance() {
return new SubkeyStatusLiveData(context, masterKeyId, SUBKEY_COMPARATOR);
}
public enum KeyHealthStatus {
OK, DIVERT, DIVERT_PARTIAL, REVOKED, EXPIRED, INSECURE, SIGN_ONLY, STRIPPED, PARTIAL_STRIPPED, BROKEN
}
public interface KeyHealthMvpView {
void setKeyStatus(KeyHealthStatus keyHealthStatus);
void setPrimarySecurityProblem(KeySecurityProblem securityProblem);
void setPrimaryExpiryDate(Date expiry);
void setShowExpander(boolean showExpander);
void showExpandedState(KeyDisplayStatus certifyStatus, KeyDisplayStatus signStatus,
KeyDisplayStatus encryptStatus);
void hideExpandedInfo();
void setOnHealthClickListener(KeyHealthClickListener keyHealthClickListener);
}
public interface KeyStatusMvpView {
void setCertifyStatus(KeyDisplayStatus unavailable);
void setSignStatus(KeyDisplayStatus signStatus);
void setDecryptStatus(KeyDisplayStatus encryptStatus);
}
public interface KeyHealthClickListener {
void onKeyHealthClick();
}
}

View File

@@ -1,78 +0,0 @@
/*
* Copyright (C) 2017 Schürmann & Breitmoser GbR
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui.keyview.presenter;
import java.util.Date;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.content.Context;
import android.support.annotation.Nullable;
import org.sufficientlysecure.keychain.model.KeyMetadata;
import org.sufficientlysecure.keychain.ui.keyview.loader.ViewKeyLiveData.KeyserverStatusLiveData;
public class KeyserverStatusPresenter implements Observer<KeyMetadata> {
private final Context context;
private final KeyserverStatusMvpView view;
private final long masterKeyId;
private final boolean isSecret;
public KeyserverStatusPresenter(Context context, KeyserverStatusMvpView view, long masterKeyId,
boolean isSecret) {
this.context = context;
this.view = view;
this.masterKeyId = masterKeyId;
this.isSecret = isSecret;
}
public LiveData<KeyMetadata> getLiveDataInstance() {
return new KeyserverStatusLiveData(context, masterKeyId);
}
@Override
public void onChanged(@Nullable KeyMetadata keyserverStatus) {
if (keyserverStatus == null) {
view.setDisplayStatusUnknown();
return;
}
if (keyserverStatus.hasBeenUpdated()) {
if (keyserverStatus.isPublished()) {
view.setDisplayStatusPublished();
} else {
view.setDisplayStatusNotPublished();
}
view.setLastUpdated(keyserverStatus.last_updated());
} else {
view.setDisplayStatusUnknown();
}
}
public interface KeyserverStatusMvpView {
void setDisplayStatusPublished();
void setDisplayStatusNotPublished();
void setLastUpdated(Date lastUpdated);
void setDisplayStatusUnknown();
}
}

View File

@@ -1,90 +0,0 @@
/*
* Copyright (C) 2017 Schürmann & Breitmoser GbR
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui.keyview.presenter;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.provider.ContactsContract;
import android.support.annotation.Nullable;
import org.sufficientlysecure.keychain.ui.keyview.loader.SystemContactDao.SystemContactInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.ViewKeyLiveData.SystemContactInfoLiveData;
public class SystemContactPresenter implements Observer<SystemContactInfo> {
private final Context context;
private final SystemContactMvpView view;
private final long masterKeyId;
private final boolean isSecret;
private long contactId;
public SystemContactPresenter(Context context, SystemContactMvpView view, long masterKeyId, boolean isSecret) {
this.context = context;
this.view = view;
this.masterKeyId = masterKeyId;
this.isSecret = isSecret;
view.setSystemContactClickListener(SystemContactPresenter.this::onSystemContactClick);
}
public LiveData<SystemContactInfo> getLiveDataInstance() {
return new SystemContactInfoLiveData(context, masterKeyId, isSecret);
}
@Override
public void onChanged(@Nullable SystemContactInfo systemContactInfo) {
if (systemContactInfo == null) {
view.hideLinkedSystemContact();
return;
}
this.contactId = systemContactInfo.contactId;
view.showLinkedSystemContact(systemContactInfo.contactName, systemContactInfo.contactPicture);
}
private void onSystemContactClick() {
launchAndroidContactActivity(contactId, context);
}
public interface SystemContactMvpView {
void setSystemContactClickListener(SystemContactClickListener systemContactClickListener);
void showLinkedSystemContact(String contactName, Bitmap picture);
void hideLinkedSystemContact();
}
public interface SystemContactClickListener {
void onSystemContactClick();
}
private static void launchAndroidContactActivity(long contactId, Context context) {
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactId));
intent.setData(uri);
context.startActivity(intent);
}
}

View File

@@ -29,15 +29,12 @@ import android.widget.Button;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter;
import org.sufficientlysecure.keychain.ui.keyview.presenter.IdentitiesPresenter.IdentitiesCardListener;
import org.sufficientlysecure.keychain.ui.keyview.presenter.IdentitiesPresenter.IdentitiesMvpView;
import org.sufficientlysecure.keychain.ui.util.recyclerview.DividerItemDecoration;
public class IdentitiesCardView extends CardView implements IdentitiesMvpView {
public class IdentitiesCardView extends CardView {
private final RecyclerView vIdentities;
private IdentitiesCardListener identitiesCardListener;
private final Button linkedIdsAddButton;
public IdentitiesCardView(Context context, AttributeSet attrs) {
@@ -50,24 +47,16 @@ public class IdentitiesCardView extends CardView implements IdentitiesMvpView {
vIdentities.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL_LIST, false));
linkedIdsAddButton = view.findViewById(R.id.view_key_card_linked_ids_add);
linkedIdsAddButton.setOnClickListener(v -> {
if (identitiesCardListener != null) {
identitiesCardListener.onClickAddIdentity();
}
});
}
@Override
public void setIdentitiesAdapter(IdentityAdapter identityAdapter) {
vIdentities.setAdapter(identityAdapter);
}
@Override
public void setIdentitiesCardListener(IdentitiesCardListener identitiesCardListener) {
this.identitiesCardListener = identitiesCardListener;
public void setIdentitiesCardListener(OnClickListener identitiesCardListener) {
linkedIdsAddButton.setOnClickListener(identitiesCardListener);
}
@Override
public void setAddLinkedIdButtonVisible(boolean show) {
linkedIdsAddButton.setVisibility(show ? View.VISIBLE : View.GONE);
}

View File

@@ -39,14 +39,12 @@ import org.sufficientlysecure.keychain.pgp.SecurityProblem.InsecureBitStrength;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.NotWhitelistedCurve;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.UnidentifiedKeyProblem;
import org.sufficientlysecure.keychain.ui.keyview.presenter.KeyHealthPresenter.KeyHealthClickListener;
import org.sufficientlysecure.keychain.ui.keyview.presenter.KeyHealthPresenter.KeyHealthMvpView;
import org.sufficientlysecure.keychain.ui.keyview.presenter.KeyHealthPresenter.KeyHealthStatus;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.KeyHealthStatus;
import org.sufficientlysecure.keychain.ui.keyview.view.KeyStatusList.KeyDisplayStatus;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
public class KeyHealthView extends LinearLayout implements KeyHealthMvpView, OnClickListener {
public class KeyHealthView extends LinearLayout implements OnClickListener {
private final View vLayout;
private final TextView vTitle, vSubtitle;
private final ImageView vIcon;
@@ -59,7 +57,7 @@ public class KeyHealthView extends LinearLayout implements KeyHealthMvpView, OnC
private final View vExpiryLayout;
private final TextView vExpiryText;
private KeyHealthClickListener keyHealthClickListener;
private OnClickListener keyHealthClickListener;
public KeyHealthView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -125,7 +123,6 @@ public class KeyHealthView extends LinearLayout implements KeyHealthMvpView, OnC
}
}
@Override
public void setKeyStatus(KeyHealthStatus keyHealthStatus) {
switch (keyHealthStatus) {
case OK:
@@ -161,7 +158,6 @@ public class KeyHealthView extends LinearLayout implements KeyHealthMvpView, OnC
}
}
@Override
public void setPrimarySecurityProblem(KeySecurityProblem securityProblem) {
if (securityProblem == null) {
vInsecureLayout.setVisibility(View.GONE);
@@ -190,7 +186,6 @@ public class KeyHealthView extends LinearLayout implements KeyHealthMvpView, OnC
}
@Override
public void setPrimaryExpiryDate(Date expiry) {
if (expiry == null) {
vExpiryLayout.setVisibility(View.GONE);
@@ -205,23 +200,20 @@ public class KeyHealthView extends LinearLayout implements KeyHealthMvpView, OnC
@Override
public void onClick(View view) {
if (keyHealthClickListener != null) {
keyHealthClickListener.onKeyHealthClick();
keyHealthClickListener.onClick(view);
}
}
@Override
public void setOnHealthClickListener(KeyHealthClickListener keyHealthClickListener) {
public void setOnHealthClickListener(OnClickListener keyHealthClickListener) {
this.keyHealthClickListener = keyHealthClickListener;
vLayout.setClickable(keyHealthClickListener != null);
}
@Override
public void setShowExpander(boolean showExpander) {
vLayout.setClickable(showExpander);
vExpander.setVisibility(showExpander ? View.VISIBLE : View.GONE);
}
@Override
public void showExpandedState(KeyDisplayStatus certifyStatus, KeyDisplayStatus signStatus,
KeyDisplayStatus encryptStatus) {
if (certifyStatus == null && signStatus == null && encryptStatus == null) {
@@ -240,7 +232,6 @@ public class KeyHealthView extends LinearLayout implements KeyHealthMvpView, OnC
}
@Override
public void hideExpandedInfo() {
showExpandedState(null, null, null);
}

View File

@@ -30,10 +30,9 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.keyview.presenter.KeyHealthPresenter.KeyStatusMvpView;
public class KeyStatusList extends LinearLayout implements KeyStatusMvpView {
public class KeyStatusList extends LinearLayout {
private final TextView vCertText, vSignText, vDecryptText;
private final ImageView vCertIcon, vSignIcon, vDecryptIcon;
private final View vCertToken, vSignToken, vDecryptToken;
@@ -107,7 +106,6 @@ public class KeyStatusList extends LinearLayout implements KeyStatusMvpView {
}
@Override
public void setCertifyStatus(KeyDisplayStatus keyDisplayStatus) {
if (keyDisplayStatus == null) {
vCertifyLayout.setVisibility(View.GONE);
@@ -121,7 +119,6 @@ public class KeyStatusList extends LinearLayout implements KeyStatusMvpView {
vCertifyLayout.setVisibility(View.VISIBLE);
}
@Override
public void setSignStatus(KeyDisplayStatus keyDisplayStatus) {
if (keyDisplayStatus == null) {
vSignLayout.setVisibility(View.GONE);
@@ -134,7 +131,6 @@ public class KeyStatusList extends LinearLayout implements KeyStatusMvpView {
vSignLayout.setVisibility(View.VISIBLE);
}
@Override
public void setDecryptStatus(KeyDisplayStatus keyDisplayStatus) {
if (keyDisplayStatus == null) {
vDecryptLayout.setVisibility(View.GONE);

View File

@@ -34,10 +34,9 @@ import android.widget.ImageView;
import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.keyview.presenter.KeyserverStatusPresenter.KeyserverStatusMvpView;
public class KeyserverStatusView extends FrameLayout implements KeyserverStatusMvpView {
public class KeyserverStatusView extends FrameLayout {
private final View vLayout;
private final TextView vTitle;
private final TextView vSubtitle;
@@ -75,23 +74,19 @@ public class KeyserverStatusView extends FrameLayout implements KeyserverStatusM
}
}
@Override
public void setDisplayStatusPublished() {
setDisplayStatus(KeyserverDisplayStatus.PUBLISHED);
}
@Override
public void setDisplayStatusNotPublished() {
setDisplayStatus(KeyserverDisplayStatus.NOT_PUBLISHED);
}
@Override
public void setDisplayStatusUnknown() {
setDisplayStatus(KeyserverDisplayStatus.UNKNOWN);
vSubtitle.setText(R.string.keyserver_last_updated_never);
}
@Override
public void setLastUpdated(Date lastUpdated) {
String lastUpdatedText = DateFormat.getMediumDateFormat(getContext()).format(lastUpdated);
vSubtitle.setText(getResources().getString(R.string.keyserver_last_updated, lastUpdatedText));

View File

@@ -24,23 +24,18 @@ import android.support.v7.widget.CardView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.keyview.presenter.SystemContactPresenter.SystemContactClickListener;
import org.sufficientlysecure.keychain.ui.keyview.presenter.SystemContactPresenter.SystemContactMvpView;
public class SystemContactCardView extends CardView implements SystemContactMvpView, OnClickListener {
public class SystemContactCardView extends CardView {
private LinearLayout vSystemContactLayout;
private ImageView vSystemContactPicture;
private TextView vSystemContactName;
private SystemContactClickListener systemContactClickListener;
public SystemContactCardView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -49,28 +44,16 @@ public class SystemContactCardView extends CardView implements SystemContactMvpV
vSystemContactLayout = view.findViewById(R.id.system_contact_layout);
vSystemContactName = view.findViewById(R.id.system_contact_name);
vSystemContactPicture = view.findViewById(R.id.system_contact_picture);
vSystemContactLayout.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if (systemContactClickListener != null) {
systemContactClickListener.onSystemContactClick();
}
}
@Override
public void setSystemContactClickListener(SystemContactClickListener systemContactClickListener) {
this.systemContactClickListener = systemContactClickListener;
vSystemContactLayout.setClickable(systemContactClickListener != null);
public void setSystemContactClickListener(OnClickListener onClickListener) {
vSystemContactLayout.setOnClickListener(onClickListener);
}
public void hideLinkedSystemContact() {
setVisibility(View.GONE);
}
@Override
public void showLinkedSystemContact(String contactName, Bitmap picture) {
vSystemContactName.setText(contactName);
if (picture != null) {