diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java index 938a3ea62..e997ea701 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java @@ -17,17 +17,16 @@ package org.sufficientlysecure.keychain.ui; + +import java.util.ArrayList; +import java.util.List; + import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; -import androidx.recyclerview.widget.DefaultItemAnimator; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -37,18 +36,20 @@ import android.widget.EditText; import android.widget.ImageButton; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.appcompat.widget.AppCompatEditText; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.DefaultItemAnimator; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; import org.sufficientlysecure.keychain.ui.dialog.AddEmailDialogFragment; import org.sufficientlysecure.keychain.ui.util.Notify; -import org.sufficientlysecure.keychain.ui.widget.EmailEditText; - -import java.util.ArrayList; -import java.util.List; public class CreateKeyEmailFragment extends Fragment { private CreateKeyActivity mCreateKeyActivity; - private EmailEditText mEmailEdit; + private AppCompatEditText mEmailEdit; private ArrayList mAdditionalEmailModels = new ArrayList<>(); private EmailAdapter mEmailAdapter; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java index f33506f18..d7ba9529a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java @@ -20,20 +20,20 @@ package org.sufficientlysecure.keychain.ui; import android.content.Context; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.appcompat.widget.AppCompatEditText; +import androidx.fragment.app.Fragment; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; -import org.sufficientlysecure.keychain.ui.widget.NameEditText; public class CreateKeyNameFragment extends Fragment { CreateKeyActivity mCreateKeyActivity; - NameEditText mNameEdit; + AppCompatEditText mNameEdit; View mBackButton; View mNextButton; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysSearchFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysSearchFragment.java index 078a54eca..864d3ab3b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysSearchFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysSearchFragment.java @@ -17,19 +17,13 @@ package org.sufficientlysecure.keychain.ui; + import android.app.Activity; import android.content.Context; import android.content.Intent; -import android.database.MatrixCursor; import android.os.Bundle; import android.preference.PreferenceActivity; -import android.provider.BaseColumns; -import androidx.fragment.app.Fragment; -import androidx.core.view.MenuItemCompat; -import androidx.core.view.MenuItemCompat.OnActionExpandListener; -import androidx.cursoradapter.widget.CursorAdapter; -import androidx.cursoradapter.widget.SimpleCursorAdapter; -import androidx.appcompat.widget.SearchView; +import android.text.InputType; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -38,18 +32,17 @@ import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; +import androidx.appcompat.widget.SearchView; +import androidx.core.view.MenuItemCompat; +import androidx.core.view.MenuItemCompat.OnActionExpandListener; +import androidx.fragment.app.Fragment; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.processing.CloudLoaderState; import org.sufficientlysecure.keychain.keyimport.processing.ImportKeysListener; -import org.sufficientlysecure.keychain.util.ContactHelper; import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences.CloudSearchPrefs; -import java.util.ArrayList; -import java.util.List; - import static androidx.appcompat.widget.SearchView.OnQueryTextListener; -import static androidx.appcompat.widget.SearchView.OnSuggestionListener; /** * Consists of the search bar, search button, and search settings button @@ -59,16 +52,9 @@ public class ImportKeysSearchFragment extends Fragment { public static final String ARG_QUERY = "query"; public static final String ARG_CLOUD_SEARCH_PREFS = "cloud_search_prefs"; - private static final String CURSOR_SUGGESTION = "suggestion"; - private Activity mActivity; private ImportKeysListener mCallback; - private List mNamesAndEmails; - private SimpleCursorAdapter mSearchAdapter; - - private List mCurrentSuggestions = new ArrayList<>(); - /** * Creates new instance of this fragment * @@ -92,14 +78,6 @@ public class ImportKeysSearchFragment extends Fragment { @Override public View onCreateView(LayoutInflater i, ViewGroup c, Bundle savedInstanceState) { - ContactHelper contactHelper = new ContactHelper(mActivity); - mNamesAndEmails = contactHelper.getContactNames(); - mNamesAndEmails.addAll(contactHelper.getContactMails()); - - mSearchAdapter = new SimpleCursorAdapter(mActivity, - R.layout.import_keys_cloud_suggestions_item, null, new String[]{CURSOR_SUGGESTION}, - new int[]{android.R.id.text1}, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); - setHasOptionsMenu(true); // no view, just search view @@ -126,21 +104,7 @@ public class ImportKeysSearchFragment extends Fragment { MenuItem searchItem = menu.findItem(R.id.menu_import_keys_cloud_search); final SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem); - searchView.setSuggestionsAdapter(mSearchAdapter); - - searchView.setOnSuggestionListener(new OnSuggestionListener() { - @Override - public boolean onSuggestionSelect(int position) { - searchView.setQuery(mCurrentSuggestions.get(position), true); - return true; - } - - @Override - public boolean onSuggestionClick(int position) { - searchView.setQuery(mCurrentSuggestions.get(position), true); - return true; - } - }); + searchView.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); searchView.setOnQueryTextListener(new OnQueryTextListener() { @Override @@ -152,7 +116,6 @@ public class ImportKeysSearchFragment extends Fragment { @Override public boolean onQueryTextChange(String newText) { - updateAdapter(newText); return false; } }); @@ -180,20 +143,6 @@ public class ImportKeysSearchFragment extends Fragment { super.onCreateOptionsMenu(menu, inflater); } - private void updateAdapter(String query) { - mCurrentSuggestions.clear(); - MatrixCursor c = new MatrixCursor(new String[]{BaseColumns._ID, CURSOR_SUGGESTION}); - for (int i = 0; i < mNamesAndEmails.size(); i++) { - String s = mNamesAndEmails.get(i); - if (s.toLowerCase().startsWith(query.toLowerCase())) { - mCurrentSuggestions.add(s); - c.addRow(new Object[]{i, s}); - } - - } - mSearchAdapter.changeCursor(c); - } - @Override public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEmailDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEmailDialogFragment.java index 969e840bf..94b2c5379 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEmailDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEmailDialogFragment.java @@ -27,6 +27,8 @@ import android.os.Bundle; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; + +import androidx.appcompat.widget.AppCompatEditText; import androidx.fragment.app.DialogFragment; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -38,7 +40,6 @@ import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.widget.EmailEditText; import timber.log.Timber; @@ -51,7 +52,7 @@ public class AddEmailDialogFragment extends DialogFragment implements OnEditorAc public static final String MESSAGE_DATA_EMAIL = "email"; private Messenger mMessenger; - private EmailEditText mEmail; + private AppCompatEditText mEmail; public static AddEmailDialogFragment newInstance(Messenger messenger) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java index be3625f04..37511b2ef 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java @@ -17,8 +17,8 @@ package org.sufficientlysecure.keychain.ui.dialog; + import android.app.Activity; -import androidx.appcompat.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; @@ -27,7 +27,6 @@ import android.os.Bundle; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; -import androidx.fragment.app.DialogFragment; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -37,11 +36,12 @@ import android.widget.Button; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.widget.AppCompatEditText; +import androidx.fragment.app.DialogFragment; import org.openintents.openpgp.util.OpenPgpUtils; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.ui.widget.EmailEditText; -import org.sufficientlysecure.keychain.ui.widget.NameEditText; import timber.log.Timber; @@ -55,8 +55,8 @@ public class AddUserIdDialogFragment extends DialogFragment implements OnEditorA public static final String MESSAGE_DATA_USER_ID = "user_id"; private Messenger mMessenger; - private NameEditText mName; - private EmailEditText mEmail; + private AppCompatEditText mName; + private AppCompatEditText mEmail; public static AddUserIdDialogFragment newInstance(Messenger messenger, String predefinedName) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/KeyFragmentViewModel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/KeyFragmentViewModel.java index 80f131b8d..099d87f0b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/KeyFragmentViewModel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/KeyFragmentViewModel.java @@ -16,14 +16,11 @@ 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> identityInfo; private LiveData subkeyStatus; - private LiveData systemContactInfo; private LiveData keyserverStatus; LiveData> getIdentityInfo(Context context, LiveData unifiedKeyInfoLiveData) { @@ -46,17 +43,6 @@ public class KeyFragmentViewModel extends ViewModel { return subkeyStatus; } - LiveData getSystemContactInfo(Context context, LiveData unifiedKeyInfoLiveData) { - if (systemContactInfo == null) { - SystemContactDao systemContactDao = SystemContactDao.getInstance(context); - systemContactInfo = Transformations.switchMap(unifiedKeyInfoLiveData, - (unifiedKeyInfo) -> unifiedKeyInfo == null ? null : new GenericLiveData<>(context, - () -> systemContactDao.getSystemContactInfo(unifiedKeyInfo.master_key_id(), - unifiedKeyInfo.has_any_secret()))); - } - return systemContactInfo; - } - LiveData getKeyserverStatus(Context context, LiveData unifiedKeyInfoLiveData) { if (keyserverStatus == null) { KeyMetadataDao keyMetadataDao = KeyMetadataDao.create(context); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java index 5bb3393d9..b3193de09 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java @@ -28,11 +28,9 @@ import android.animation.ObjectAnimator; import android.annotation.SuppressLint; import android.app.Activity; import android.app.ActivityOptions; -import androidx.lifecycle.ViewModelProviders; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; -import android.graphics.PorterDuff; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; @@ -40,17 +38,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; -import android.provider.ContactsContract; -import androidx.annotation.IntDef; -import androidx.annotation.NonNull; -import com.google.android.material.appbar.AppBarLayout; -import com.google.android.material.appbar.CollapsingToolbarLayout; -import androidx.coordinatorlayout.widget.CoordinatorLayout; -import com.google.android.material.floatingactionbutton.FloatingActionButton; -import androidx.core.app.ActivityCompat; -import androidx.fragment.app.FragmentManager; -import androidx.core.content.ContextCompat; -import androidx.cardview.widget.CardView; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -58,13 +45,22 @@ import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.AnimationUtils; -import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; -import android.widget.Toast; +import androidx.annotation.IntDef; +import androidx.annotation.NonNull; +import androidx.cardview.widget.CardView; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.FragmentManager; +import androidx.lifecycle.ViewModelProviders; +import com.google.android.material.appbar.AppBarLayout; +import com.google.android.material.appbar.CollapsingToolbarLayout; +import com.google.android.material.floatingactionbutton.FloatingActionButton; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.daos.KeyRepository; @@ -102,10 +98,8 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.ui.util.QrCodeUtils; -import org.sufficientlysecure.keychain.util.ContactHelper; import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.ShareKeyHelper; -import timber.log.Timber; public class ViewKeyActivity extends BaseSecurityTokenActivity { @@ -121,7 +115,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity { 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; @@ -140,8 +133,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity { private ImageButton actionShare; private ImageButton actionShareClipboard; private FloatingActionButton floatingActionButton; - private ImageView photoView; - private FrameLayout photoLayout; private ImageView qrCodeView; private CardView qrCodeLayout; @@ -181,8 +172,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity { actionShare= findViewById(R.id.view_key_action_share); actionShareClipboard = findViewById(R.id.view_key_action_share_clipboard); floatingActionButton = findViewById(R.id.fab); - photoView = findViewById(R.id.view_key_photo); - photoLayout = findViewById(R.id.view_key_photo_layout); qrCodeView = findViewById(R.id.view_key_qr_code); qrCodeLayout = findViewById(R.id.view_key_qr_code_layout); @@ -243,20 +232,10 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity { long masterKeyId; 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"); + throw new IllegalArgumentException("Missing required extra master_key_id"); } actionEncryptFile.setOnClickListener(v -> encrypt(false)); @@ -660,25 +639,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity { // this is done at the end of the animation otherwise } - AsyncTask photoTask = - new AsyncTask() { - protected Bitmap doInBackground(Long... mMasterKeyId) { - return new ContactHelper(ViewKeyActivity.this) - .loadPhotoByMasterKeyId(mMasterKeyId[0], true); - } - - protected void onPostExecute(Bitmap photo) { - if (photo == null) { - return; - } - - photoView.setImageBitmap(photo); - photoView.setColorFilter(ContextCompat.getColor(ViewKeyActivity.this, R.color.toolbar_photo_tint), - PorterDuff.Mode.SRC_ATOP); - photoLayout.setVisibility(View.VISIBLE); - } - }; - boolean showStatusText = unifiedKeyInfo.is_secure() && !unifiedKeyInfo.is_expired() && !unifiedKeyInfo.is_revoked(); if (showStatusText) { statusText.setVisibility(View.VISIBLE); @@ -733,7 +693,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity { if (!Arrays.equals(unifiedKeyInfo.fingerprint(), qrCodeLoaded)) { loadQrCode(unifiedKeyInfo.fingerprint()); } - photoTask.execute(unifiedKeyInfo.master_key_id()); qrCodeLayout.setVisibility(View.VISIBLE); // and place leftOf qr code @@ -775,7 +734,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity { KeyFormattingUtils.setStatusImage(this, statusImage, statusText, State.VERIFIED, R.color.icons, true); color = ContextCompat.getColor(this, R.color.key_flag_green); - photoTask.execute(unifiedKeyInfo.master_key_id()); hideFab(); } else { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java index d6e39aeeb..b8d7d1d79 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java @@ -20,25 +20,21 @@ package org.sufficientlysecure.keychain.ui.keyview; import java.util.List; -import androidx.lifecycle.LiveData; -import androidx.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 androidx.annotation.NonNull; -import androidx.fragment.app.DialogFragment; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentTransaction; -import androidx.appcompat.widget.PopupMenu; -import androidx.appcompat.widget.PopupMenu.OnMenuItemClickListener; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.appcompat.widget.PopupMenu; +import androidx.appcompat.widget.PopupMenu.OnMenuItemClickListener; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.ViewModelProvider; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.daos.AutocryptPeerDao; @@ -55,18 +51,15 @@ 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.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 timber.log.Timber; public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener { private IdentitiesCardView identitiesCardView; - private SystemContactCardView systemContactCard; private KeyHealthView keyStatusHealth; private KeyserverStatusView keyserverStatusView; private View keyStatusCardView; @@ -87,7 +80,6 @@ public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener View view = inflater.inflate(R.layout.view_key_fragment, viewGroup, false); identitiesCardView = view.findViewById(R.id.card_identities); - systemContactCard = view.findViewById(R.id.linked_system_contact_card); keyStatusCardView = view.findViewById(R.id.subkey_status_card); keyStatusHealth = view.findViewById(R.id.key_status_health); keyserverStatusView = view.findViewById(R.id.key_status_keyserver); @@ -118,17 +110,16 @@ public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener Context context = requireContext(); - UnifiedKeyInfoViewModel viewKeyViewModel = ViewModelProviders.of(requireActivity()).get(UnifiedKeyInfoViewModel.class); + UnifiedKeyInfoViewModel viewKeyViewModel = new ViewModelProvider(requireActivity()).get(UnifiedKeyInfoViewModel.class); LiveData unifiedKeyInfoLiveData = viewKeyViewModel.getUnifiedKeyInfoLiveData(requireContext()); - unifiedKeyInfoLiveData.observe(this, this::onLoadUnifiedKeyInfo); + unifiedKeyInfoLiveData.observe(getViewLifecycleOwner(), this::onLoadUnifiedKeyInfo); - KeyFragmentViewModel model = ViewModelProviders.of(this).get(KeyFragmentViewModel.class); + KeyFragmentViewModel model = new ViewModelProvider(this).get(KeyFragmentViewModel.class); - model.getIdentityInfo(context, unifiedKeyInfoLiveData).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); + model.getIdentityInfo(context, unifiedKeyInfoLiveData).observe(getViewLifecycleOwner(), this::onLoadIdentityInfo); + model.getKeyserverStatus(context, unifiedKeyInfoLiveData).observe(getViewLifecycleOwner(), this::onLoadKeyMetadata); + model.getSubkeyStatus(context, unifiedKeyInfoLiveData).observe(getViewLifecycleOwner(), this::onLoadSubkeyStatus); } private void onLoadSubkeyStatus(KeySubkeyStatus subkeyStatus) { @@ -274,16 +265,6 @@ public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener identitiesAdapter.setData(identityInfos); } - 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(); @@ -299,14 +280,6 @@ public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener } } - public void switchToFragment(final Fragment frag, final String backStackName) { - new Handler().post(() -> requireFragmentManager().beginTransaction() - .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) - .replace(R.id.view_key_fragment, frag) - .addToBackStack(backStackName) - .commit()); - } - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { // if a result has been returned, display a notify @@ -320,7 +293,7 @@ public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener public void showDialogFragment(final DialogFragment dialogFragment, final String tag) { DialogFragmentWorkaround.INTERFACE.runnableRunDelayed( - () -> dialogFragment.show(requireFragmentManager(), tag)); + () -> dialogFragment.show(getParentFragmentManager(), tag)); } public void showContextMenu(int position, View anchor) { @@ -349,11 +322,4 @@ public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener 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); - } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/SystemContactDao.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/SystemContactDao.java deleted file mode 100644 index 5b0f59055..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/SystemContactDao.java +++ /dev/null @@ -1,137 +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 . - */ - -package org.sufficientlysecure.keychain.ui.keyview.loader; - - -import java.util.List; - -import android.Manifest; -import android.content.ContentResolver; -import android.content.Context; -import android.content.pm.PackageManager; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.net.Uri; -import android.provider.ContactsContract; -import androidx.core.content.ContextCompat; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.util.ContactHelper; -import timber.log.Timber; - - -public class SystemContactDao { - private static final String[] PROJECTION = { - ContactsContract.RawContacts.CONTACT_ID - }; - private static final int INDEX_CONTACT_ID = 0; - private static final String CONTACT_NOT_DELETED = "0"; - - - private final Context context; - private final ContentResolver contentResolver; - private final ContactHelper contactHelper; - - - public static SystemContactDao getInstance(Context context) { - ContactHelper contactHelper = new ContactHelper(context); - ContentResolver contentResolver = context.getContentResolver(); - return new SystemContactDao(context, contactHelper, contentResolver); - } - - private SystemContactDao(Context context, ContactHelper contactHelper, ContentResolver contentResolver) { - this.context = context; - this.contactHelper = contactHelper; - this.contentResolver = contentResolver; - } - - 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!"); - return null; - } - - Uri baseUri = isSecret ? ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI : - ContactsContract.RawContacts.CONTENT_URI; - Cursor cursor = contentResolver.query(baseUri, PROJECTION, - ContactsContract.RawContacts.ACCOUNT_TYPE + " = ? AND " + - ContactsContract.RawContacts.SOURCE_ID + " = ? AND " + - ContactsContract.RawContacts.DELETED + " = ?", - new String[] { - Constants.ACCOUNT_TYPE, - Long.toString(masterKeyId), - CONTACT_NOT_DELETED - }, null); - - if (cursor == null) { - Timber.e("Error loading key items!"); - return null; - } - - try { - if (!cursor.moveToFirst()) { - return null; - } - - long contactId = cursor.getLong(INDEX_CONTACT_ID); - if (contactId == -1) { - return null; - } - - 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) { - return null; - } - - Bitmap contactPicture; - if (isSecret) { - contactPicture = contactHelper.loadMainProfilePhoto(false); - } else { - contactPicture = contactHelper.loadPhotoByContactId(contactId, false); - } - - return new SystemContactInfo(masterKeyId, contactId, contactName, contactPicture); - } finally { - cursor.close(); - } - } - - public static class SystemContactInfo { - final long masterKeyId; - public final long contactId; - public final String contactName; - public final Bitmap contactPicture; - - SystemContactInfo(long masterKeyId, long contactId, String contactName, Bitmap contactPicture) { - this.masterKeyId = masterKeyId; - this.contactId = contactId; - this.contactName = contactName; - this.contactPicture = contactPicture; - } - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/view/SystemContactCardView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/view/SystemContactCardView.java deleted file mode 100644 index 1d487a8a7..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/view/SystemContactCardView.java +++ /dev/null @@ -1,67 +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 . - */ - -package org.sufficientlysecure.keychain.ui.keyview.view; - - -import android.content.Context; -import android.graphics.Bitmap; -import androidx.cardview.widget.CardView; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import org.sufficientlysecure.keychain.R; - - -public class SystemContactCardView extends CardView { - private LinearLayout vSystemContactLayout; - private ImageView vSystemContactPicture; - private TextView vSystemContactName; - - public SystemContactCardView(Context context, AttributeSet attrs) { - super(context, attrs); - - View view = LayoutInflater.from(context).inflate(R.layout.system_contact_card, this, true); - - vSystemContactLayout = view.findViewById(R.id.system_contact_layout); - vSystemContactName = view.findViewById(R.id.system_contact_name); - vSystemContactPicture = view.findViewById(R.id.system_contact_picture); - } - - public void setSystemContactClickListener(OnClickListener onClickListener) { - vSystemContactLayout.setOnClickListener(onClickListener); - } - - public void hideLinkedSystemContact() { - setVisibility(View.GONE); - } - - 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/EmailEditText.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java deleted file mode 100644 index ad15ef62e..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java +++ /dev/null @@ -1,88 +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 . - */ - -package org.sufficientlysecure.keychain.ui.widget; - -import android.content.Context; -import androidx.appcompat.widget.AppCompatAutoCompleteTextView; -import android.text.Editable; -import android.text.InputType; -import android.text.TextWatcher; -import android.util.AttributeSet; -import android.view.inputmethod.EditorInfo; -import android.widget.ArrayAdapter; - -import org.sufficientlysecure.keychain.util.ContactHelper; - -public class EmailEditText extends AppCompatAutoCompleteTextView { - - public EmailEditText(Context context) { - super(context); - init(); - } - - public EmailEditText(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public EmailEditText(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - } - - private void init() { - setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); - reenableKeyboardSuggestions(); - - addTextChangedListener(textWatcher); - initAdapter(); - } - - TextWatcher textWatcher = new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - - } - - @Override - public void afterTextChanged(Editable editable) { - - } - }; - - private void initAdapter() { - setThreshold(1); // Start working from first character - setAdapter(new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, - new ContactHelper(getContext()).getPossibleUserEmails())); - } - - /** - * Hack to re-enable keyboard auto correction in AutoCompleteTextView. - * From http://stackoverflow.com/a/22512858 - */ - private void reenableKeyboardSuggestions() { - int inputType = getInputType(); - inputType &= ~EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE; - setRawInputType(inputType); - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NameEditText.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NameEditText.java deleted file mode 100644 index b9b266f36..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NameEditText.java +++ /dev/null @@ -1,65 +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 . - */ - -package org.sufficientlysecure.keychain.ui.widget; - -import android.content.Context; -import androidx.appcompat.widget.AppCompatAutoCompleteTextView; -import android.util.AttributeSet; -import android.view.inputmethod.EditorInfo; -import android.widget.ArrayAdapter; - -import org.sufficientlysecure.keychain.util.ContactHelper; - -public class NameEditText extends AppCompatAutoCompleteTextView { - public NameEditText(Context context) { - super(context); - init(); - } - - public NameEditText(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public NameEditText(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - } - - private void init() { - reenableKeyboardSuggestions(); - initAdapter(); - } - - private void initAdapter() { - setThreshold(1); // Start working from first character - setAdapter(new ArrayAdapter<>( - getContext(), android.R.layout.simple_spinner_dropdown_item, - new ContactHelper(getContext()).getPossibleUserNames())); - } - - /** - * Hack to re-enable keyboard suggestions in AutoCompleteTextView. - * From http://stackoverflow.com/a/22512858 - */ - private void reenableKeyboardSuggestions() { - int inputType = getInputType(); - inputType &= ~EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE; - setRawInputType(inputType); - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java deleted file mode 100644 index ceca99564..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java +++ /dev/null @@ -1,850 +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 . - */ - -package org.sufficientlysecure.keychain.util; - - -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.regex.Matcher; - -import android.Manifest; -import android.accounts.Account; -import android.accounts.AccountManager; -import android.content.ContentProviderOperation; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.Context; -import android.content.pm.PackageManager; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.Uri; -import android.os.Build; -import android.provider.ContactsContract; -import android.provider.ContactsContract.CommonDataKinds.Email; -import android.provider.ContactsContract.CommonDataKinds.Im; -import android.provider.ContactsContract.Data; -import androidx.core.content.ContextCompat; -import android.util.Patterns; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; -import org.sufficientlysecure.keychain.model.UserPacket.UserId; -import org.sufficientlysecure.keychain.daos.KeyRepository; -import timber.log.Timber; - -public class ContactHelper { - private final KeyRepository keyRepository; - - private Context mContext; - private ContentResolver mContentResolver; - - public ContactHelper(Context context) { - mContext = context; - mContentResolver = context.getContentResolver(); - keyRepository = KeyRepository.create(context); - } - - public List getPossibleUserEmails() { - Set accountMails = getAccountEmails(); - accountMails.addAll(getMainProfileContactEmails()); - - // remove items that are not an email - Iterator it = accountMails.iterator(); - while (it.hasNext()) { - String email = it.next(); - Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email); - if (!emailMatcher.matches()) { - it.remove(); - } - } - - // now return the Set (without duplicates) as a List - return new ArrayList<>(accountMails); - } - - public List getPossibleUserNames() { - Set accountMails = getAccountEmails(); - Set names = getContactNamesFromEmails(accountMails); - names.addAll(getMainProfileContactName()); - - // remove items that are an email - Iterator it = names.iterator(); - while (it.hasNext()) { - String email = it.next(); - Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email); - if (emailMatcher.matches()) { - it.remove(); - } - } - - return new ArrayList<>(names); - } - - /** - * Get emails from AccountManager - */ - private Set getAccountEmails() { - final Account[] accounts = AccountManager.get(mContext).getAccounts(); - final Set emailSet = new HashSet<>(); - for (Account account : accounts) { - emailSet.add(account.name); - } - return emailSet; - } - - /** - * Search for contact names based on a list of emails (to find out the names of the - * device owner based on the email addresses from AccountsManager) - */ - private Set getContactNamesFromEmails(Set emails) { - if (!isContactsPermissionGranted()) { - return new HashSet<>(); - } - - Set names = new HashSet<>(); - for (String email : emails) { - Cursor profileCursor = mContentResolver.query( - ContactsContract.CommonDataKinds.Email.CONTENT_URI, - new String[]{ - ContactsContract.CommonDataKinds.Email.ADDRESS, - ContactsContract.Contacts.DISPLAY_NAME - }, - ContactsContract.CommonDataKinds.Email.ADDRESS + "=?", - new String[]{email}, null - ); - if (profileCursor == null) { - return null; - } - - Set currNames = new HashSet<>(); - while (profileCursor.moveToNext()) { - String name = profileCursor.getString(1); - if (name != null) { - currNames.add(name); - } - } - profileCursor.close(); - names.addAll(currNames); - } - return names; - } - - /** - * Retrieves the emails of the primary profile contact - * http://developer.android.com/reference/android/provider/ContactsContract.Profile.html - */ - private Set getMainProfileContactEmails() { - if (!isContactsPermissionGranted()) { - return new HashSet<>(); - } - - Cursor profileCursor = mContentResolver.query( - Uri.withAppendedPath( - ContactsContract.Profile.CONTENT_URI, - ContactsContract.Contacts.Data.CONTENT_DIRECTORY), - new String[]{ - ContactsContract.CommonDataKinds.Email.ADDRESS, - ContactsContract.CommonDataKinds.Email.IS_PRIMARY - }, - // Selects only email addresses - ContactsContract.Contacts.Data.MIMETYPE + "=?", - new String[]{ - ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE, - }, - // Show primary rows first. Note that there won't be a primary email address if the - // user hasn't specified one. - ContactsContract.Contacts.Data.IS_PRIMARY + " DESC" - ); - if (profileCursor == null) { - return new HashSet<>(); - } - - Set emails = new HashSet<>(); - while (profileCursor.moveToNext()) { - String email = profileCursor.getString(0); - if (email != null) { - emails.add(email); - } - } - profileCursor.close(); - return emails; - } - - /** - * Retrieves the name of the primary profile contact - * http://developer.android.com/reference/android/provider/ContactsContract.Profile.html - */ - public List getMainProfileContactName() { - if (!isContactsPermissionGranted()) { - return new ArrayList<>(); - } - - Cursor profileCursor = mContentResolver.query( - ContactsContract.Profile.CONTENT_URI, - new String[]{ - ContactsContract.Profile.DISPLAY_NAME - }, - null, null, null); - if (profileCursor == null) { - return new ArrayList<>(); - } - - Set names = new HashSet<>(); - // should only contain one entry! - while (profileCursor.moveToNext()) { - String name = profileCursor.getString(0); - if (name != null) { - names.add(name); - } - } - profileCursor.close(); - return new ArrayList<>(names); - } - - /** - * returns the CONTACT_ID of the main ("me") contact - * http://developer.android.com/reference/android/provider/ContactsContract.Profile.html - */ - private long getMainProfileContactId() { - Cursor profileCursor = mContentResolver.query(ContactsContract.Profile.CONTENT_URI, - new String[]{ContactsContract.Profile._ID}, null, null, null); - - if (profileCursor != null && profileCursor.getCount() != 0 && profileCursor.moveToNext()) { - long contactId = profileCursor.getLong(0); - profileCursor.close(); - return contactId; - } else { - if (profileCursor != null) { - profileCursor.close(); - } - return -1; - } - } - - /** - * loads the profile picture of the main ("me") contact - * http://developer.android.com/reference/android/provider/ContactsContract.Profile.html - * - * @param highRes true for large image if present, false for thumbnail - * @return bitmap of loaded photo - */ - public Bitmap loadMainProfilePhoto(boolean highRes) { - if (!isContactsPermissionGranted()) { - return null; - } - - try { - long mainProfileContactId = getMainProfileContactId(); - - Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, - Long.toString(mainProfileContactId)); - InputStream photoInputStream = ContactsContract.Contacts.openContactPhotoInputStream( - mContentResolver, - contactUri, - highRes - ); - if (photoInputStream == null) { - return null; - } - return BitmapFactory.decodeStream(photoInputStream); - } catch (Throwable ignored) { - return null; - } - } - - public List getContactMails() { - if (!isContactsPermissionGranted()) { - return new ArrayList<>(); - } - - Cursor mailCursor = mContentResolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, - new String[]{ContactsContract.CommonDataKinds.Email.DATA}, - null, null, null); - if (mailCursor == null) { - return new ArrayList<>(); - } - - Set mails = new HashSet<>(); - while (mailCursor.moveToNext()) { - String email = mailCursor.getString(0); - if (email != null) { - mails.add(email); - } - } - mailCursor.close(); - return new ArrayList<>(mails); - } - - public List getContactNames() { - if (!isContactsPermissionGranted()) { - return new ArrayList<>(); - } - - Cursor cursor = mContentResolver.query(ContactsContract.Contacts.CONTENT_URI, - new String[]{ContactsContract.Contacts.DISPLAY_NAME}, - null, null, null); - if (cursor == null) { - return new ArrayList<>(); - } - - Set names = new HashSet<>(); - while (cursor.moveToNext()) { - String name = cursor.getString(0); - if (name != null) { - names.add(name); - } - } - cursor.close(); - return new ArrayList<>(names); - } - - public Long masterKeyIdFromContactsDataUri(Uri contactUri) { - if (!isContactsPermissionGranted()) { - return null; - } - - Cursor contactMasterKey = mContentResolver.query(contactUri, - new String[]{ContactsContract.Data.DATA2}, null, null, null); - if (contactMasterKey != null) { - try { - if (contactMasterKey.moveToNext()) { - return contactMasterKey.getLong(0); - } - } finally { - contactMasterKey.close(); - } - } - return null; - } - - /** - * returns the CONTACT_ID of the raw contact to which a masterKeyId is associated, if the - * raw contact has not been marked for deletion. - * - * @return CONTACT_ID (id of aggregated contact) linked to masterKeyId - */ - private long findContactId(long masterKeyId) { - long contactId = -1; - Cursor raw = mContentResolver.query(ContactsContract.RawContacts.CONTENT_URI, - new String[]{ - ContactsContract.RawContacts.CONTACT_ID - }, - ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + - ContactsContract.RawContacts.SOURCE_ID + "=? AND " + - ContactsContract.RawContacts.DELETED + "=?", - new String[]{//"0" for "not deleted" - Constants.ACCOUNT_TYPE, - Long.toString(masterKeyId), - "0" - }, null); - if (raw != null) { - if (raw.moveToNext()) { - contactId = raw.getLong(0); - } - raw.close(); - } - return contactId; - } - - /** - * Returns the display name of the system contact associated with contactId, null if the - * contact does not exist - * - * @return primary display name of system contact associated with contactId, null if it does - * not exist - */ - public String getContactName(long contactId) { - String contactName = null; - Cursor raw = mContentResolver.query(ContactsContract.Contacts.CONTENT_URI, - new String[]{ - ContactsContract.Contacts.DISPLAY_NAME_PRIMARY - }, - ContactsContract.Contacts._ID + "=?", - new String[]{//"0" for "not deleted" - Long.toString(contactId) - }, null); - if (raw != null) { - if (raw.moveToNext()) { - contactName = raw.getString(0); - } - raw.close(); - } - return contactName; - } - - public Bitmap loadPhotoByMasterKeyId(long masterKeyId, boolean highRes) { - if (!isContactsPermissionGranted()) { - return null; - } - - if (masterKeyId == -1) { - return null; - } - try { - long contactId = findContactId(masterKeyId); - return loadPhotoByContactId(contactId, highRes); - - } catch (Throwable ignored) { - return null; - } - } - - public Bitmap loadPhotoByContactId(long contactId, boolean highRes) { - if (!isContactsPermissionGranted()) { - return null; - } - - if (contactId == -1) { - return null; - } - Uri contactUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId); - // older android versions (tested on API level 15) fail on lookupuris being passed to - // openContactPhotoInputStream - // http://stackoverflow.com/a/21214524/3000919 - // Uri lookupUri = ContactsContract.Contacts.getLookupUri(contentResolver, contactUri); - // Also, we don't need a permanent shortcut to the contact since we load it afresh each time - - InputStream photoInputStream = ContactsContract.Contacts.openContactPhotoInputStream( - mContentResolver, - contactUri, - highRes); - - if (photoInputStream == null) { - return null; - } - return BitmapFactory.decodeStream(photoInputStream); - } - - /** - * Write/Update the current OpenKeychain keys to the contact db - */ - public void writeKeysToContacts() { - if (Constants.DEBUG_SYNC_REMOVE_CONTACTS) { - deleteAllContacts(); - } - - writeKeysToMainProfileContact(); - - writeKeysToNormalContacts(); - } - - private boolean isContactsPermissionGranted() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - return true; - } - - if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.READ_CONTACTS) - == PackageManager.PERMISSION_GRANTED) { - return true; - } - - Timber.w("READ_CONTACTS permission denied!"); - return false; - } - - private void writeKeysToNormalContacts() { - // delete raw contacts flagged for deletion by user so they can be reinserted - deleteFlaggedNormalRawContacts(); - - Set deletedKeys = getRawContactMasterKeyIds(); - - // Load all public Keys from OK - for (UnifiedKeyInfo keyInfo : keyRepository.getAllUnifiedKeyInfo()) { - if (keyInfo.has_any_secret()) { - continue; - } - - long masterKeyId = keyInfo.master_key_id(); - String name = keyInfo.name(); - - deletedKeys.remove(masterKeyId); - - ArrayList ops = new ArrayList<>(); - - // Do not store expired or revoked or unverified keys in contact db - and - // remove them if they already exist. Secret keys do not reach this point - if (keyInfo.is_expired() || keyInfo.is_revoked() || !keyInfo.is_verified()) { - Timber.d("Expired or revoked or unverified: Deleting masterKeyId " - + masterKeyId); - if (masterKeyId != -1) { - deleteRawContactByMasterKeyId(masterKeyId); - } - } else { - if (name != null) { - - // get raw contact to this master key id - long rawContactId = findRawContactId(masterKeyId); - Timber.d("rawContactId: " + rawContactId); - - // Create a new rawcontact with corresponding key if it does not exist yet - if (rawContactId == -1) { - Timber.d("Insert new raw contact with masterKeyId " + masterKeyId); - - insertContact(ops, masterKeyId); - writeContactKey(ops, rawContactId, masterKeyId, name); - } - - // We always update the display name (which is derived from primary user id) - // and email addresses from user id - writeContactDisplayName(ops, rawContactId, name); - writeContactEmail(ops, rawContactId, masterKeyId); - try { - mContentResolver.applyBatch(ContactsContract.AUTHORITY, ops); - } catch (Exception e) { - Timber.w(e); - } - } - } - } - - // Delete master key ids that are no longer present in OK - for (Long masterKeyId : deletedKeys) { - Timber.d("Delete raw contact with masterKeyId " + masterKeyId); - deleteRawContactByMasterKeyId(masterKeyId); - } - } - - /** - * Links all keys with secrets to the main ("me") contact - * http://developer.android.com/reference/android/provider/ContactsContract.Profile.html - */ - private void writeKeysToMainProfileContact() { - // deletes contacts hidden by the user so they can be reinserted if necessary - deleteFlaggedMainProfileRawContacts(); - - Set keysToDelete = getMainProfileMasterKeyIds(); - - // get all keys which have associated secret keys - // TODO: figure out why using selectionArgs does not work in this case - for (UnifiedKeyInfo keyInfo : keyRepository.getAllUnifiedKeyInfoWithSecret()) { - long masterKeyId = keyInfo.master_key_id(); - - if (!keyInfo.is_expired() && !keyInfo.is_revoked() && keyInfo.name() != null) { - // if expired or revoked will not be removed from keysToDelete or inserted - // into main profile ("me" contact) - boolean existsInMainProfile = keysToDelete.remove(masterKeyId); - if (!existsInMainProfile) { - long rawContactId = -1;//new raw contact - - Timber.d("masterKeyId with secret " + masterKeyId); - - ArrayList ops = new ArrayList<>(); - insertMainProfileRawContact(ops, masterKeyId); - writeContactKey(ops, rawContactId, masterKeyId, keyInfo.name()); - - try { - mContentResolver.applyBatch(ContactsContract.AUTHORITY, ops); - } catch (Exception e) { - Timber.w(e); - } - } - } - } - - for (long masterKeyId : keysToDelete) { - deleteMainProfileRawContactByMasterKeyId(masterKeyId); - Timber.d("Delete main profile raw contact with masterKeyId " + masterKeyId); - } - } - - /** - * Inserts a raw contact into the table defined by ContactsContract.Profile - * http://developer.android.com/reference/android/provider/ContactsContract.Profile.html - */ - private void insertMainProfileRawContact(ArrayList ops, - long masterKeyId) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI) - .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, Constants.ACCOUNT_NAME) - .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, Constants.ACCOUNT_TYPE) - .withValue(ContactsContract.RawContacts.SOURCE_ID, Long.toString(masterKeyId)) - .build()); - } - - /** - * deletes a raw contact from the main profile table ("me" contact) - * http://developer.android.com/reference/android/provider/ContactsContract.Profile.html - * - * @return number of rows deleted - */ - private int deleteMainProfileRawContactByMasterKeyId(long masterKeyId) { - // CALLER_IS_SYNCADAPTER allows us to actually wipe the RawContact from the device, otherwise - // would be just flagged for deletion - Uri deleteUri = ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI.buildUpon(). - appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build(); - - return mContentResolver.delete(deleteUri, - ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + - ContactsContract.RawContacts.SOURCE_ID + "=?", - new String[]{ - Constants.ACCOUNT_TYPE, Long.toString(masterKeyId) - }); - } - - /** - * deletes all raw contact entries in the "me" contact flagged for deletion ('hidden'), - * presumably by the user - * - * @return number of raw contacts deleted - */ - private int deleteFlaggedMainProfileRawContacts() { - // CALLER_IS_SYNCADAPTER allows us to actually wipe the RawContact from the device, otherwise - // would be just flagged for deletion - Uri deleteUri = ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI.buildUpon(). - appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build(); - - return mContentResolver.delete(deleteUri, - ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + - ContactsContract.RawContacts.DELETED + "=?", - new String[]{ - Constants.ACCOUNT_TYPE, - "1" - }); - } - - /** - * Delete all raw contacts associated to OpenKeychain, including those from "me" contact - * defined by ContactsContract.Profile - * - * @return number of rows deleted - */ - public int deleteAllContacts() { - // CALLER_IS_SYNCADAPTER allows us to actually wipe the RawContact from the device, otherwise - // would be just flagged for deletion - Uri deleteUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon(). - appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build(); - - Timber.d("Deleting all raw contacts associated to OK..."); - int delete = mContentResolver.delete(deleteUri, - ContactsContract.RawContacts.ACCOUNT_TYPE + "=?", - new String[]{ - Constants.ACCOUNT_TYPE - }); - - Uri mainProfileDeleteUri = ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI.buildUpon() - .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build(); - - delete += mContentResolver.delete(mainProfileDeleteUri, - ContactsContract.RawContacts.ACCOUNT_TYPE + "=?", - new String[]{ - Constants.ACCOUNT_TYPE - }); - - return delete; - } - - /** - * Deletes raw contacts from ContactsContract.RawContacts based on masterKeyId. Does not - * delete contacts from the "me" contact defined in ContactsContract.Profile - * - * @return number of rows deleted - */ - private int deleteRawContactByMasterKeyId(long masterKeyId) { - // CALLER_IS_SYNCADAPTER allows us to actually wipe the RawContact from the device, otherwise - // would be just flagged for deletion - Uri deleteUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon(). - appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build(); - - return mContentResolver.delete(deleteUri, - ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + - ContactsContract.RawContacts.SOURCE_ID + "=?", - new String[]{ - Constants.ACCOUNT_TYPE, Long.toString(masterKeyId) - }); - } - - private int deleteFlaggedNormalRawContacts() { - // CALLER_IS_SYNCADAPTER allows us to actually wipe the RawContact from the device, otherwise - // would be just flagged for deletion - Uri deleteUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon(). - appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build(); - - return mContentResolver.delete(deleteUri, - ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + - ContactsContract.RawContacts.DELETED + "=?", - new String[]{ - Constants.ACCOUNT_TYPE, - "1" - }); - } - - /** - * @return a set of all key master key ids currently present in the contact db - */ - private Set getRawContactMasterKeyIds() { - HashSet result = new HashSet<>(); - Cursor masterKeyIds = mContentResolver.query(ContactsContract.RawContacts.CONTENT_URI, - new String[]{ - ContactsContract.RawContacts.SOURCE_ID - }, - ContactsContract.RawContacts.ACCOUNT_TYPE + "=?", - new String[]{ - Constants.ACCOUNT_TYPE - }, null); - if (masterKeyIds != null) { - while (masterKeyIds.moveToNext()) { - result.add(masterKeyIds.getLong(0)); - } - masterKeyIds.close(); - } - return result; - } - - /** - * @return a set of all key master key ids currently present in the contact db - */ - private Set getMainProfileMasterKeyIds() { - HashSet result = new HashSet<>(); - Cursor masterKeyIds = mContentResolver.query(ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI, - new String[]{ - ContactsContract.RawContacts.SOURCE_ID - }, - ContactsContract.RawContacts.ACCOUNT_TYPE + "=?", - new String[]{ - Constants.ACCOUNT_TYPE - }, null); - if (masterKeyIds != null) { - while (masterKeyIds.moveToNext()) { - result.add(masterKeyIds.getLong(0)); - } - masterKeyIds.close(); - } - return result; - } - - /** - * This will search the contact db for a raw contact with a given master key id - * - * @return raw contact id or -1 if not found - */ - private long findRawContactId(long masterKeyId) { - long rawContactId = -1; - Cursor raw = mContentResolver.query(ContactsContract.RawContacts.CONTENT_URI, - new String[]{ - ContactsContract.RawContacts._ID - }, - ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + ContactsContract.RawContacts.SOURCE_ID + "=?", - new String[]{ - Constants.ACCOUNT_TYPE, Long.toString(masterKeyId) - }, null); - if (raw != null) { - if (raw.moveToNext()) { - rawContactId = raw.getLong(0); - } - raw.close(); - } - return rawContactId; - } - - /** - * Creates a empty raw contact with a given masterKeyId - */ - private void insertContact(ArrayList ops, long masterKeyId) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) - .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, Constants.ACCOUNT_NAME) - .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, Constants.ACCOUNT_TYPE) - .withValue(ContactsContract.RawContacts.SOURCE_ID, Long.toString(masterKeyId)) - .build()); - } - - /** - * Adds a key id to the given raw contact. - *

- * This creates the link to OK in contact details - */ - private void writeContactKey(ArrayList ops, long rawContactId, - long masterKeyId, String keyName) { - ops.add(referenceRawContact(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI), rawContactId) - .withValue(ContactsContract.Data.MIMETYPE, Constants.CUSTOM_CONTACT_DATA_MIME_TYPE) - .withValue(ContactsContract.Data.DATA1, mContext.getString(R.string.contact_show_key, keyName)) - .withValue(ContactsContract.Data.DATA2, masterKeyId) - .build()); - } - - /** - * Write all known email addresses of a key (derived from user ids) to a given raw contact - */ - private void writeContactEmail(ArrayList ops, - long rawContactId, long masterKeyId) { - ContentProviderOperation deleteEmailOp = selectByRawContactAndItemType( - ContentProviderOperation.newDelete(Data.CONTENT_URI), rawContactId, Email.CONTENT_ITEM_TYPE).build(); - ops.add(deleteEmailOp); - - ContentProviderOperation deleteJabberOp = selectByRawContactAndItemType( - ContentProviderOperation.newDelete(Data.CONTENT_URI), rawContactId, Im.CONTENT_ITEM_TYPE).build(); - ops.add(deleteJabberOp); - - for (UserId userId : keyRepository.getUserIds(masterKeyId)) { - if (userId.email() != null) { - ContentProviderOperation.Builder builder = referenceRawContact( - ContentProviderOperation.newInsert(Data.CONTENT_URI), rawContactId); - - if (userId.email().startsWith("xmpp:")) { - builder = builder.withValue(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE) - .withValue(Im.PROTOCOL, Im.PROTOCOL_JABBER) - .withValue(Im.DATA, userId.email().substring(5)); - } else { - builder = builder.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE) - .withValue(Email.DATA, userId.email()); - } - ops.add(builder.build()); - } - } - } - - private void writeContactDisplayName(ArrayList ops, long rawContactId, - String displayName) { - if (displayName != null) { - ops.add(insertOrUpdateForRawContact(ContactsContract.Data.CONTENT_URI, rawContactId, - ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName) - .build()); - } - } - - private ContentProviderOperation.Builder referenceRawContact(ContentProviderOperation.Builder builder, - long rawContactId) { - return rawContactId == -1 ? - builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) : - builder.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); - } - - private ContentProviderOperation.Builder insertOrUpdateForRawContact(Uri uri, long rawContactId, - String itemType) { - if (rawContactId == -1) { - return referenceRawContact(ContentProviderOperation.newInsert(uri), rawContactId).withValue( - ContactsContract.Data.MIMETYPE, itemType); - } else { - return selectByRawContactAndItemType(ContentProviderOperation.newUpdate(uri), rawContactId, itemType); - } - } - - private ContentProviderOperation.Builder selectByRawContactAndItemType( - ContentProviderOperation.Builder builder, long rawContactId, String itemType) { - return builder.withSelection( - ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", - new String[]{ - Long.toString(rawContactId), itemType - }); - } -} diff --git a/OpenKeychain/src/main/res/layout/add_email_dialog.xml b/OpenKeychain/src/main/res/layout/add_email_dialog.xml index 68d895145..5937e9dba 100644 --- a/OpenKeychain/src/main/res/layout/add_email_dialog.xml +++ b/OpenKeychain/src/main/res/layout/add_email_dialog.xml @@ -14,13 +14,14 @@ android:text="@string/create_key_add_email_text" android:textAppearance="?android:textAppearanceMedium" /> - diff --git a/OpenKeychain/src/main/res/layout/add_user_id_dialog.xml b/OpenKeychain/src/main/res/layout/add_user_id_dialog.xml index 64d2934c5..5a130fd0d 100644 --- a/OpenKeychain/src/main/res/layout/add_user_id_dialog.xml +++ b/OpenKeychain/src/main/res/layout/add_user_id_dialog.xml @@ -8,15 +8,16 @@ android:paddingLeft="24dp" android:paddingRight="24dp"> - - - diff --git a/OpenKeychain/src/main/res/layout/create_key_name_fragment.xml b/OpenKeychain/src/main/res/layout/create_key_name_fragment.xml index 4e37dd713..b5b39725d 100644 --- a/OpenKeychain/src/main/res/layout/create_key_name_fragment.xml +++ b/OpenKeychain/src/main/res/layout/create_key_name_fragment.xml @@ -24,7 +24,7 @@ android:textAppearance="?android:attr/textAppearanceMedium" android:text="@string/create_key_name_text" /> - - - - - - - - - - - -