Show notification when READ_CONTACTS permission is denied in sync service, hide linked contact card if permission is denied
This commit is contained in:
@@ -56,14 +56,20 @@
|
|||||||
<!-- CAMERA permission requested by ZXing library -->
|
<!-- CAMERA permission requested by ZXing library -->
|
||||||
|
|
||||||
<!-- contact group -->
|
<!-- contact group -->
|
||||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
<!--
|
||||||
|
AUTHENTICATE_ACCOUNTS and MANAGE_ACCOUNTS removed in Android >= 6,
|
||||||
|
see https://code.google.com/p/android-developer-preview/issues/detail?id=2592
|
||||||
|
also READ_PROFILE, WRITE_PROFILE?
|
||||||
|
-->
|
||||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
|
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
|
||||||
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
|
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
|
||||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
|
||||||
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
|
||||||
<uses-permission android:name="android.permission.READ_PROFILE" />
|
<uses-permission android:name="android.permission.READ_PROFILE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_PROFILE" />
|
<uses-permission android:name="android.permission.WRITE_PROFILE" />
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||||
|
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||||
|
|
||||||
<!-- storage group -->
|
<!-- storage group -->
|
||||||
<!--
|
<!--
|
||||||
No need on >= Android 4.4 for WRITE_EXTERNAL_STORAGE, because we use Storage Access Framework,
|
No need on >= Android 4.4 for WRITE_EXTERNAL_STORAGE, because we use Storage Access Framework,
|
||||||
@@ -840,10 +846,10 @@
|
|||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.content.SyncAdapter"
|
android:name="android.content.SyncAdapter"
|
||||||
android:resource="@xml/sync_adapter_desc" />
|
android:resource="@xml/sync_adapter_contacts" />
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.provider.CONTACTS_STRUCTURE"
|
android:name="android.provider.CONTACTS_STRUCTURE"
|
||||||
android:resource="@xml/custom_pgp_contacts_structure" />
|
android:resource="@xml/sync_adapter_contacts_structure" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
@@ -857,7 +863,7 @@
|
|||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.content.SyncAdapter"
|
android:name="android.content.SyncAdapter"
|
||||||
android:resource="@xml/keyserver_sync_adapter_desc" />
|
android:resource="@xml/sync_adapter_keys" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<!-- Storage Provider for temporary decrypted files -->
|
<!-- Storage Provider for temporary decrypted files -->
|
||||||
|
|||||||
@@ -18,14 +18,12 @@
|
|||||||
package org.sufficientlysecure.keychain.operations;
|
package org.sufficientlysecure.keychain.operations;
|
||||||
|
|
||||||
|
|
||||||
import java.net.Proxy;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
|
|
||||||
import org.sufficientlysecure.keychain.operations.results.CertifyResult;
|
import org.sufficientlysecure.keychain.operations.results.CertifyResult;
|
||||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
||||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
||||||
@@ -51,8 +49,6 @@ import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
|||||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSignOperationsBuilder;
|
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSignOperationsBuilder;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||||
import org.sufficientlysecure.keychain.util.Preferences;
|
|
||||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An operation which implements a high level user id certification operation.
|
* An operation which implements a high level user id certification operation.
|
||||||
@@ -256,7 +252,7 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// since only verified keys are synced to contacts, we need to initiate a sync now
|
// since only verified keys are synced to contacts, we need to initiate a sync now
|
||||||
ContactSyncAdapterService.requestSync();
|
ContactSyncAdapterService.requestContactsSync();
|
||||||
|
|
||||||
log.add(LogType.MSG_CRT_SUCCESS, 0);
|
log.add(LogType.MSG_CRT_SUCCESS, 0);
|
||||||
if (uploadError != 0) {
|
if (uploadError != 0) {
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ public class DeleteOperation extends BaseOperation<DeleteKeyringParcel> {
|
|||||||
int result = DeleteResult.RESULT_OK;
|
int result = DeleteResult.RESULT_OK;
|
||||||
if (success > 0) {
|
if (success > 0) {
|
||||||
// make sure new data is synced into contacts
|
// make sure new data is synced into contacts
|
||||||
ContactSyncAdapterService.requestSync();
|
ContactSyncAdapterService.requestContactsSync();
|
||||||
|
|
||||||
log.add(LogType.MSG_DEL_OK, 0, success);
|
log.add(LogType.MSG_DEL_OK, 0, success);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ public class EditKeyOperation extends BaseOperation<SaveKeyringParcel> {
|
|||||||
updateProgress(R.string.progress_done, 100, 100);
|
updateProgress(R.string.progress_done, 100, 100);
|
||||||
|
|
||||||
// make sure new data is synced into contacts
|
// make sure new data is synced into contacts
|
||||||
ContactSyncAdapterService.requestSync();
|
ContactSyncAdapterService.requestContactsSync();
|
||||||
|
|
||||||
log.add(LogType.MSG_ED_SUCCESS, 0);
|
log.add(LogType.MSG_ED_SUCCESS, 0);
|
||||||
return new EditKeyResult(EditKeyResult.RESULT_OK, log, ring.getMasterKeyId());
|
return new EditKeyResult(EditKeyResult.RESULT_OK, log, ring.getMasterKeyId());
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Since the introduction of multithreaded import, we expect calling functions to handle the
|
* Since the introduction of multithreaded import, we expect calling functions to handle the
|
||||||
* contact-to-key sync i.e ContactSyncAdapterService.requestSync()
|
* contact-to-key sync i.e ContactSyncAdapterService.requestContactsSync()
|
||||||
*
|
*
|
||||||
* @param entries keys to import
|
* @param entries keys to import
|
||||||
* @param num number of keys to import
|
* @param num number of keys to import
|
||||||
@@ -325,7 +325,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
// Special: make sure new data is synced into contacts
|
// Special: make sure new data is synced into contacts
|
||||||
// disabling sync right now since it reduces speed while multi-threading
|
// disabling sync right now since it reduces speed while multi-threading
|
||||||
// so, we expect calling functions to take care of it. KeychainService handles this
|
// so, we expect calling functions to take care of it. KeychainService handles this
|
||||||
// ContactSyncAdapterService.requestSync();
|
// ContactSyncAdapterService.requestContactsSync();
|
||||||
|
|
||||||
// convert to long array
|
// convert to long array
|
||||||
long[] importedMasterKeyIdsArray = new long[importedMasterKeyIds.size()];
|
long[] importedMasterKeyIdsArray = new long[importedMasterKeyIds.size()];
|
||||||
@@ -405,7 +405,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
result = multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer, proxy);
|
result = multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer, proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
ContactSyncAdapterService.requestSync();
|
ContactSyncAdapterService.requestContactsSync();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,6 +18,9 @@
|
|||||||
package org.sufficientlysecure.keychain.service;
|
package org.sufficientlysecure.keychain.service;
|
||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.AbstractThreadedSyncAdapter;
|
import android.content.AbstractThreadedSyncAdapter;
|
||||||
import android.content.ContentProviderClient;
|
import android.content.ContentProviderClient;
|
||||||
@@ -26,14 +29,20 @@ import android.content.Intent;
|
|||||||
import android.content.SyncResult;
|
import android.content.SyncResult;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.preference.PreferenceActivity;
|
||||||
import android.provider.ContactsContract;
|
import android.provider.ContactsContract;
|
||||||
|
import android.support.v4.app.NotificationCompat;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.ui.SettingsActivity;
|
||||||
import org.sufficientlysecure.keychain.util.ContactHelper;
|
import org.sufficientlysecure.keychain.util.ContactHelper;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
public class ContactSyncAdapterService extends Service {
|
public class ContactSyncAdapterService extends Service {
|
||||||
|
|
||||||
|
private static final int NOTIFICATION_ID_SYNC_SETTINGS = 13;
|
||||||
|
|
||||||
private class ContactSyncAdapter extends AbstractThreadedSyncAdapter {
|
private class ContactSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
|
|
||||||
// private final AtomicBoolean importDone = new AtomicBoolean(false);
|
// private final AtomicBoolean importDone = new AtomicBoolean(false);
|
||||||
@@ -46,7 +55,44 @@ public class ContactSyncAdapterService extends Service {
|
|||||||
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider,
|
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider,
|
||||||
final SyncResult syncResult) {
|
final SyncResult syncResult) {
|
||||||
Log.d(Constants.TAG, "Performing a contact sync!");
|
Log.d(Constants.TAG, "Performing a contact sync!");
|
||||||
// TODO: Import is currently disabled for 2.8, until we implement proper origin management
|
|
||||||
|
ContactHelper.writeKeysToContacts(ContactSyncAdapterService.this);
|
||||||
|
|
||||||
|
importKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSecurityException(Account account, Bundle extras, String authority, SyncResult syncResult) {
|
||||||
|
super.onSecurityException(account, extras, authority, syncResult);
|
||||||
|
|
||||||
|
// deactivate sync
|
||||||
|
ContentResolver.setSyncAutomatically(account, authority, false);
|
||||||
|
|
||||||
|
// show notification linking to sync settings
|
||||||
|
Intent resultIntent = new Intent(ContactSyncAdapterService.this, SettingsActivity.class);
|
||||||
|
resultIntent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT,
|
||||||
|
SettingsActivity.SyncPrefsFragment.class.getName());
|
||||||
|
PendingIntent resultPendingIntent =
|
||||||
|
PendingIntent.getActivity(
|
||||||
|
ContactSyncAdapterService.this,
|
||||||
|
0,
|
||||||
|
resultIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
);
|
||||||
|
NotificationCompat.Builder mBuilder =
|
||||||
|
new NotificationCompat.Builder(ContactSyncAdapterService.this)
|
||||||
|
.setSmallIcon(R.drawable.ic_stat_notify_24dp)
|
||||||
|
.setContentTitle(getString(R.string.sync_notification_permission_required_title))
|
||||||
|
.setContentText(getString(R.string.sync_notification_permission_required_text))
|
||||||
|
.setContentIntent(resultPendingIntent);
|
||||||
|
NotificationManager mNotifyMgr =
|
||||||
|
(NotificationManager) ContactSyncAdapterService.this.getSystemService(Activity.NOTIFICATION_SERVICE);
|
||||||
|
mNotifyMgr.notify(NOTIFICATION_ID_SYNC_SETTINGS, mBuilder.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void importKeys() {
|
||||||
|
// TODO: Import is currently disabled, until we implement proper origin management
|
||||||
// importDone.set(false);
|
// importDone.set(false);
|
||||||
// KeychainApplication.setupAccountAsNeeded(ContactSyncAdapterService.this);
|
// KeychainApplication.setupAccountAsNeeded(ContactSyncAdapterService.this);
|
||||||
// EmailKeyHelper.importContacts(getContext(), new Messenger(new Handler(Looper.getMainLooper(),
|
// EmailKeyHelper.importContacts(getContext(), new Messenger(new Handler(Looper.getMainLooper(),
|
||||||
@@ -84,14 +130,13 @@ public class ContactSyncAdapterService extends Service {
|
|||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
ContactHelper.writeKeysToContacts(ContactSyncAdapterService.this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void requestSync() {
|
public static void requestContactsSync() {
|
||||||
Bundle extras = new Bundle();
|
Bundle extras = new Bundle();
|
||||||
// no need to wait for internet connection!
|
// no need to wait, do it immediately
|
||||||
extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
|
extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
|
||||||
|
extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
|
||||||
ContentResolver.requestSync(
|
ContentResolver.requestSync(
|
||||||
new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE),
|
new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE),
|
||||||
ContactsContract.AUTHORITY,
|
ContactsContract.AUTHORITY,
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Load the preferences from an XML resource
|
// Load the preferences from an XML resource
|
||||||
addPreferencesFromResource(R.xml.cloud_search_prefs);
|
addPreferencesFromResource(R.xml.cloud_search_preferences);
|
||||||
|
|
||||||
mKeyServerPreference = (PreferenceScreen) findPreference(Constants.Pref.KEY_SERVERS);
|
mKeyServerPreference = (PreferenceScreen) findPreference(Constants.Pref.KEY_SERVERS);
|
||||||
mKeyServerPreference.setSummary(keyserverSummary(getActivity()));
|
mKeyServerPreference.setSummary(keyserverSummary(getActivity()));
|
||||||
@@ -238,11 +238,11 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||||||
if (mFragment != null) {
|
if (mFragment != null) {
|
||||||
Preferences.setPreferenceManagerFileAndMode(mFragment.getPreferenceManager());
|
Preferences.setPreferenceManagerFileAndMode(mFragment.getPreferenceManager());
|
||||||
// Load the preferences from an XML resource
|
// Load the preferences from an XML resource
|
||||||
mFragment.addPreferencesFromResource(R.xml.proxy_prefs);
|
mFragment.addPreferencesFromResource(R.xml.proxy_preferences);
|
||||||
} else {
|
} else {
|
||||||
Preferences.setPreferenceManagerFileAndMode(mActivity.getPreferenceManager());
|
Preferences.setPreferenceManagerFileAndMode(mActivity.getPreferenceManager());
|
||||||
// Load the preferences from an XML resource
|
// Load the preferences from an XML resource
|
||||||
mActivity.addPreferencesFromResource(R.xml.proxy_prefs);
|
mActivity.addPreferencesFromResource(R.xml.proxy_preferences);
|
||||||
}
|
}
|
||||||
|
|
||||||
mUseTor = (SwitchPreference) automaticallyFindPreference(Constants.Pref.USE_TOR_PROXY);
|
mUseTor = (SwitchPreference) automaticallyFindPreference(Constants.Pref.USE_TOR_PROXY);
|
||||||
@@ -509,9 +509,9 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||||||
// permission granted -> enable contact linking
|
// permission granted -> enable contact linking
|
||||||
AccountManager manager = AccountManager.get(getActivity());
|
AccountManager manager = AccountManager.get(getActivity());
|
||||||
final Account account = manager.getAccountsByType(Constants.ACCOUNT_TYPE)[0];
|
final Account account = manager.getAccountsByType(Constants.ACCOUNT_TYPE)[0];
|
||||||
SwitchPreference pref = (SwitchPreference) findPreference(Constants.Pref.SYNC_KEYSERVER);
|
SwitchPreference pref = (SwitchPreference) findPreference(Constants.Pref.SYNC_CONTACTS);
|
||||||
ContentResolver.setSyncAutomatically(account, Constants.PROVIDER_AUTHORITY, true);
|
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
|
||||||
setSummary(pref, Constants.PROVIDER_AUTHORITY, true);
|
setSummary(pref, ContactsContract.AUTHORITY, true);
|
||||||
pref.setChecked(true);
|
pref.setChecked(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -855,8 +855,8 @@ public class ViewKeyActivity extends BaseNfcActivity implements
|
|||||||
AsyncTask<Long, Void, Bitmap> photoTask =
|
AsyncTask<Long, Void, Bitmap> photoTask =
|
||||||
new AsyncTask<Long, Void, Bitmap>() {
|
new AsyncTask<Long, Void, Bitmap>() {
|
||||||
protected Bitmap doInBackground(Long... mMasterKeyId) {
|
protected Bitmap doInBackground(Long... mMasterKeyId) {
|
||||||
return ContactHelper.loadPhotoByMasterKeyId(getContentResolver(),
|
return ContactHelper.loadPhotoByMasterKeyId(ViewKeyActivity.this,
|
||||||
mMasterKeyId[0], true);
|
getContentResolver(), mMasterKeyId[0], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onPostExecute(Bitmap photo) {
|
protected void onPostExecute(Bitmap photo) {
|
||||||
|
|||||||
@@ -22,10 +22,12 @@ package org.sufficientlysecure.keychain.ui;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@@ -35,6 +37,7 @@ import android.os.Bundle;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.provider.ContactsContract;
|
import android.provider.ContactsContract;
|
||||||
import android.support.v4.app.LoaderManager;
|
import android.support.v4.app.LoaderManager;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v4.content.CursorLoader;
|
import android.support.v4.content.CursorLoader;
|
||||||
import android.support.v4.content.Loader;
|
import android.support.v4.content.Loader;
|
||||||
import android.support.v7.widget.CardView;
|
import android.support.v7.widget.CardView;
|
||||||
@@ -241,9 +244,9 @@ public class ViewKeyFragment extends LoaderFragment implements
|
|||||||
|
|
||||||
Bitmap picture;
|
Bitmap picture;
|
||||||
if (mIsSecret) {
|
if (mIsSecret) {
|
||||||
picture = ContactHelper.loadMainProfilePhoto(resolver, false);
|
picture = ContactHelper.loadMainProfilePhoto(getActivity(), resolver, false);
|
||||||
} else {
|
} else {
|
||||||
picture = ContactHelper.loadPhotoByContactId(resolver, contactId, false);
|
picture = ContactHelper.loadPhotoByContactId(getActivity(), resolver, contactId, false);
|
||||||
}
|
}
|
||||||
if (picture != null) mSystemContactPicture.setImageBitmap(picture);
|
if (picture != null) mSystemContactPicture.setImageBitmap(picture);
|
||||||
|
|
||||||
@@ -419,13 +422,7 @@ public class ViewKeyFragment extends LoaderFragment implements
|
|||||||
getLoaderManager().initLoader(LOADER_ID_LINKED_IDS, null, this);
|
getLoaderManager().initLoader(LOADER_ID_LINKED_IDS, null, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initLinkedContactLoader(masterKeyId, mIsSecret);
|
||||||
Bundle linkedContactData = new Bundle();
|
|
||||||
linkedContactData.putLong(LOADER_EXTRA_LINKED_CONTACT_MASTER_KEY_ID, masterKeyId);
|
|
||||||
linkedContactData.putBoolean(LOADER_EXTRA_LINKED_CONTACT_IS_SECRET, mIsSecret);
|
|
||||||
|
|
||||||
// initialises loader for contact query so we can listen to any updates
|
|
||||||
getLoaderManager().initLoader(LOADER_ID_LINKED_CONTACT, linkedContactData, this);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -465,6 +462,22 @@ public class ViewKeyFragment extends LoaderFragment implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initLinkedContactLoader(long masterKeyId, boolean isSecret) {
|
||||||
|
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();
|
||||||
|
linkedContactData.putLong(LOADER_EXTRA_LINKED_CONTACT_MASTER_KEY_ID, masterKeyId);
|
||||||
|
linkedContactData.putBoolean(LOADER_EXTRA_LINKED_CONTACT_IS_SECRET, isSecret);
|
||||||
|
|
||||||
|
// initialises loader for contact query so we can listen to any updates
|
||||||
|
getLoaderManager().initLoader(LOADER_ID_LINKED_CONTACT, linkedContactData, this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is called when the last Cursor provided to onLoadFinished() above is about to be closed.
|
* This is called when the last Cursor provided to onLoadFinished() above is about to be closed.
|
||||||
* We need to make sure we are no longer using it.
|
* We need to make sure we are no longer using it.
|
||||||
|
|||||||
@@ -17,17 +17,21 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.util;
|
package org.sufficientlysecure.keychain.util;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
import android.accounts.AccountManager;
|
import android.accounts.AccountManager;
|
||||||
import android.content.ContentProviderOperation;
|
import android.content.ContentProviderOperation;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentUris;
|
import android.content.ContentUris;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.provider.ContactsContract;
|
import android.provider.ContactsContract;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.util.Patterns;
|
import android.util.Patterns;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
@@ -51,6 +55,11 @@ public class ContactHelper {
|
|||||||
private static final Map<Long, Bitmap> photoCache = new HashMap<>();
|
private static final Map<Long, Bitmap> photoCache = new HashMap<>();
|
||||||
|
|
||||||
public static List<String> getPossibleUserEmails(Context context) {
|
public static List<String> getPossibleUserEmails(Context context) {
|
||||||
|
if (!isContactsPermissionGranted(context)) {
|
||||||
|
Log.w(Constants.TAG, "getting emails not possible READ_CONTACTS permission denied!");
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
Set<String> accountMails = getAccountEmails(context);
|
Set<String> accountMails = getAccountEmails(context);
|
||||||
accountMails.addAll(getMainProfileContactEmails(context));
|
accountMails.addAll(getMainProfileContactEmails(context));
|
||||||
|
|
||||||
@@ -69,6 +78,11 @@ public class ContactHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> getPossibleUserNames(Context context) {
|
public static List<String> getPossibleUserNames(Context context) {
|
||||||
|
if (!isContactsPermissionGranted(context)) {
|
||||||
|
Log.w(Constants.TAG, "getting names not possible READ_CONTACTS permission denied!");
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
Set<String> accountMails = getAccountEmails(context);
|
Set<String> accountMails = getAccountEmails(context);
|
||||||
Set<String> names = getContactNamesFromEmails(context, accountMails);
|
Set<String> names = getContactNamesFromEmails(context, accountMails);
|
||||||
names.addAll(getMainProfileContactName(context));
|
names.addAll(getMainProfileContactName(context));
|
||||||
@@ -242,7 +256,7 @@ public class ContactHelper {
|
|||||||
* @param highRes true for large image if present, false for thumbnail
|
* @param highRes true for large image if present, false for thumbnail
|
||||||
* @return bitmap of loaded photo
|
* @return bitmap of loaded photo
|
||||||
*/
|
*/
|
||||||
public static Bitmap loadMainProfilePhoto(ContentResolver contentResolver, boolean highRes) {
|
public static Bitmap loadMainProfilePhoto(Context context, ContentResolver contentResolver, boolean highRes) {
|
||||||
try {
|
try {
|
||||||
long mainProfileContactId = getMainProfileContactId(contentResolver);
|
long mainProfileContactId = getMainProfileContactId(contentResolver);
|
||||||
|
|
||||||
@@ -300,6 +314,7 @@ public class ContactHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Uri dataUriFromContactUri(Context context, Uri contactUri) {
|
public static Uri dataUriFromContactUri(Context context, Uri contactUri) {
|
||||||
|
|
||||||
Cursor contactMasterKey = context.getContentResolver().query(contactUri,
|
Cursor contactMasterKey = context.getContentResolver().query(contactUri,
|
||||||
new String[]{ContactsContract.Data.DATA2}, null, null, null);
|
new String[]{ContactsContract.Data.DATA2}, null, null, null);
|
||||||
if (contactMasterKey != null) {
|
if (contactMasterKey != null) {
|
||||||
@@ -373,32 +388,43 @@ public class ContactHelper {
|
|||||||
return contactName;
|
return contactName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Bitmap getCachedPhotoByMasterKeyId(ContentResolver contentResolver, long masterKeyId) {
|
public static Bitmap getCachedPhotoByMasterKeyId(Context context, ContentResolver contentResolver,
|
||||||
|
long masterKeyId) {
|
||||||
if (masterKeyId == -1) {
|
if (masterKeyId == -1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!photoCache.containsKey(masterKeyId)) {
|
if (!photoCache.containsKey(masterKeyId)) {
|
||||||
photoCache.put(masterKeyId, loadPhotoByMasterKeyId(contentResolver, masterKeyId, false));
|
photoCache.put(masterKeyId, loadPhotoByMasterKeyId(context, contentResolver, masterKeyId, false));
|
||||||
}
|
}
|
||||||
return photoCache.get(masterKeyId);
|
return photoCache.get(masterKeyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Bitmap loadPhotoByMasterKeyId(ContentResolver contentResolver, long masterKeyId,
|
public static Bitmap loadPhotoByMasterKeyId(Context context, ContentResolver contentResolver,
|
||||||
boolean highRes) {
|
long masterKeyId, boolean highRes) {
|
||||||
|
if (!isContactsPermissionGranted(context)) {
|
||||||
|
Log.w(Constants.TAG, "loading photo not possible READ_CONTACTS permission denied!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (masterKeyId == -1) {
|
if (masterKeyId == -1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
long contactId = findContactId(contentResolver, masterKeyId);
|
long contactId = findContactId(contentResolver, masterKeyId);
|
||||||
return loadPhotoByContactId(contentResolver, contactId, highRes);
|
return loadPhotoByContactId(context, contentResolver, contactId, highRes);
|
||||||
|
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Bitmap loadPhotoByContactId(ContentResolver contentResolver, long contactId,
|
public static Bitmap loadPhotoByContactId(Context context, ContentResolver contentResolver,
|
||||||
boolean highRes) {
|
long contactId, boolean highRes) {
|
||||||
|
if (!isContactsPermissionGranted(context)) {
|
||||||
|
Log.w(Constants.TAG, "loading photo not possible READ_CONTACTS permission denied!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (contactId == -1) {
|
if (contactId == -1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -452,6 +478,19 @@ public class ContactHelper {
|
|||||||
writeKeysToNormalContacts(context, resolver);
|
writeKeysToNormalContacts(context, resolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isContactsPermissionGranted(Context context) {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS)
|
||||||
|
== PackageManager.PERMISSION_GRANTED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static void writeKeysToNormalContacts(Context context, ContentResolver resolver) {
|
private static void writeKeysToNormalContacts(Context context, ContentResolver resolver) {
|
||||||
// delete raw contacts flagged for deletion by user so they can be reinserted
|
// delete raw contacts flagged for deletion by user so they can be reinserted
|
||||||
deleteFlaggedNormalRawContacts(resolver);
|
deleteFlaggedNormalRawContacts(resolver);
|
||||||
|
|||||||
@@ -1505,6 +1505,8 @@
|
|||||||
<string name="account_no_manual_account_creation">"You can not create OpenKeychain accounts manually."</string>
|
<string name="account_no_manual_account_creation">"You can not create OpenKeychain accounts manually."</string>
|
||||||
<string name="account_privacy_title">"Privacy"</string>
|
<string name="account_privacy_title">"Privacy"</string>
|
||||||
<string name="account_privacy_text">"OpenKeychain does not synchronize your contacts with the Internet. It only links contacts to keys based on names and email addresses. It does this offline on your device."</string>
|
<string name="account_privacy_text">"OpenKeychain does not synchronize your contacts with the Internet. It only links contacts to keys based on names and email addresses. It does this offline on your device."</string>
|
||||||
|
<string name="sync_notification_permission_required_title">"Linking keys needs contacts access!"</string>
|
||||||
|
<string name="sync_notification_permission_required_text">"Touch to configure linking to contacts"</string>
|
||||||
|
|
||||||
<!-- Passphrase wizard -->
|
<!-- Passphrase wizard -->
|
||||||
<!-- TODO: rename all the things! -->
|
<!-- TODO: rename all the things! -->
|
||||||
|
|||||||
Reference in New Issue
Block a user