mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-27 11:12:15 -05:00
Show keys with android contacts
This means to sync userid + keyid into contact storage. Android will merge them to normal contacts based on primary userid.
This commit is contained in:
parent
dc1e26f39c
commit
80e9998640
@ -58,6 +58,7 @@
|
|||||||
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
|
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
|
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
|
||||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||||
|
|
||||||
<!-- android:allowBackup="false": Don't allow backup over adb backup or other apps! -->
|
<!-- android:allowBackup="false": Don't allow backup over adb backup or other apps! -->
|
||||||
<application
|
<application
|
||||||
@ -91,6 +92,11 @@
|
|||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".ui.KeyListActivity" />
|
android:value=".ui.KeyListActivity" />
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.key" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.ViewCertActivity"
|
android:name=".ui.ViewCertActivity"
|
||||||
|
@ -46,6 +46,8 @@ public final class Constants {
|
|||||||
|
|
||||||
public static final String INTENT_PREFIX = PACKAGE_NAME + ".action.";
|
public static final String INTENT_PREFIX = PACKAGE_NAME + ".action.";
|
||||||
|
|
||||||
|
public static final String CUSTOM_CONTACT_DATA_MIME_TYPE = "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.key";
|
||||||
|
|
||||||
public static final class Path {
|
public static final class Path {
|
||||||
public static final String APP_DIR = Environment.getExternalStorageDirectory()
|
public static final String APP_DIR = Environment.getExternalStorageDirectory()
|
||||||
+ "/OpenKeychain";
|
+ "/OpenKeychain";
|
||||||
|
@ -26,6 +26,7 @@ import android.graphics.drawable.Drawable;
|
|||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
|
|
||||||
import org.spongycastle.jce.provider.BouncyCastleProvider;
|
import org.spongycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.sufficientlysecure.keychain.helper.ContactHelper;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.util.PRNGFixes;
|
import org.sufficientlysecure.keychain.util.PRNGFixes;
|
||||||
|
|
||||||
@ -79,14 +80,14 @@ public class KeychainApplication extends Application {
|
|||||||
brandGlowEffect(getApplicationContext(),
|
brandGlowEffect(getApplicationContext(),
|
||||||
getApplicationContext().getResources().getColor(R.color.emphasis));
|
getApplicationContext().getResources().getColor(R.color.emphasis));
|
||||||
|
|
||||||
setupAccountAsNeeded();
|
setupAccountAsNeeded(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupAccountAsNeeded() {
|
public static void setupAccountAsNeeded(Context context) {
|
||||||
AccountManager manager = AccountManager.get(this);
|
AccountManager manager = AccountManager.get(context);
|
||||||
Account[] accounts = manager.getAccountsByType(getPackageName());
|
Account[] accounts = manager.getAccountsByType(Constants.PACKAGE_NAME);
|
||||||
if (accounts == null || accounts.length == 0) {
|
if (accounts == null || accounts.length == 0) {
|
||||||
Account dummy = new Account(getString(R.string.app_name), getPackageName());
|
Account dummy = new Account(context.getString(R.string.app_name), Constants.PACKAGE_NAME);
|
||||||
manager.addAccountExplicitly(dummy, null, null);
|
manager.addAccountExplicitly(dummy, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,11 +19,17 @@ package org.sufficientlysecure.keychain.helper;
|
|||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
import android.accounts.AccountManager;
|
import android.accounts.AccountManager;
|
||||||
import android.content.ContentResolver;
|
import android.content.*;
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.RemoteException;
|
||||||
import android.provider.ContactsContract;
|
import android.provider.ContactsContract;
|
||||||
import android.util.Patterns;
|
import android.util.Patterns;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -32,6 +38,15 @@ import java.util.Set;
|
|||||||
|
|
||||||
public class ContactHelper {
|
public class ContactHelper {
|
||||||
|
|
||||||
|
public static final String[] KEYS_TO_CONTACT_PROJECTION = new String[]{
|
||||||
|
KeychainContract.KeyRings.USER_ID,
|
||||||
|
KeychainContract.KeyRings.FINGERPRINT,
|
||||||
|
KeychainContract.KeyRings.KEY_ID,
|
||||||
|
KeychainContract.KeyRings.MASTER_KEY_ID};
|
||||||
|
public static final String[] RAW_CONTACT_ID_PROJECTION = new String[]{ContactsContract.RawContacts._ID};
|
||||||
|
public static final String FIND_RAW_CONTACT_SELECTION =
|
||||||
|
ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + ContactsContract.RawContacts.SOURCE_ID + "=?";
|
||||||
|
|
||||||
public static final List<String> getMailAccounts(Context context) {
|
public static final List<String> getMailAccounts(Context context) {
|
||||||
final Account[] accounts = AccountManager.get(context).getAccounts();
|
final Account[] accounts = AccountManager.get(context).getAccounts();
|
||||||
final Set<String> emailSet = new HashSet<String>();
|
final Set<String> emailSet = new HashSet<String>();
|
||||||
@ -60,4 +75,74 @@ public class ContactHelper {
|
|||||||
mailCursor.close();
|
mailCursor.close();
|
||||||
return new ArrayList<String>(mails);
|
return new ArrayList<String>(mails);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Uri dataUriFromContactUri(Context context, Uri contactUri) {
|
||||||
|
Cursor contactMasterKey = context.getContentResolver().query(contactUri, new String[]{ContactsContract.Data.DATA2}, null, null, null, null);
|
||||||
|
if (contactMasterKey != null) {
|
||||||
|
if (contactMasterKey.moveToNext()) {
|
||||||
|
return KeychainContract.KeyRings.buildGenericKeyRingUri(contactMasterKey.getLong(0));
|
||||||
|
}
|
||||||
|
contactMasterKey.close();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeKeysToContacts(Context context) {
|
||||||
|
ContentResolver resolver = context.getContentResolver();
|
||||||
|
Cursor cursor = resolver.query(KeychainContract.KeyRings.buildUnifiedKeyRingsUri(), KEYS_TO_CONTACT_PROJECTION,
|
||||||
|
null, null, null);
|
||||||
|
if (cursor != null) {
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
String[] userId = PgpKeyHelper.splitUserId(cursor.getString(0));
|
||||||
|
String fingerprint = PgpKeyHelper.convertFingerprintToHex(cursor.getBlob(1));
|
||||||
|
String keyIdShort = PgpKeyHelper.convertKeyIdToHexShort(cursor.getLong(2));
|
||||||
|
long masterKeyId = cursor.getLong(3);
|
||||||
|
int rawContactId = -1;
|
||||||
|
Cursor raw = resolver.query(ContactsContract.RawContacts.CONTENT_URI, RAW_CONTACT_ID_PROJECTION,
|
||||||
|
FIND_RAW_CONTACT_SELECTION, new String[]{Constants.PACKAGE_NAME, fingerprint}, null, null);
|
||||||
|
if (raw != null) {
|
||||||
|
if (raw.moveToNext()) {
|
||||||
|
rawContactId = raw.getInt(0);
|
||||||
|
}
|
||||||
|
raw.close();
|
||||||
|
}
|
||||||
|
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
|
||||||
|
if (rawContactId == -1) {
|
||||||
|
ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
|
||||||
|
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, context.getString(R.string.app_name))
|
||||||
|
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, Constants.PACKAGE_NAME)
|
||||||
|
.withValue(ContactsContract.RawContacts.SOURCE_ID, fingerprint)
|
||||||
|
.build());
|
||||||
|
if (userId[0] != null) {
|
||||||
|
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
||||||
|
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
||||||
|
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
|
||||||
|
.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, userId[0])
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
if (userId[1] != null) {
|
||||||
|
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
||||||
|
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
||||||
|
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
|
||||||
|
.withValue(ContactsContract.CommonDataKinds.Email.DATA, userId[1])
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
||||||
|
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
||||||
|
.withValue(ContactsContract.Data.MIMETYPE, Constants.CUSTOM_CONTACT_DATA_MIME_TYPE)
|
||||||
|
.withValue(ContactsContract.Data.DATA1, String.format(context.getString(R.string.contact_show_key), keyIdShort))
|
||||||
|
.withValue(ContactsContract.Data.DATA2, masterKeyId)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
resolver.applyBatch(ContactsContract.AUTHORITY, ops);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (OperationApplicationException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@ import android.content.ContentProviderClient;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SyncResult;
|
import android.content.SyncResult;
|
||||||
import android.os.*;
|
import android.os.*;
|
||||||
|
import org.sufficientlysecure.keychain.KeychainApplication;
|
||||||
|
import org.sufficientlysecure.keychain.helper.ContactHelper;
|
||||||
import org.sufficientlysecure.keychain.helper.EmailKeyHelper;
|
import org.sufficientlysecure.keychain.helper.EmailKeyHelper;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
@ -60,6 +62,8 @@ public class ContactSyncAdapterService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})));
|
})));
|
||||||
|
KeychainApplication.setupAccountAsNeeded(ContactSyncAdapterService.this);
|
||||||
|
ContactHelper.writeKeysToContacts(ContactSyncAdapterService.this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui;
|
|||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@ -32,6 +33,7 @@ import android.os.Build;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.provider.ContactsContract;
|
||||||
import android.support.v4.app.LoaderManager;
|
import android.support.v4.app.LoaderManager;
|
||||||
import android.support.v4.content.CursorLoader;
|
import android.support.v4.content.CursorLoader;
|
||||||
import android.support.v4.content.Loader;
|
import android.support.v4.content.Loader;
|
||||||
@ -47,6 +49,7 @@ import com.devspark.appmsg.AppMsg;
|
|||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.helper.ContactHelper;
|
||||||
import org.sufficientlysecure.keychain.helper.ExportHelper;
|
import org.sufficientlysecure.keychain.helper.ExportHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
@ -125,7 +128,7 @@ public class ViewKeyActivity extends ActionBarActivity implements
|
|||||||
switchToTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB);
|
switchToTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB);
|
||||||
}
|
}
|
||||||
|
|
||||||
Uri dataUri = getIntent().getData();
|
Uri dataUri = getDataUri();
|
||||||
if (dataUri == null) {
|
if (dataUri == null) {
|
||||||
Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
|
Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
|
||||||
finish();
|
finish();
|
||||||
@ -163,6 +166,14 @@ public class ViewKeyActivity extends ActionBarActivity implements
|
|||||||
mViewPager.setCurrentItem(switchToTab);
|
mViewPager.setCurrentItem(switchToTab);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Uri getDataUri() {
|
||||||
|
Uri dataUri = getIntent().getData();
|
||||||
|
if (dataUri != null && dataUri.getHost().equals(ContactsContract.AUTHORITY)) {
|
||||||
|
dataUri = ContactHelper.dataUriFromContactUri(this, dataUri);
|
||||||
|
}
|
||||||
|
return dataUri;
|
||||||
|
}
|
||||||
|
|
||||||
private void loadData(Uri dataUri) {
|
private void loadData(Uri dataUri) {
|
||||||
mDataUri = dataUri;
|
mDataUri = dataUri;
|
||||||
|
|
||||||
|
@ -521,5 +521,6 @@
|
|||||||
<string name="error_no_encrypt_subkey">No encryption subkey available!</string>
|
<string name="error_no_encrypt_subkey">No encryption subkey available!</string>
|
||||||
<string name="info_no_manual_account_creation">Do not create OpenKeychain-Accounts manually.
|
<string name="info_no_manual_account_creation">Do not create OpenKeychain-Accounts manually.
|
||||||
For more information, see Help.</string>
|
For more information, see Help.</string>
|
||||||
|
<string name="contact_show_key">Show key (%s)</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<ContactsSource xmlns:android="http://schemas.android.com/apk/res/android">
|
<ContactsSource xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<ContactsDataKind android:mimeType="vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.key"
|
<ContactsDataKind android:mimeType="vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.key"
|
||||||
android:icon="@drawable/key_small"
|
android:detailColumn="data1"/>
|
||||||
android:summaryColumn="data1"
|
|
||||||
android:detailColumn="data2"/>
|
|
||||||
</ContactsSource>
|
</ContactsSource>
|
Loading…
Reference in New Issue
Block a user