mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-25 13:07:20 +00:00
Support for populating contacts DB with TS account type.
// FREEBIE
This commit is contained in:
parent
8d9ae731ef
commit
d1940fe0f9
@ -148,6 +148,8 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</activity-alias>
|
</activity-alias>
|
||||||
|
|
||||||
<activity android:name=".ConversationActivity"
|
<activity android:name=".ConversationActivity"
|
||||||
@ -278,6 +280,11 @@
|
|||||||
<data android:scheme="mms" />
|
<data android:scheme="mms" />
|
||||||
<data android:scheme="mmsto" />
|
<data android:scheme="mmsto" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<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.thoughtcrime.securesms.contact" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".RecipientPreferenceActivity"
|
<activity android:name=".RecipientPreferenceActivity"
|
||||||
@ -308,6 +315,20 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service android:name=".service.AccountAuthenticatorService" android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.accounts.AccountAuthenticator" />
|
||||||
|
</intent-filter>
|
||||||
|
<meta-data android:name="android.accounts.AccountAuthenticator" android:resource="@xml/authenticator" />
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<service android:name=".service.ContactsSyncAdapterService" android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.content.SyncAdapter"/>
|
||||||
|
</intent-filter>
|
||||||
|
<meta-data android:name="android.content.SyncAdapter" android:resource="@xml/syncadapter" />
|
||||||
|
<meta-data android:name="android.provider.CONTACTS_STRUCTURE" android:resource="@xml/contactsformat" />
|
||||||
|
</service>
|
||||||
|
|
||||||
<receiver android:name=".gcm.GcmBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND" >
|
<receiver android:name=".gcm.GcmBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND" >
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
6
res/xml/authenticator.xml
Normal file
6
res/xml/authenticator.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:accountType="org.thoughtcrime.securesms"
|
||||||
|
android:icon="@drawable/icon"
|
||||||
|
android:smallIcon="@drawable/icon"
|
||||||
|
android:label="@string/app_name"/>
|
||||||
|
|
9
res/xml/contactsformat.xml
Normal file
9
res/xml/contactsformat.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ContactsSource xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<ContactsDataKind
|
||||||
|
android:icon="@drawable/icon"
|
||||||
|
android:mimeType="vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.contact"
|
||||||
|
android:summaryColumn="data2"
|
||||||
|
android:detailColumn="data3"
|
||||||
|
android:detailSocialSummary="true"/>
|
||||||
|
</ContactsSource>
|
8
res/xml/syncadapter.xml
Normal file
8
res/xml/syncadapter.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:contentAuthority="com.android.contacts"
|
||||||
|
android:accountType="org.thoughtcrime.securesms"
|
||||||
|
android:userVisible="true"
|
||||||
|
android:supportsUploading="false"
|
||||||
|
android:allowParallelSyncs="false"
|
||||||
|
android:isAlwaysSyncable="true"/>
|
@ -2,7 +2,10 @@ package org.thoughtcrime.securesms;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.database.Cursor;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.provider.ContactsContract;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
@ -25,36 +28,79 @@ public class SmsSendtoActivity extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Intent getNextIntent(Intent original) {
|
private Intent getNextIntent(Intent original) {
|
||||||
String body = "";
|
DestinationAndBody destination;
|
||||||
String data = "";
|
|
||||||
|
|
||||||
if (original.getAction().equals(Intent.ACTION_SENDTO)) {
|
if (original.getAction().equals(Intent.ACTION_SENDTO)) {
|
||||||
body = original.getStringExtra("sms_body");
|
destination = getDestinationForSendTo(original);
|
||||||
data = original.getData().getSchemeSpecificPart();
|
} else if (original.getData() != null && "content".equals(original.getData().getScheme())) {
|
||||||
|
destination = getDestinationForSyncAdapter(original);
|
||||||
} else {
|
} else {
|
||||||
try {
|
destination = getDestinationForView(original);
|
||||||
Rfc5724Uri smsUri = new Rfc5724Uri(original.getData().toString());
|
|
||||||
body = smsUri.getQueryParams().get("body");
|
|
||||||
data = smsUri.getPath();
|
|
||||||
} catch (URISyntaxException e) {
|
|
||||||
Log.w(TAG, "unable to parse RFC5724 URI from intent", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Recipients recipients = RecipientFactory.getRecipientsFromString(this, data, false);
|
Recipients recipients = RecipientFactory.getRecipientsFromString(this, destination.getDestination(), false);
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients);
|
long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients);
|
||||||
|
|
||||||
final Intent nextIntent;
|
final Intent nextIntent;
|
||||||
if (recipients == null || recipients.isEmpty()) {
|
if (recipients == null || recipients.isEmpty()) {
|
||||||
nextIntent = new Intent(this, NewConversationActivity.class);
|
nextIntent = new Intent(this, NewConversationActivity.class);
|
||||||
nextIntent.putExtra(ConversationActivity.DRAFT_TEXT_EXTRA, body);
|
nextIntent.putExtra(ConversationActivity.DRAFT_TEXT_EXTRA, destination.getBody());
|
||||||
Toast.makeText(this, R.string.ConversationActivity_specify_recipient, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.ConversationActivity_specify_recipient, Toast.LENGTH_LONG).show();
|
||||||
} else {
|
} else {
|
||||||
nextIntent = new Intent(this, ConversationActivity.class);
|
nextIntent = new Intent(this, ConversationActivity.class);
|
||||||
nextIntent.putExtra(ConversationActivity.DRAFT_TEXT_EXTRA, body);
|
nextIntent.putExtra(ConversationActivity.DRAFT_TEXT_EXTRA, destination.getBody());
|
||||||
nextIntent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
|
nextIntent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
|
||||||
nextIntent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds());
|
nextIntent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds());
|
||||||
}
|
}
|
||||||
return nextIntent;
|
return nextIntent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private @NonNull DestinationAndBody getDestinationForSendTo(Intent intent) {
|
||||||
|
return new DestinationAndBody(intent.getData().getSchemeSpecificPart(),
|
||||||
|
intent.getStringExtra("sms_body"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NonNull DestinationAndBody getDestinationForView(Intent intent) {
|
||||||
|
try {
|
||||||
|
Rfc5724Uri smsUri = new Rfc5724Uri(intent.getData().toString());
|
||||||
|
return new DestinationAndBody(smsUri.getPath(), smsUri.getQueryParams().get("body"));
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
Log.w(TAG, "unable to parse RFC5724 URI from intent", e);
|
||||||
|
return new DestinationAndBody("", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NonNull DestinationAndBody getDestinationForSyncAdapter(Intent intent) {
|
||||||
|
Cursor cursor = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = getContentResolver().query(intent.getData(), null, null, null, null);
|
||||||
|
|
||||||
|
if (cursor != null && cursor.moveToNext()) {
|
||||||
|
return new DestinationAndBody(cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.RawContacts.Data.DATA1)), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DestinationAndBody("", "");
|
||||||
|
} finally {
|
||||||
|
if (cursor != null) cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DestinationAndBody {
|
||||||
|
private final String destination;
|
||||||
|
private final String body;
|
||||||
|
|
||||||
|
private DestinationAndBody(String destination, String body) {
|
||||||
|
this.destination = destination;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDestination() {
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.contacts;
|
package org.thoughtcrime.securesms.contacts;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.content.ContentProviderOperation;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.OperationApplicationException;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.CursorWrapper;
|
import android.database.CursorWrapper;
|
||||||
import android.database.MatrixCursor;
|
import android.database.MatrixCursor;
|
||||||
@ -25,7 +28,10 @@ import android.database.MergeCursor;
|
|||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.provider.BaseColumns;
|
||||||
import android.provider.ContactsContract;
|
import android.provider.ContactsContract;
|
||||||
|
import android.provider.ContactsContract.RawContacts;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@ -36,7 +42,12 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Database to supply all types of contacts that TextSecure needs to know about
|
* Database to supply all types of contacts that TextSecure needs to know about
|
||||||
@ -84,6 +95,88 @@ public class ContactsDatabase {
|
|||||||
dbHelper.close();
|
dbHelper.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized void setRegisteredUsers(Account account, List<String> e164numbers)
|
||||||
|
throws RemoteException, OperationApplicationException
|
||||||
|
{
|
||||||
|
Map<String, Long> currentContacts = new HashMap<>();
|
||||||
|
Set<String> registeredNumbers = new HashSet<>(e164numbers);
|
||||||
|
ArrayList<ContentProviderOperation> operations = new ArrayList<>();
|
||||||
|
Uri currentContactsUri = RawContacts.CONTENT_URI.buildUpon()
|
||||||
|
.appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name)
|
||||||
|
.appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type).build();
|
||||||
|
|
||||||
|
Cursor cursor = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = context.getContentResolver().query(currentContactsUri, new String[] {BaseColumns._ID, RawContacts.SYNC1}, null, null, null);
|
||||||
|
|
||||||
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
|
currentContacts.put(cursor.getString(1), cursor.getLong(0));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (cursor != null)
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String number : e164numbers) {
|
||||||
|
if (!currentContacts.containsKey(number)) {
|
||||||
|
addTextSecureRawContact(operations, account, number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, Long> currentContactEntry : currentContacts.entrySet()) {
|
||||||
|
if (!registeredNumbers.contains(currentContactEntry.getKey())) {
|
||||||
|
removeTextSecureRawContact(operations, account, currentContactEntry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!operations.isEmpty()) {
|
||||||
|
context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTextSecureRawContact(List<ContentProviderOperation> operations,
|
||||||
|
Account account, String e164number)
|
||||||
|
{
|
||||||
|
int index = operations.size();
|
||||||
|
Uri dataUri = ContactsContract.Data.CONTENT_URI.buildUpon()
|
||||||
|
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
operations.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
|
||||||
|
.withValue(RawContacts.ACCOUNT_NAME, account.name)
|
||||||
|
.withValue(RawContacts.ACCOUNT_TYPE, account.type)
|
||||||
|
.withValue(RawContacts.SYNC1, e164number)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
operations.add(ContentProviderOperation.newInsert(dataUri)
|
||||||
|
.withValueBackReference(ContactsContract.CommonDataKinds.Phone.RAW_CONTACT_ID, index)
|
||||||
|
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
|
||||||
|
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, e164number)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
operations.add(ContentProviderOperation.newInsert(dataUri)
|
||||||
|
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index)
|
||||||
|
.withValue(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.contact")
|
||||||
|
.withValue(ContactsContract.Data.DATA1, e164number)
|
||||||
|
.withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name))
|
||||||
|
.withValue(ContactsContract.Data.DATA3, String.format("Message %s", e164number))
|
||||||
|
.withYieldAllowed(true)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeTextSecureRawContact(List<ContentProviderOperation> operations,
|
||||||
|
Account account, long rowId)
|
||||||
|
{
|
||||||
|
operations.add(ContentProviderOperation.newDelete(RawContacts.CONTENT_URI.buildUpon()
|
||||||
|
.appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name)
|
||||||
|
.appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type)
|
||||||
|
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build())
|
||||||
|
.withYieldAllowed(true)
|
||||||
|
.withSelection(BaseColumns._ID + " = ?", new String[] {String.valueOf(rowId)})
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
public Cursor query(String filter, boolean pushOnly) {
|
public Cursor query(String filter, boolean pushOnly) {
|
||||||
// FIXME: This doesn't make sense to me. You pass in pushOnly, but then
|
// FIXME: This doesn't make sense to me. You pass in pushOnly, but then
|
||||||
// conditionally check to see whether other contacts should be included
|
// conditionally check to see whether other contacts should be included
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
package org.thoughtcrime.securesms.contacts;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.content.AbstractThreadedSyncAdapter;
|
||||||
|
import android.content.ContentProviderClient;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SyncResult;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class ContactsSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
|
|
||||||
|
private static final String TAG = ContactsSyncAdapter.class.getSimpleName();
|
||||||
|
|
||||||
|
public ContactsSyncAdapter(Context context, boolean autoInitialize) {
|
||||||
|
super(context, autoInitialize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPerformSync(Account account, Bundle extras, String authority,
|
||||||
|
ContentProviderClient provider, SyncResult syncResult)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
DirectoryHelper.refreshDirectory(getContext());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -24,20 +24,18 @@ public class TextSecureDirectory {
|
|||||||
private static final int INTRODUCED_CHANGE_FROM_TOKEN_TO_E164_NUMBER = 2;
|
private static final int INTRODUCED_CHANGE_FROM_TOKEN_TO_E164_NUMBER = 2;
|
||||||
|
|
||||||
private static final String DATABASE_NAME = "whisper_directory.db";
|
private static final String DATABASE_NAME = "whisper_directory.db";
|
||||||
private static final int DATABASE_VERSION = 2;
|
private static final int DATABASE_VERSION = 3;
|
||||||
|
|
||||||
private static final String TABLE_NAME = "directory";
|
private static final String TABLE_NAME = "directory";
|
||||||
private static final String ID = "_id";
|
private static final String ID = "_id";
|
||||||
private static final String NUMBER = "number";
|
private static final String NUMBER = "number";
|
||||||
private static final String REGISTERED = "registered";
|
private static final String REGISTERED = "registered";
|
||||||
private static final String RELAY = "relay";
|
private static final String RELAY = "relay";
|
||||||
private static final String SUPPORTS_SMS = "supports_sms";
|
|
||||||
private static final String TIMESTAMP = "timestamp";
|
private static final String TIMESTAMP = "timestamp";
|
||||||
private static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "(" + ID + " INTEGER PRIMARY KEY, " +
|
private static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "(" + ID + " INTEGER PRIMARY KEY, " +
|
||||||
NUMBER + " TEXT UNIQUE, " +
|
NUMBER + " TEXT UNIQUE, " +
|
||||||
REGISTERED + " INTEGER, " +
|
REGISTERED + " INTEGER, " +
|
||||||
RELAY + " TEXT, " +
|
RELAY + " TEXT, " +
|
||||||
SUPPORTS_SMS + " INTEGER, " +
|
|
||||||
TIMESTAMP + " INTEGER);";
|
TIMESTAMP + " INTEGER);";
|
||||||
|
|
||||||
private static final Object instanceLock = new Object();
|
private static final Object instanceLock = new Object();
|
||||||
@ -63,25 +61,6 @@ public class TextSecureDirectory {
|
|||||||
this.databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
|
this.databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSmsFallbackSupported(String e164number) {
|
|
||||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
|
||||||
Cursor cursor = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
cursor = db.query(TABLE_NAME, new String[] {SUPPORTS_SMS}, NUMBER + " = ?",
|
|
||||||
new String[]{e164number}, null, null, null);
|
|
||||||
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
|
||||||
return cursor.getInt(0) == 1;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (cursor != null)
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isActiveNumber(String e164number) throws NotInDirectoryException {
|
public boolean isActiveNumber(String e164number) throws NotInDirectoryException {
|
||||||
if (e164number == null || e164number.length() == 0) {
|
if (e164number == null || e164number.length() == 0) {
|
||||||
return false;
|
return false;
|
||||||
@ -131,7 +110,6 @@ public class TextSecureDirectory {
|
|||||||
values.put(NUMBER, token.getNumber());
|
values.put(NUMBER, token.getNumber());
|
||||||
values.put(RELAY, token.getRelay());
|
values.put(RELAY, token.getRelay());
|
||||||
values.put(REGISTERED, active ? 1 : 0);
|
values.put(REGISTERED, active ? 1 : 0);
|
||||||
values.put(SUPPORTS_SMS, token.isSupportsSms() ? 1 : 0);
|
|
||||||
values.put(TIMESTAMP, System.currentTimeMillis());
|
values.put(TIMESTAMP, System.currentTimeMillis());
|
||||||
db.replace(TABLE_NAME, null, values);
|
db.replace(TABLE_NAME, null, values);
|
||||||
}
|
}
|
||||||
@ -149,7 +127,6 @@ public class TextSecureDirectory {
|
|||||||
values.put(REGISTERED, 1);
|
values.put(REGISTERED, 1);
|
||||||
values.put(TIMESTAMP, timestamp);
|
values.put(TIMESTAMP, timestamp);
|
||||||
values.put(RELAY, token.getRelay());
|
values.put(RELAY, token.getRelay());
|
||||||
values.put(SUPPORTS_SMS, token.isSupportsSms() ? 1 : 0);
|
|
||||||
db.replace(TABLE_NAME, null, values);
|
db.replace(TABLE_NAME, null, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +146,7 @@ public class TextSecureDirectory {
|
|||||||
|
|
||||||
public Set<String> getPushEligibleContactNumbers(String localNumber) {
|
public Set<String> getPushEligibleContactNumbers(String localNumber) {
|
||||||
final Uri uri = Phone.CONTENT_URI;
|
final Uri uri = Phone.CONTENT_URI;
|
||||||
final Set<String> results = new HashSet<String>();
|
final Set<String> results = new HashSet<>();
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -208,7 +185,7 @@ public class TextSecureDirectory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getActiveNumbers() {
|
public List<String> getActiveNumbers() {
|
||||||
final List<String> results = new ArrayList<String>();
|
final List<String> results = new ArrayList<>();
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
try {
|
try {
|
||||||
cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[]{NUMBER},
|
cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[]{NUMBER},
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
package org.thoughtcrime.securesms.service;
|
||||||
|
|
||||||
|
import android.accounts.AbstractAccountAuthenticator;
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.accounts.AccountAuthenticatorResponse;
|
||||||
|
import android.accounts.NetworkErrorException;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
|
||||||
|
public class AccountAuthenticatorService extends Service {
|
||||||
|
|
||||||
|
private static AccountAuthenticatorImpl accountAuthenticator = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
if (intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT)) {
|
||||||
|
return getAuthenticator().getIBinder();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized AccountAuthenticatorImpl getAuthenticator() {
|
||||||
|
if (accountAuthenticator == null) {
|
||||||
|
accountAuthenticator = new AccountAuthenticatorImpl(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return accountAuthenticator;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AccountAuthenticatorImpl extends AbstractAccountAuthenticator {
|
||||||
|
|
||||||
|
public AccountAuthenticatorImpl(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType,
|
||||||
|
String[] requiredFeatures, Bundle options)
|
||||||
|
throws NetworkErrorException
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType,
|
||||||
|
Bundle options) throws NetworkErrorException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAuthTokenLabel(String authTokenType) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features)
|
||||||
|
throws NetworkErrorException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType,
|
||||||
|
Bundle options) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package org.thoughtcrime.securesms.service;
|
||||||
|
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.contacts.ContactsSyncAdapter;
|
||||||
|
|
||||||
|
public class ContactsSyncAdapterService extends Service {
|
||||||
|
|
||||||
|
private static ContactsSyncAdapter syncAdapter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void onCreate() {
|
||||||
|
if (syncAdapter == null) {
|
||||||
|
syncAdapter = new ContactsSyncAdapter(this, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return syncAdapter.getSyncAdapterBinder();
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +1,28 @@
|
|||||||
package org.thoughtcrime.securesms.util;
|
package org.thoughtcrime.securesms.util;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.accounts.AccountManager;
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.OperationApplicationException;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.provider.ContactsContract;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.contacts.ContactsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.NotInDirectoryException;
|
import org.thoughtcrime.securesms.database.NotInDirectoryException;
|
||||||
import org.thoughtcrime.securesms.database.TextSecureDirectory;
|
import org.thoughtcrime.securesms.database.TextSecureDirectory;
|
||||||
import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory;
|
import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||||
|
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||||
import org.whispersystems.textsecure.api.TextSecureAccountManager;
|
import org.whispersystems.textsecure.api.TextSecureAccountManager;
|
||||||
import org.whispersystems.textsecure.api.push.ContactTokenDetails;
|
import org.whispersystems.textsecure.api.push.ContactTokenDetails;
|
||||||
import org.whispersystems.textsecure.api.util.InvalidNumberException;
|
import org.whispersystems.textsecure.api.util.InvalidNumberException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@ -65,6 +74,7 @@ public class DirectoryHelper {
|
|||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
TextSecureDirectory directory = TextSecureDirectory.getInstance(context);
|
TextSecureDirectory directory = TextSecureDirectory.getInstance(context);
|
||||||
|
Optional<Account> account = getOrCreateAccount(context);
|
||||||
Set<String> eligibleContactNumbers = directory.getPushEligibleContactNumbers(localNumber);
|
Set<String> eligibleContactNumbers = directory.getPushEligibleContactNumbers(localNumber);
|
||||||
List<ContactTokenDetails> activeTokens = accountManager.getContacts(eligibleContactNumbers);
|
List<ContactTokenDetails> activeTokens = accountManager.getContacts(eligibleContactNumbers);
|
||||||
|
|
||||||
@ -75,6 +85,20 @@ public class DirectoryHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
directory.setNumbers(activeTokens, eligibleContactNumbers);
|
directory.setNumbers(activeTokens, eligibleContactNumbers);
|
||||||
|
|
||||||
|
if (account.isPresent()) {
|
||||||
|
List<String> e164numbers = new LinkedList<>();
|
||||||
|
|
||||||
|
for (ContactTokenDetails contactTokenDetails : activeTokens) {
|
||||||
|
e164numbers.add(contactTokenDetails.getNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
new ContactsDatabase(context).setRegisteredUsers(account.get(), e164numbers);
|
||||||
|
} catch (RemoteException | OperationApplicationException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,24 +137,25 @@ public class DirectoryHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isSmsFallbackAllowed(Context context, Recipients recipients) {
|
private static Optional<Account> getOrCreateAccount(Context context) {
|
||||||
try {
|
AccountManager accountManager = AccountManager.get(context);
|
||||||
if (recipients == null || !recipients.isSingleRecipient() || recipients.isGroupRecipient()) {
|
Account[] accounts = accountManager.getAccountsByType("org.thoughtcrime.securesms");
|
||||||
return false;
|
|
||||||
|
if (accounts.length == 0) return createAccount(context);
|
||||||
|
else return Optional.of(accounts[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
final String number = recipients.getPrimaryRecipient().getNumber();
|
private static Optional<Account> createAccount(Context context) {
|
||||||
|
AccountManager accountManager = AccountManager.get(context);
|
||||||
|
Account account = new Account(context.getString(R.string.app_name), "org.thoughtcrime.securesms");
|
||||||
|
|
||||||
if (number == null) {
|
if (accountManager.addAccountExplicitly(account, null, null)) {
|
||||||
return false;
|
Log.w(TAG, "Created new account...");
|
||||||
}
|
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
|
||||||
|
return Optional.of(account);
|
||||||
final String e164number = Util.canonicalizeNumber(context, number);
|
} else {
|
||||||
|
Log.w(TAG, "Failed to create account!");
|
||||||
return TextSecureDirectory.getInstance(context).isSmsFallbackSupported(e164number);
|
return Optional.absent();
|
||||||
} catch (InvalidNumberException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user