diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index e8e4e04d3..b5b897c58 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -20,24 +20,16 @@ package org.sufficientlysecure.keychain.ui; import java.io.IOException; -import java.util.List; -import android.Manifest; import android.annotation.TargetApi; -import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; import android.database.Cursor; -import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Handler; -import android.provider.ContactsContract; import android.support.v4.app.LoaderManager; -import android.support.v4.content.ContextCompat; -import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v7.widget.CardView; import android.transition.Fade; @@ -50,7 +42,6 @@ import android.view.ViewTreeObserver.OnPreDrawListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.Button; -import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; @@ -68,9 +59,10 @@ import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment; import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment.OnIdentityLoadedListener; import org.sufficientlysecure.keychain.ui.linked.LinkedIdWizard; -import org.sufficientlysecure.keychain.ui.widget.KeyHealthPresenter; import org.sufficientlysecure.keychain.ui.widget.KeyHealthCardView; -import org.sufficientlysecure.keychain.util.ContactHelper; +import org.sufficientlysecure.keychain.ui.widget.KeyHealthPresenter; +import org.sufficientlysecure.keychain.ui.widget.SystemContactCardView; +import org.sufficientlysecure.keychain.ui.widget.SystemContactPresenter; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Preferences; @@ -102,16 +94,14 @@ public class ViewKeyFragment extends LoaderFragment implements private PostponeType mPostponeType; - private CardView mSystemContactCard; - private LinearLayout mSystemContactLayout; - private ImageView mSystemContactPicture; - private TextView mSystemContactName; - private ListView mLinkedIds; private CardView mLinkedIdsCard; private TextView mLinkedIdsEmpty; private TextView mLinkedIdsExpander; + SystemContactCardView mSystemContactCard; + SystemContactPresenter mSystemContactPresenter; + KeyHealthCardView mKeyHealthCard; KeyHealthPresenter mKeyHealthPresenter; @@ -144,10 +134,6 @@ public class ViewKeyFragment extends LoaderFragment implements mLinkedIdsExpander = (TextView) view.findViewById(R.id.view_key_linked_ids_expander); mLinkedIdsEmpty = (TextView) view.findViewById(R.id.view_key_linked_ids_empty); Button linkedIdsAddButton = (Button) view.findViewById(R.id.view_key_card_linked_ids_add); - mSystemContactCard = (CardView) view.findViewById(R.id.linked_system_contact_card); - mSystemContactLayout = (LinearLayout) view.findViewById(R.id.system_contact_layout); - mSystemContactName = (TextView) view.findViewById(R.id.system_contact_name); - mSystemContactPicture = (ImageView) view.findViewById(R.id.system_contact_picture); userIdsEditButton.setOnClickListener(new View.OnClickListener() { @Override @@ -176,6 +162,7 @@ public class ViewKeyFragment extends LoaderFragment implements } }); + mSystemContactCard = (SystemContactCardView) view.findViewById(R.id.linked_system_contact_card); mKeyHealthCard = (KeyHealthCardView) view.findViewById(R.id.subkey_status_card); return root; @@ -249,10 +236,13 @@ public class ViewKeyFragment extends LoaderFragment implements // initialize loaders, which will take care of auto-refresh on change getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); - initLinkedContactLoader(); initLinkedIds(mIsSecret); initCardButtonsVisibility(mIsSecret); + mSystemContactPresenter = new SystemContactPresenter( + getContext(), mSystemContactCard, LOADER_ID_LINKED_CONTACT, mMasterKeyId, mIsSecret); + mSystemContactPresenter.startLoader(getLoaderManager()); + mKeyHealthPresenter = new KeyHealthPresenter( getContext(), mKeyHealthCard, LOADER_ID_SUBKEY_STATUS, mMasterKeyId, mIsSecret); mKeyHealthPresenter.startLoader(getLoaderManager()); @@ -274,77 +264,6 @@ public class ViewKeyFragment extends LoaderFragment implements } } - /** - * Hides card if no linked system contact exists. Sets name, picture - * and onClickListener for the linked system contact's layout. - * In the case of a secret key, "me" (own profile) contact details are loaded. - */ - private void loadLinkedSystemContact(final long contactId) { - // contact doesn't exist, stop - if (contactId == -1) { - return; - } - - final Context context = mSystemContactName.getContext(); - ContactHelper contactHelper = new ContactHelper(context); - - String contactName = null; - - if (mIsSecret) {//all secret keys are linked to "me" profile in contacts - List mainProfileNames = contactHelper.getMainProfileContactName(); - if (mainProfileNames != null && mainProfileNames.size() > 0) { - contactName = mainProfileNames.get(0); - } - } else { - contactName = contactHelper.getContactName(contactId); - } - - if (contactName != null) { //contact name exists for given master key - showLinkedSystemContact(); - - mSystemContactName.setText(contactName); - - Bitmap picture; - if (mIsSecret) { - picture = contactHelper.loadMainProfilePhoto(false); - } else { - picture = contactHelper.loadPhotoByContactId(contactId, false); - } - if (picture != null) mSystemContactPicture.setImageBitmap(picture); - - mSystemContactLayout.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - launchContactActivity(contactId, context); - } - }); - } else { - hideLinkedSystemContact(); - } - } - - private void hideLinkedSystemContact() { - mSystemContactCard.setVisibility(View.GONE); - } - - private void showLinkedSystemContact() { - mSystemContactCard.setVisibility(View.VISIBLE); - } - - /** - * launches the default android Contacts app to view a contact with the passed - * contactId (CONTACT_ID column from ContactsContract.RawContact table which is _ID column in - * ContactsContract.Contact table) - * - * @param contactId _ID for row in ContactsContract.Contacts table - */ - private void launchContactActivity(final 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); - } - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { // if a result has been returned, display a notify @@ -356,12 +275,6 @@ public class ViewKeyFragment extends LoaderFragment implements } } - private static final String[] RAW_CONTACT_PROJECTION = { - ContactsContract.RawContacts.CONTACT_ID - }; - - private static final int INDEX_CONTACT_ID = 0; - @Override public Loader onCreateLoader(int id, Bundle args) { @@ -374,28 +287,7 @@ public class ViewKeyFragment extends LoaderFragment implements return LinkedIdsAdapter.createLoader(getActivity(), mDataUri); } - case LOADER_ID_LINKED_CONTACT: { - // we need a separate loader for linked contact - // to ensure refreshing on verification - - Uri baseUri = mIsSecret ? ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI : - ContactsContract.RawContacts.CONTENT_URI; - - return new CursorLoader( - getActivity(), - baseUri, - RAW_CONTACT_PROJECTION, - ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + - ContactsContract.RawContacts.SOURCE_ID + "=? AND " + - ContactsContract.RawContacts.DELETED + "=?", - new String[]{ - Constants.ACCOUNT_TYPE, - Long.toString(mMasterKeyId), - "0" // "0" for "not deleted" - }, - null); - } - + case LOADER_ID_LINKED_CONTACT: case LOADER_ID_SUBKEY_STATUS: { throw new IllegalStateException("This callback should never end up here!"); } @@ -448,14 +340,7 @@ public class ViewKeyFragment extends LoaderFragment implements break; } - case LOADER_ID_LINKED_CONTACT: { - if (data.moveToFirst()) { // if we have a linked contact - long contactId = data.getLong(INDEX_CONTACT_ID); - loadLinkedSystemContact(contactId); - } - break; - } - + case LOADER_ID_LINKED_CONTACT: case LOADER_ID_SUBKEY_STATUS: { throw new IllegalStateException("This callback should never end up here!"); } @@ -472,20 +357,6 @@ public class ViewKeyFragment extends LoaderFragment implements getLoaderManager().initLoader(LOADER_ID_LINKED_IDS, null, this); } - private void initLinkedContactLoader() { - if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_CONTACTS) - == PackageManager.PERMISSION_DENIED) { - Log.w(Constants.TAG, "loading linked system contact not possible READ_CONTACTS permission denied!"); - hideLinkedSystemContact(); - return; - } - - Bundle linkedContactData = new Bundle(); - - // initialises loader for contact query so we can listen to any updates - getLoaderManager().initLoader(LOADER_ID_LINKED_CONTACT, linkedContactData, this); - } - private void initCardButtonsVisibility(boolean isSecret) { LinearLayout buttonsUserIdsLayout = (LinearLayout) getActivity().findViewById(R.id.view_key_card_user_ids_buttons); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SystemContactCardView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SystemContactCardView.java new file mode 100644 index 000000000..58df99587 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SystemContactCardView.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2017 Vincent Breitmoser + * + * 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 . + */ + +package org.sufficientlysecure.keychain.ui.widget; + + +import android.content.Context; +import android.graphics.Bitmap; +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.widget.SystemContactPresenter.SystemContactClickListener; +import org.sufficientlysecure.keychain.ui.widget.SystemContactPresenter.SystemContactMvpView; + + +public class SystemContactCardView extends CardView implements SystemContactMvpView, OnClickListener { + private LinearLayout vSystemContactLayout; + private ImageView vSystemContactPicture; + private TextView vSystemContactName; + + private SystemContactClickListener systemContactClickListener; + + public SystemContactCardView(Context context, AttributeSet attrs) { + super(context, attrs); + + View view = LayoutInflater.from(context).inflate(R.layout.system_contact_card, this, true); + + vSystemContactLayout = (LinearLayout) view.findViewById(R.id.system_contact_layout); + vSystemContactName = (TextView) view.findViewById(R.id.system_contact_name); + vSystemContactPicture = (ImageView) 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 hideLinkedSystemContact() { + setVisibility(View.GONE); + } + + @Override + public void showLinkedSystemContact(String contactName, Bitmap picture) { + vSystemContactName.setText(contactName); + if (picture != null) { + vSystemContactPicture.setImageBitmap(picture); + } else { + vSystemContactPicture.setImageResource(R.drawable.ic_person_grey_48dp); + } + + setVisibility(View.VISIBLE); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SystemContactPresenter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SystemContactPresenter.java new file mode 100644 index 000000000..7b8919250 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SystemContactPresenter.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2017 Vincent Breitmoser + * + * 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 . + */ + +package org.sufficientlysecure.keychain.ui.widget; + + +import java.util.List; + +import android.Manifest; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Bundle; +import android.provider.ContactsContract; +import android.support.v4.app.LoaderManager; +import android.support.v4.app.LoaderManager.LoaderCallbacks; +import android.support.v4.content.ContextCompat; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.util.ContactHelper; +import org.sufficientlysecure.keychain.util.Log; + + +public class SystemContactPresenter implements LoaderCallbacks { + private static final String[] RAW_CONTACT_PROJECTION = { + ContactsContract.RawContacts.CONTACT_ID + }; + private static final int INDEX_CONTACT_ID = 0; + + + private final Context context; + private final SystemContactMvpView view; + private final int loaderId; + + private final long masterKeyId; + private final boolean isSecret; + + private long contactId; + + + public SystemContactPresenter(Context context, SystemContactMvpView view, int loaderId, long masterKeyId, boolean isSecret) { + this.context = context; + this.view = view; + this.loaderId = loaderId; + + this.masterKeyId = masterKeyId; + this.isSecret = isSecret; + + view.setSystemContactClickListener(new SystemContactClickListener() { + @Override + public void onSystemContactClick() { + SystemContactPresenter.this.onSystemContactClick(); + } + }); + } + + public void startLoader(LoaderManager loaderManager) { + if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) + == PackageManager.PERMISSION_DENIED) { + Log.w(Constants.TAG, "loading linked system contact not possible READ_CONTACTS permission denied!"); + view.hideLinkedSystemContact(); + return; + } + + Bundle linkedContactData = new Bundle(); + + // initialises loader for contact query so we can listen to any updates + loaderManager.restartLoader(loaderId, linkedContactData, this); + } + + @Override + public Loader onCreateLoader(int id, Bundle args) { + Uri baseUri = isSecret ? ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI : + ContactsContract.RawContacts.CONTENT_URI; + + return new CursorLoader(context, baseUri, RAW_CONTACT_PROJECTION, + ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + + ContactsContract.RawContacts.SOURCE_ID + "=? AND " + + ContactsContract.RawContacts.DELETED + "=?", + new String[] { + Constants.ACCOUNT_TYPE, + Long.toString(masterKeyId), + "0" // "0" for "not deleted" + }, + null); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + if (!data.moveToFirst()) { + return; + } + + long contactId = data.getLong(INDEX_CONTACT_ID); + loadLinkedSystemContact(contactId); + } + + private void loadLinkedSystemContact(final long contactId) { + this.contactId = contactId; + + if (contactId == -1) { + return; + } + + ContactHelper contactHelper = new ContactHelper(context); + + String contactName = null; + if (isSecret) { //all secret keys are linked to "me" profile in contacts + List mainProfileNames = contactHelper.getMainProfileContactName(); + if (mainProfileNames != null && mainProfileNames.size() > 0) { + contactName = mainProfileNames.get(0); + } + } else { + contactName = contactHelper.getContactName(contactId); + } + + if (contactName != null) { //contact name exists for given master key + Bitmap picture; + if (isSecret) { + picture = contactHelper.loadMainProfilePhoto(false); + } else { + picture = contactHelper.loadPhotoByContactId(contactId, false); + } + + view.showLinkedSystemContact(contactName, picture); + } else { + view.hideLinkedSystemContact(); + } + } + + @Override + public void onLoaderReset(Loader loader) { + + } + + private void onSystemContactClick() { + launchAndroidContactActivity(contactId, context); + } + + interface SystemContactMvpView { + void setSystemContactClickListener(SystemContactClickListener systemContactClickListener); + + void showLinkedSystemContact(String contactName, Bitmap picture); + void hideLinkedSystemContact(); + } + + 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); + } +} diff --git a/OpenKeychain/src/main/res/layout/system_contact_card.xml b/OpenKeychain/src/main/res/layout/system_contact_card.xml new file mode 100644 index 000000000..c907348cc --- /dev/null +++ b/OpenKeychain/src/main/res/layout/system_contact_card.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + diff --git a/OpenKeychain/src/main/res/layout/view_key_fragment.xml b/OpenKeychain/src/main/res/layout/view_key_fragment.xml index fbf910543..fee97d9bf 100644 --- a/OpenKeychain/src/main/res/layout/view_key_fragment.xml +++ b/OpenKeychain/src/main/res/layout/view_key_fragment.xml @@ -165,7 +165,7 @@ - - - - - - - - - - - - - - - - + card_view:cardUseCompatPadding="true" + tools:visibility="visible" />