2015-06-09 14:37:20 +00:00
package org.thoughtcrime.securesms.database ;
import android.content.ContentValues ;
import android.content.Context ;
import android.database.Cursor ;
import android.net.Uri ;
import android.support.annotation.NonNull ;
import android.support.annotation.Nullable ;
2017-08-07 04:43:11 +00:00
import com.annimon.stream.Stream ;
2018-01-25 03:17:44 +00:00
import net.sqlcipher.database.SQLiteDatabase ;
2015-06-30 16:16:05 +00:00
import org.thoughtcrime.securesms.color.MaterialColor ;
2018-01-25 03:17:44 +00:00
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper ;
2018-05-22 09:13:10 +00:00
import org.thoughtcrime.securesms.logging.Log ;
2017-08-01 15:56:00 +00:00
import org.thoughtcrime.securesms.recipients.Recipient ;
2017-08-15 01:11:13 +00:00
import org.thoughtcrime.securesms.util.Base64 ;
2017-11-26 18:45:39 +00:00
import org.thoughtcrime.securesms.util.Util ;
2016-03-23 17:34:41 +00:00
import org.whispersystems.libsignal.util.guava.Optional ;
2015-06-09 14:37:20 +00:00
2018-08-16 16:47:43 +00:00
import java.io.Closeable ;
2017-08-15 01:11:13 +00:00
import java.io.IOException ;
2017-11-26 18:45:39 +00:00
import java.util.HashMap ;
2017-08-07 21:24:53 +00:00
import java.util.HashSet ;
import java.util.LinkedList ;
2017-08-07 04:43:11 +00:00
import java.util.List ;
2017-11-26 18:45:39 +00:00
import java.util.Map ;
2017-08-07 21:24:53 +00:00
import java.util.Set ;
2018-09-25 22:41:42 +00:00
import java.util.function.Consumer ;
2017-08-07 04:43:11 +00:00
2017-08-22 01:37:39 +00:00
public class RecipientDatabase extends Database {
2015-06-09 14:37:20 +00:00
2017-08-22 01:37:39 +00:00
private static final String TAG = RecipientDatabase . class . getSimpleName ( ) ;
2015-06-09 14:37:20 +00:00
2018-05-22 09:13:10 +00:00
static final String TABLE_NAME = " recipient_preferences " ;
private static final String ID = " _id " ;
static final String ADDRESS = " recipient_ids " ;
private static final String BLOCK = " block " ;
private static final String NOTIFICATION = " notification " ;
private static final String VIBRATE = " vibrate " ;
private static final String MUTE_UNTIL = " mute_until " ;
private static final String COLOR = " color " ;
private static final String SEEN_INVITE_REMINDER = " seen_invite_reminder " ;
private static final String DEFAULT_SUBSCRIPTION_ID = " default_subscription_id " ;
private static final String EXPIRE_MESSAGES = " expire_messages " ;
private static final String REGISTERED = " registered " ;
private static final String PROFILE_KEY = " profile_key " ;
private static final String SYSTEM_DISPLAY_NAME = " system_display_name " ;
private static final String SYSTEM_PHOTO_URI = " system_contact_photo " ;
private static final String SYSTEM_PHONE_LABEL = " system_phone_label " ;
private static final String SYSTEM_CONTACT_URI = " system_contact_uri " ;
private static final String SIGNAL_PROFILE_NAME = " signal_profile_name " ;
private static final String SIGNAL_PROFILE_AVATAR = " signal_profile_avatar " ;
private static final String PROFILE_SHARING = " profile_sharing_approval " ;
private static final String CALL_RINGTONE = " call_ringtone " ;
private static final String CALL_VIBRATE = " call_vibrate " ;
private static final String NOTIFICATION_CHANNEL = " notification_channel " ;
private static final String UNIDENTIFIED_ACCESS_MODE = " unidentified_access_mode " ;
2019-04-12 19:22:38 +00:00
private static final String FORCE_SMS_SELECTION = " force_sms_selection " ;
2015-06-09 14:37:20 +00:00
2017-08-07 04:43:11 +00:00
private static final String [ ] RECIPIENT_PROJECTION = new String [ ] {
2018-02-16 19:10:35 +00:00
BLOCK , NOTIFICATION , CALL_RINGTONE , VIBRATE , CALL_VIBRATE , MUTE_UNTIL , COLOR , SEEN_INVITE_REMINDER , DEFAULT_SUBSCRIPTION_ID , EXPIRE_MESSAGES , REGISTERED ,
2017-11-26 18:45:39 +00:00
PROFILE_KEY , SYSTEM_DISPLAY_NAME , SYSTEM_PHOTO_URI , SYSTEM_PHONE_LABEL , SYSTEM_CONTACT_URI ,
2018-05-22 09:13:10 +00:00
SIGNAL_PROFILE_NAME , SIGNAL_PROFILE_AVATAR , PROFILE_SHARING , NOTIFICATION_CHANNEL ,
2019-04-12 19:22:38 +00:00
UNIDENTIFIED_ACCESS_MODE ,
FORCE_SMS_SELECTION ,
2017-08-07 04:43:11 +00:00
} ;
static final List < String > TYPED_RECIPIENT_PROJECTION = Stream . of ( RECIPIENT_PROJECTION )
. map ( columnName - > TABLE_NAME + " . " + columnName )
. toList ( ) ;
2015-06-09 14:37:20 +00:00
public enum VibrateState {
DEFAULT ( 0 ) , ENABLED ( 1 ) , DISABLED ( 2 ) ;
private final int id ;
VibrateState ( int id ) {
this . id = id ;
}
public int getId ( ) {
return id ;
}
public static VibrateState fromId ( int id ) {
return values ( ) [ id ] ;
}
}
2017-08-22 17:44:04 +00:00
public enum RegisteredState {
UNKNOWN ( 0 ) , REGISTERED ( 1 ) , NOT_REGISTERED ( 2 ) ;
private final int id ;
RegisteredState ( int id ) {
this . id = id ;
}
public int getId ( ) {
return id ;
}
public static RegisteredState fromId ( int id ) {
return values ( ) [ id ] ;
}
}
2018-05-22 09:13:10 +00:00
public enum UnidentifiedAccessMode {
UNKNOWN ( 0 ) , DISABLED ( 1 ) , ENABLED ( 2 ) , UNRESTRICTED ( 3 ) ;
private final int mode ;
UnidentifiedAccessMode ( int mode ) {
this . mode = mode ;
}
public int getMode ( ) {
return mode ;
}
public static UnidentifiedAccessMode fromMode ( int mode ) {
return values ( ) [ mode ] ;
}
}
2015-06-09 14:37:20 +00:00
public static final String CREATE_TABLE =
" CREATE TABLE " + TABLE_NAME +
" ( " + ID + " INTEGER PRIMARY KEY, " +
2017-08-01 15:56:00 +00:00
ADDRESS + " TEXT UNIQUE, " +
2015-06-09 14:37:20 +00:00
BLOCK + " INTEGER DEFAULT 0, " +
NOTIFICATION + " TEXT DEFAULT NULL, " +
VIBRATE + " INTEGER DEFAULT " + VibrateState . DEFAULT . getId ( ) + " , " +
2015-06-23 22:10:50 +00:00
MUTE_UNTIL + " INTEGER DEFAULT 0, " +
2015-10-14 04:44:01 +00:00
COLOR + " TEXT DEFAULT NULL, " +
2016-02-06 00:10:33 +00:00
SEEN_INVITE_REMINDER + " INTEGER DEFAULT 0, " +
2016-08-16 03:23:56 +00:00
DEFAULT_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " +
2017-08-07 21:24:53 +00:00
EXPIRE_MESSAGES + " INTEGER DEFAULT 0, " +
2017-08-07 22:31:12 +00:00
REGISTERED + " INTEGER DEFAULT 0, " +
2017-08-15 01:11:13 +00:00
SYSTEM_DISPLAY_NAME + " TEXT DEFAULT NULL, " +
2017-11-26 18:45:39 +00:00
SYSTEM_PHOTO_URI + " TEXT DEFAULT NULL, " +
SYSTEM_PHONE_LABEL + " TEXT DEFAULT NULL, " +
SYSTEM_CONTACT_URI + " TEXT DEFAULT NULL, " +
2017-08-15 01:11:13 +00:00
PROFILE_KEY + " TEXT DEFAULT NULL, " +
SIGNAL_PROFILE_NAME + " TEXT DEFAULT NULL, " +
2017-08-17 04:49:41 +00:00
SIGNAL_PROFILE_AVATAR + " TEXT DEFAULT NULL, " +
2018-02-16 19:10:35 +00:00
PROFILE_SHARING + " INTEGER DEFAULT 0, " +
CALL_RINGTONE + " TEXT DEFAULT NULL, " +
2018-08-16 16:47:43 +00:00
CALL_VIBRATE + " INTEGER DEFAULT " + VibrateState . DEFAULT . getId ( ) + " , " +
2018-05-22 09:13:10 +00:00
NOTIFICATION_CHANNEL + " TEXT DEFAULT NULL, " +
2019-04-12 19:22:38 +00:00
UNIDENTIFIED_ACCESS_MODE + " INTEGER DEFAULT 0, " +
FORCE_SMS_SELECTION + " INTEGER DEFAULT 0); " ;
2015-06-09 14:37:20 +00:00
2018-01-25 03:17:44 +00:00
public RecipientDatabase ( Context context , SQLCipherOpenHelper databaseHelper ) {
2015-06-09 14:37:20 +00:00
super ( context , databaseHelper ) ;
}
public Cursor getBlocked ( ) {
SQLiteDatabase database = databaseHelper . getReadableDatabase ( ) ;
2017-11-26 18:45:39 +00:00
return database . query ( TABLE_NAME , new String [ ] { ID , ADDRESS } , BLOCK + " = 1 " ,
null , null , null , null , null ) ;
2015-06-09 14:37:20 +00:00
}
2018-08-16 16:47:43 +00:00
public RecipientReader readerForBlocked ( Cursor cursor ) {
return new RecipientReader ( context , cursor ) ;
}
public RecipientReader getRecipientsWithNotificationChannels ( ) {
SQLiteDatabase database = databaseHelper . getReadableDatabase ( ) ;
Cursor cursor = database . query ( TABLE_NAME , new String [ ] { ID , ADDRESS } , NOTIFICATION_CHANNEL + " NOT NULL " ,
null , null , null , null , null ) ;
return new RecipientReader ( context , cursor ) ;
2016-08-26 23:53:23 +00:00
}
2017-08-22 01:47:37 +00:00
public Optional < RecipientSettings > getRecipientSettings ( @NonNull Address address ) {
2015-06-09 14:37:20 +00:00
SQLiteDatabase database = databaseHelper . getReadableDatabase ( ) ;
Cursor cursor = null ;
try {
2017-08-01 15:56:00 +00:00
cursor = database . query ( TABLE_NAME , null , ADDRESS + " = ? " , new String [ ] { address . serialize ( ) } , null , null , null ) ;
2015-06-09 14:37:20 +00:00
if ( cursor ! = null & & cursor . moveToNext ( ) ) {
2017-08-22 17:44:04 +00:00
return getRecipientSettings ( cursor ) ;
2015-06-09 14:37:20 +00:00
}
return Optional . absent ( ) ;
} finally {
if ( cursor ! = null ) cursor . close ( ) ;
}
}
2017-08-22 17:44:04 +00:00
Optional < RecipientSettings > getRecipientSettings ( @NonNull Cursor cursor ) {
2018-05-22 09:13:10 +00:00
boolean blocked = cursor . getInt ( cursor . getColumnIndexOrThrow ( BLOCK ) ) = = 1 ;
String messageRingtone = cursor . getString ( cursor . getColumnIndexOrThrow ( NOTIFICATION ) ) ;
String callRingtone = cursor . getString ( cursor . getColumnIndexOrThrow ( CALL_RINGTONE ) ) ;
int messageVibrateState = cursor . getInt ( cursor . getColumnIndexOrThrow ( VIBRATE ) ) ;
int callVibrateState = cursor . getInt ( cursor . getColumnIndexOrThrow ( CALL_VIBRATE ) ) ;
long muteUntil = cursor . getLong ( cursor . getColumnIndexOrThrow ( MUTE_UNTIL ) ) ;
String serializedColor = cursor . getString ( cursor . getColumnIndexOrThrow ( COLOR ) ) ;
boolean seenInviteReminder = cursor . getInt ( cursor . getColumnIndexOrThrow ( SEEN_INVITE_REMINDER ) ) = = 1 ;
int defaultSubscriptionId = cursor . getInt ( cursor . getColumnIndexOrThrow ( DEFAULT_SUBSCRIPTION_ID ) ) ;
int expireMessages = cursor . getInt ( cursor . getColumnIndexOrThrow ( EXPIRE_MESSAGES ) ) ;
int registeredState = cursor . getInt ( cursor . getColumnIndexOrThrow ( REGISTERED ) ) ;
String profileKeyString = cursor . getString ( cursor . getColumnIndexOrThrow ( PROFILE_KEY ) ) ;
String systemDisplayName = cursor . getString ( cursor . getColumnIndexOrThrow ( SYSTEM_DISPLAY_NAME ) ) ;
String systemContactPhoto = cursor . getString ( cursor . getColumnIndexOrThrow ( SYSTEM_PHOTO_URI ) ) ;
String systemPhoneLabel = cursor . getString ( cursor . getColumnIndexOrThrow ( SYSTEM_PHONE_LABEL ) ) ;
String systemContactUri = cursor . getString ( cursor . getColumnIndexOrThrow ( SYSTEM_CONTACT_URI ) ) ;
String signalProfileName = cursor . getString ( cursor . getColumnIndexOrThrow ( SIGNAL_PROFILE_NAME ) ) ;
String signalProfileAvatar = cursor . getString ( cursor . getColumnIndexOrThrow ( SIGNAL_PROFILE_AVATAR ) ) ;
boolean profileSharing = cursor . getInt ( cursor . getColumnIndexOrThrow ( PROFILE_SHARING ) ) = = 1 ;
String notificationChannel = cursor . getString ( cursor . getColumnIndexOrThrow ( NOTIFICATION_CHANNEL ) ) ;
int unidentifiedAccessMode = cursor . getInt ( cursor . getColumnIndexOrThrow ( UNIDENTIFIED_ACCESS_MODE ) ) ;
2019-04-12 19:22:38 +00:00
boolean forceSmsSelection = cursor . getInt ( cursor . getColumnIndexOrThrow ( FORCE_SMS_SELECTION ) ) = = 1 ;
2017-08-07 04:43:11 +00:00
MaterialColor color ;
2018-05-22 09:13:10 +00:00
byte [ ] profileKey = null ;
2017-08-07 04:43:11 +00:00
try {
color = serializedColor = = null ? null : MaterialColor . fromSerialized ( serializedColor ) ;
} catch ( MaterialColor . UnknownColorException e ) {
Log . w ( TAG , e ) ;
color = null ;
}
2017-08-15 01:11:13 +00:00
if ( profileKeyString ! = null ) {
try {
profileKey = Base64 . decode ( profileKeyString ) ;
} catch ( IOException e ) {
Log . w ( TAG , e ) ;
profileKey = null ;
}
}
2017-08-22 01:47:37 +00:00
return Optional . of ( new RecipientSettings ( blocked , muteUntil ,
2018-02-16 19:10:35 +00:00
VibrateState . fromId ( messageVibrateState ) ,
VibrateState . fromId ( callVibrateState ) ,
Util . uri ( messageRingtone ) , Util . uri ( callRingtone ) ,
color , seenInviteReminder ,
2017-08-22 17:44:04 +00:00
defaultSubscriptionId , expireMessages ,
RegisteredState . fromId ( registeredState ) ,
2017-11-26 18:45:39 +00:00
profileKey , systemDisplayName , systemContactPhoto ,
systemPhoneLabel , systemContactUri ,
2018-08-16 16:47:43 +00:00
signalProfileName , signalProfileAvatar , profileSharing ,
2019-04-12 19:22:38 +00:00
notificationChannel , UnidentifiedAccessMode . fromMode ( unidentifiedAccessMode ) ,
forceSmsSelection ) ) ;
2017-08-07 04:43:11 +00:00
}
2017-11-26 18:45:39 +00:00
public BulkOperationsHandle resetAllSystemContactInfo ( ) {
2017-08-16 02:23:42 +00:00
SQLiteDatabase database = databaseHelper . getWritableDatabase ( ) ;
database . beginTransaction ( ) ;
ContentValues contentValues = new ContentValues ( 1 ) ;
contentValues . put ( SYSTEM_DISPLAY_NAME , ( String ) null ) ;
2017-11-26 18:45:39 +00:00
contentValues . put ( SYSTEM_PHOTO_URI , ( String ) null ) ;
contentValues . put ( SYSTEM_PHONE_LABEL , ( String ) null ) ;
contentValues . put ( SYSTEM_CONTACT_URI , ( String ) null ) ;
2017-08-16 02:23:42 +00:00
database . update ( TABLE_NAME , contentValues , null , null ) ;
return new BulkOperationsHandle ( database ) ;
}
2017-08-22 17:44:04 +00:00
public void setColor ( @NonNull Recipient recipient , @NonNull MaterialColor color ) {
2015-06-23 22:10:50 +00:00
ContentValues values = new ContentValues ( ) ;
2015-06-30 16:16:05 +00:00
values . put ( COLOR , color . serialize ( ) ) ;
2017-08-07 22:31:12 +00:00
updateOrInsert ( recipient . getAddress ( ) , values ) ;
2017-08-22 17:44:04 +00:00
recipient . resolve ( ) . setColor ( color ) ;
2015-06-23 22:10:50 +00:00
}
2017-08-01 15:56:00 +00:00
public void setDefaultSubscriptionId ( @NonNull Recipient recipient , int defaultSubscriptionId ) {
2016-02-06 00:10:33 +00:00
ContentValues values = new ContentValues ( ) ;
values . put ( DEFAULT_SUBSCRIPTION_ID , defaultSubscriptionId ) ;
2017-08-07 22:31:12 +00:00
updateOrInsert ( recipient . getAddress ( ) , values ) ;
2017-08-22 17:44:04 +00:00
recipient . resolve ( ) . setDefaultSubscriptionId ( Optional . of ( defaultSubscriptionId ) ) ;
2016-02-06 00:10:33 +00:00
}
2019-04-12 19:22:38 +00:00
public void setForceSmsSelection ( @NonNull Recipient recipient , boolean forceSmsSelection ) {
ContentValues contentValues = new ContentValues ( 1 ) ;
contentValues . put ( FORCE_SMS_SELECTION , forceSmsSelection ? 1 : 0 ) ;
updateOrInsert ( recipient . getAddress ( ) , contentValues ) ;
recipient . resolve ( ) . setForceSmsSelection ( forceSmsSelection ) ;
}
2017-08-22 17:44:04 +00:00
public void setBlocked ( @NonNull Recipient recipient , boolean blocked ) {
2015-06-09 14:37:20 +00:00
ContentValues values = new ContentValues ( ) ;
values . put ( BLOCK , blocked ? 1 : 0 ) ;
2017-08-07 22:31:12 +00:00
updateOrInsert ( recipient . getAddress ( ) , values ) ;
2017-08-22 17:44:04 +00:00
recipient . resolve ( ) . setBlocked ( blocked ) ;
2015-06-09 14:37:20 +00:00
}
2018-02-16 19:10:35 +00:00
public void setMessageRingtone ( @NonNull Recipient recipient , @Nullable Uri notification ) {
2015-06-09 14:37:20 +00:00
ContentValues values = new ContentValues ( ) ;
values . put ( NOTIFICATION , notification = = null ? null : notification . toString ( ) ) ;
2017-08-07 22:31:12 +00:00
updateOrInsert ( recipient . getAddress ( ) , values ) ;
2018-02-16 19:10:35 +00:00
recipient . resolve ( ) . setMessageRingtone ( notification ) ;
2015-06-09 14:37:20 +00:00
}
2018-02-16 19:10:35 +00:00
public void setCallRingtone ( @NonNull Recipient recipient , @Nullable Uri ringtone ) {
ContentValues values = new ContentValues ( ) ;
values . put ( CALL_RINGTONE , ringtone = = null ? null : ringtone . toString ( ) ) ;
updateOrInsert ( recipient . getAddress ( ) , values ) ;
recipient . resolve ( ) . setCallRingtone ( ringtone ) ;
}
public void setMessageVibrate ( @NonNull Recipient recipient , @NonNull VibrateState enabled ) {
2015-06-09 14:37:20 +00:00
ContentValues values = new ContentValues ( ) ;
values . put ( VIBRATE , enabled . getId ( ) ) ;
2017-08-07 22:31:12 +00:00
updateOrInsert ( recipient . getAddress ( ) , values ) ;
2018-02-16 19:10:35 +00:00
recipient . resolve ( ) . setMessageVibrate ( enabled ) ;
}
public void setCallVibrate ( @NonNull Recipient recipient , @NonNull VibrateState enabled ) {
ContentValues values = new ContentValues ( ) ;
values . put ( CALL_VIBRATE , enabled . getId ( ) ) ;
updateOrInsert ( recipient . getAddress ( ) , values ) ;
recipient . resolve ( ) . setCallVibrate ( enabled ) ;
2015-06-09 14:37:20 +00:00
}
2017-08-22 17:44:04 +00:00
public void setMuted ( @NonNull Recipient recipient , long until ) {
2015-06-09 14:37:20 +00:00
ContentValues values = new ContentValues ( ) ;
values . put ( MUTE_UNTIL , until ) ;
2017-08-07 22:31:12 +00:00
updateOrInsert ( recipient . getAddress ( ) , values ) ;
2017-08-22 17:44:04 +00:00
recipient . resolve ( ) . setMuted ( until ) ;
2015-06-09 14:37:20 +00:00
}
2017-11-26 18:45:39 +00:00
public void setSeenInviteReminder ( @NonNull Recipient recipient , @SuppressWarnings ( " SameParameterValue " ) boolean seen ) {
2015-10-14 04:44:01 +00:00
ContentValues values = new ContentValues ( 1 ) ;
values . put ( SEEN_INVITE_REMINDER , seen ? 1 : 0 ) ;
2017-08-07 22:31:12 +00:00
updateOrInsert ( recipient . getAddress ( ) , values ) ;
2017-08-22 17:44:04 +00:00
recipient . resolve ( ) . setHasSeenInviteReminder ( seen ) ;
2015-10-14 04:44:01 +00:00
}
2017-08-22 17:44:04 +00:00
public void setExpireMessages ( @NonNull Recipient recipient , int expiration ) {
2017-08-01 15:56:00 +00:00
recipient . setExpireMessages ( expiration ) ;
2016-08-16 03:23:56 +00:00
ContentValues values = new ContentValues ( 1 ) ;
values . put ( EXPIRE_MESSAGES , expiration ) ;
2017-08-07 22:31:12 +00:00
updateOrInsert ( recipient . getAddress ( ) , values ) ;
2017-08-22 17:44:04 +00:00
recipient . resolve ( ) . setExpireMessages ( expiration ) ;
2017-08-07 22:31:12 +00:00
}
2018-05-22 09:13:10 +00:00
public void setUnidentifiedAccessMode ( @NonNull Recipient recipient , @NonNull UnidentifiedAccessMode unidentifiedAccessMode ) {
ContentValues values = new ContentValues ( 1 ) ;
values . put ( UNIDENTIFIED_ACCESS_MODE , unidentifiedAccessMode . getMode ( ) ) ;
updateOrInsert ( recipient . getAddress ( ) , values ) ;
recipient . resolve ( ) . setUnidentifiedAccessMode ( unidentifiedAccessMode ) ;
}
2017-08-22 17:44:04 +00:00
public void setProfileKey ( @NonNull Recipient recipient , @Nullable byte [ ] profileKey ) {
2017-08-15 01:11:13 +00:00
ContentValues values = new ContentValues ( 1 ) ;
values . put ( PROFILE_KEY , profileKey = = null ? null : Base64 . encodeBytes ( profileKey ) ) ;
2017-08-22 17:44:04 +00:00
updateOrInsert ( recipient . getAddress ( ) , values ) ;
recipient . resolve ( ) . setProfileKey ( profileKey ) ;
2017-08-15 01:11:13 +00:00
}
2017-08-22 17:44:04 +00:00
public void setProfileName ( @NonNull Recipient recipient , @Nullable String profileName ) {
2017-08-15 01:11:13 +00:00
ContentValues contentValues = new ContentValues ( 1 ) ;
contentValues . put ( SIGNAL_PROFILE_NAME , profileName ) ;
2017-08-22 17:44:04 +00:00
updateOrInsert ( recipient . getAddress ( ) , contentValues ) ;
recipient . resolve ( ) . setProfileName ( profileName ) ;
2017-08-15 01:11:13 +00:00
}
2017-08-22 17:44:04 +00:00
public void setProfileAvatar ( @NonNull Recipient recipient , @Nullable String profileAvatar ) {
2017-08-15 01:11:13 +00:00
ContentValues contentValues = new ContentValues ( 1 ) ;
contentValues . put ( SIGNAL_PROFILE_AVATAR , profileAvatar ) ;
2017-08-22 17:44:04 +00:00
updateOrInsert ( recipient . getAddress ( ) , contentValues ) ;
recipient . resolve ( ) . setProfileAvatar ( profileAvatar ) ;
2017-08-15 01:11:13 +00:00
}
2017-11-26 18:45:39 +00:00
public void setProfileSharing ( @NonNull Recipient recipient , @SuppressWarnings ( " SameParameterValue " ) boolean enabled ) {
2017-08-17 04:49:41 +00:00
ContentValues contentValues = new ContentValues ( 1 ) ;
contentValues . put ( PROFILE_SHARING , enabled ? 1 : 0 ) ;
2017-08-22 17:44:04 +00:00
updateOrInsert ( recipient . getAddress ( ) , contentValues ) ;
recipient . setProfileSharing ( enabled ) ;
2017-08-17 04:49:41 +00:00
}
2018-08-16 16:47:43 +00:00
public void setNotificationChannel ( @NonNull Recipient recipient , @Nullable String notificationChannel ) {
ContentValues contentValues = new ContentValues ( 1 ) ;
contentValues . put ( NOTIFICATION_CHANNEL , notificationChannel ) ;
updateOrInsert ( recipient . getAddress ( ) , contentValues ) ;
recipient . setNotificationChannel ( notificationChannel ) ;
}
2017-11-26 18:45:39 +00:00
public Set < Address > getAllAddresses ( ) {
2017-08-07 21:24:53 +00:00
SQLiteDatabase db = databaseHelper . getReadableDatabase ( ) ;
2017-11-26 18:45:39 +00:00
Set < Address > results = new HashSet < > ( ) ;
2017-08-07 21:24:53 +00:00
try ( Cursor cursor = db . query ( TABLE_NAME , new String [ ] { ADDRESS } , null , null , null , null , null ) ) {
while ( cursor ! = null & & cursor . moveToNext ( ) ) {
2017-11-26 18:45:39 +00:00
results . add ( Address . fromExternal ( context , cursor . getString ( 0 ) ) ) ;
2017-08-07 21:24:53 +00:00
}
}
return results ;
}
2017-08-22 17:44:04 +00:00
public void setRegistered ( @NonNull Recipient recipient , RegisteredState registeredState ) {
ContentValues contentValues = new ContentValues ( 1 ) ;
contentValues . put ( REGISTERED , registeredState . getId ( ) ) ;
updateOrInsert ( recipient . getAddress ( ) , contentValues ) ;
recipient . setRegistered ( registeredState ) ;
}
2017-08-07 21:24:53 +00:00
2017-11-26 18:45:39 +00:00
public void setRegistered ( @NonNull List < Address > activeAddresses ,
@NonNull List < Address > inactiveAddresses )
2017-08-22 17:44:04 +00:00
{
2017-11-26 18:45:39 +00:00
for ( Address activeAddress : activeAddresses ) {
2017-08-16 02:23:42 +00:00
ContentValues contentValues = new ContentValues ( 1 ) ;
2017-08-22 17:44:04 +00:00
contentValues . put ( REGISTERED , RegisteredState . REGISTERED . getId ( ) ) ;
2017-08-07 21:24:53 +00:00
2017-11-26 18:45:39 +00:00
updateOrInsert ( activeAddress , contentValues ) ;
Recipient . applyCached ( activeAddress , recipient - > recipient . setRegistered ( RegisteredState . REGISTERED ) ) ;
2017-08-07 21:24:53 +00:00
}
2017-11-26 18:45:39 +00:00
for ( Address inactiveAddress : inactiveAddresses ) {
2017-08-16 02:23:42 +00:00
ContentValues contentValues = new ContentValues ( 1 ) ;
2017-08-22 17:44:04 +00:00
contentValues . put ( REGISTERED , RegisteredState . NOT_REGISTERED . getId ( ) ) ;
2017-08-07 21:24:53 +00:00
2017-11-26 18:45:39 +00:00
updateOrInsert ( inactiveAddress , contentValues ) ;
Recipient . applyCached ( inactiveAddress , recipient - > recipient . setRegistered ( RegisteredState . NOT_REGISTERED ) ) ;
2017-08-07 21:24:53 +00:00
}
}
public List < Address > getRegistered ( ) {
SQLiteDatabase db = databaseHelper . getReadableDatabase ( ) ;
List < Address > results = new LinkedList < > ( ) ;
try ( Cursor cursor = db . query ( TABLE_NAME , new String [ ] { ADDRESS } , REGISTERED + " = ? " , new String [ ] { " 1 " } , null , null , null ) ) {
while ( cursor ! = null & & cursor . moveToNext ( ) ) {
results . add ( Address . fromSerialized ( cursor . getString ( 0 ) ) ) ;
}
}
return results ;
}
2017-12-07 19:53:17 +00:00
public List < Address > getSystemContacts ( ) {
SQLiteDatabase db = databaseHelper . getReadableDatabase ( ) ;
List < Address > results = new LinkedList < > ( ) ;
try ( Cursor cursor = db . query ( TABLE_NAME , new String [ ] { ADDRESS } , SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \" \" " , null , null , null , null ) ) {
while ( cursor ! = null & & cursor . moveToNext ( ) ) {
results . add ( Address . fromSerialized ( cursor . getString ( 0 ) ) ) ;
}
}
return results ;
}
2018-09-25 22:41:42 +00:00
public void updateSystemContactColors ( @NonNull ColorUpdater updater ) {
SQLiteDatabase db = databaseHelper . getReadableDatabase ( ) ;
Map < Address , MaterialColor > updates = new HashMap < > ( ) ;
db . beginTransaction ( ) ;
try ( Cursor cursor = db . query ( TABLE_NAME , new String [ ] { ADDRESS , COLOR , SYSTEM_DISPLAY_NAME } , SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \" \" " , null , null , null , null ) ) {
while ( cursor ! = null & & cursor . moveToNext ( ) ) {
Address address = Address . fromSerialized ( cursor . getString ( cursor . getColumnIndexOrThrow ( ADDRESS ) ) ) ;
MaterialColor newColor = updater . update ( cursor . getString ( cursor . getColumnIndexOrThrow ( SYSTEM_DISPLAY_NAME ) ) ,
cursor . getString ( cursor . getColumnIndexOrThrow ( COLOR ) ) ) ;
ContentValues contentValues = new ContentValues ( 1 ) ;
contentValues . put ( COLOR , newColor . serialize ( ) ) ;
db . update ( TABLE_NAME , contentValues , ADDRESS + " = ? " , new String [ ] { address . serialize ( ) } ) ;
updates . put ( address , newColor ) ;
}
} finally {
db . setTransactionSuccessful ( ) ;
db . endTransaction ( ) ;
Stream . of ( updates . entrySet ( ) ) . forEach ( entry - > {
Recipient . applyCached ( entry . getKey ( ) , recipient - > {
recipient . setColor ( entry . getValue ( ) ) ;
} ) ;
} ) ;
}
}
2017-11-25 17:50:36 +00:00
// XXX This shouldn't be here, and is just a temporary workaround
public RegisteredState isRegistered ( @NonNull Address address ) {
SQLiteDatabase db = databaseHelper . getReadableDatabase ( ) ;
try ( Cursor cursor = db . query ( TABLE_NAME , new String [ ] { REGISTERED } , ADDRESS + " = ? " , new String [ ] { address . serialize ( ) } , null , null , null ) ) {
if ( cursor ! = null & & cursor . moveToFirst ( ) ) return RegisteredState . fromId ( cursor . getInt ( 0 ) ) ;
else return RegisteredState . UNKNOWN ;
}
}
2017-08-07 22:31:12 +00:00
private void updateOrInsert ( Address address , ContentValues contentValues ) {
2015-06-09 14:37:20 +00:00
SQLiteDatabase database = databaseHelper . getWritableDatabase ( ) ;
database . beginTransaction ( ) ;
2017-08-01 15:56:00 +00:00
int updated = database . update ( TABLE_NAME , contentValues , ADDRESS + " = ? " ,
2017-08-07 22:31:12 +00:00
new String [ ] { address . serialize ( ) } ) ;
2015-06-09 14:37:20 +00:00
if ( updated < 1 ) {
2017-08-07 22:31:12 +00:00
contentValues . put ( ADDRESS , address . serialize ( ) ) ;
2015-06-09 14:37:20 +00:00
database . insert ( TABLE_NAME , null , contentValues ) ;
}
2017-11-26 18:45:39 +00:00
database . setTransactionSuccessful ( ) ;
database . endTransaction ( ) ;
2017-08-16 02:23:42 +00:00
}
2015-06-09 14:37:20 +00:00
2017-08-16 02:23:42 +00:00
public class BulkOperationsHandle {
2015-06-11 20:00:50 +00:00
2017-08-16 02:23:42 +00:00
private final SQLiteDatabase database ;
2017-11-26 18:45:39 +00:00
private final Map < Address , PendingContactInfo > pendingContactInfoMap = new HashMap < > ( ) ;
2017-08-22 17:44:04 +00:00
BulkOperationsHandle ( SQLiteDatabase database ) {
2017-08-16 02:23:42 +00:00
this . database = database ;
}
2017-11-26 18:45:39 +00:00
public void setSystemContactInfo ( @NonNull Address address , @Nullable String displayName , @Nullable String photoUri , @Nullable String systemPhoneLabel , @Nullable String systemContactUri ) {
2017-08-16 02:23:42 +00:00
ContentValues contentValues = new ContentValues ( 1 ) ;
contentValues . put ( SYSTEM_DISPLAY_NAME , displayName ) ;
2017-11-26 18:45:39 +00:00
contentValues . put ( SYSTEM_PHOTO_URI , photoUri ) ;
contentValues . put ( SYSTEM_PHONE_LABEL , systemPhoneLabel ) ;
contentValues . put ( SYSTEM_CONTACT_URI , systemContactUri ) ;
updateOrInsert ( address , contentValues ) ;
pendingContactInfoMap . put ( address , new PendingContactInfo ( displayName , photoUri , systemPhoneLabel , systemContactUri ) ) ;
2017-08-16 02:23:42 +00:00
}
public void finish ( ) {
database . setTransactionSuccessful ( ) ;
database . endTransaction ( ) ;
2017-08-22 17:44:04 +00:00
2017-11-26 18:45:39 +00:00
Stream . of ( pendingContactInfoMap . entrySet ( ) )
. forEach ( entry - > Recipient . applyCached ( entry . getKey ( ) , recipient - > {
recipient . setName ( entry . getValue ( ) . displayName ) ;
recipient . setSystemContactPhoto ( Util . uri ( entry . getValue ( ) . photoUri ) ) ;
recipient . setCustomLabel ( entry . getValue ( ) . phoneLabel ) ;
recipient . setContactUri ( Util . uri ( entry . getValue ( ) . contactUri ) ) ;
} ) ) ;
2017-08-16 02:23:42 +00:00
}
2015-06-09 14:37:20 +00:00
}
2018-09-25 22:41:42 +00:00
public interface ColorUpdater {
MaterialColor update ( @NonNull String name , @Nullable String color ) ;
}
2017-08-22 01:47:37 +00:00
public static class RecipientSettings {
2018-05-22 09:13:10 +00:00
private final boolean blocked ;
private final long muteUntil ;
private final VibrateState messageVibrateState ;
private final VibrateState callVibrateState ;
private final Uri messageRingtone ;
private final Uri callRingtone ;
private final MaterialColor color ;
private final boolean seenInviteReminder ;
private final int defaultSubscriptionId ;
private final int expireMessages ;
private final RegisteredState registered ;
private final byte [ ] profileKey ;
private final String systemDisplayName ;
private final String systemContactPhoto ;
private final String systemPhoneLabel ;
private final String systemContactUri ;
private final String signalProfileName ;
private final String signalProfileAvatar ;
private final boolean profileSharing ;
private final String notificationChannel ;
private final UnidentifiedAccessMode unidentifiedAccessMode ;
2019-04-12 19:22:38 +00:00
private final boolean forceSmsSelection ;
2015-06-30 16:16:05 +00:00
2017-08-22 01:47:37 +00:00
RecipientSettings ( boolean blocked , long muteUntil ,
2018-02-16 19:10:35 +00:00
@NonNull VibrateState messageVibrateState ,
@NonNull VibrateState callVibrateState ,
@Nullable Uri messageRingtone ,
@Nullable Uri callRingtone ,
2017-08-22 01:47:37 +00:00
@Nullable MaterialColor color ,
boolean seenInviteReminder ,
int defaultSubscriptionId ,
int expireMessages ,
2017-08-22 17:44:04 +00:00
@NonNull RegisteredState registered ,
2017-08-22 01:47:37 +00:00
@Nullable byte [ ] profileKey ,
@Nullable String systemDisplayName ,
2017-11-26 18:45:39 +00:00
@Nullable String systemContactPhoto ,
@Nullable String systemPhoneLabel ,
@Nullable String systemContactUri ,
2017-08-22 01:47:37 +00:00
@Nullable String signalProfileName ,
@Nullable String signalProfileAvatar ,
2018-08-16 16:47:43 +00:00
boolean profileSharing ,
2018-05-22 09:13:10 +00:00
@Nullable String notificationChannel ,
2019-04-12 19:22:38 +00:00
@NonNull UnidentifiedAccessMode unidentifiedAccessMode ,
boolean forceSmsSelection )
2015-06-23 22:10:50 +00:00
{
2018-05-22 09:13:10 +00:00
this . blocked = blocked ;
this . muteUntil = muteUntil ;
this . messageVibrateState = messageVibrateState ;
this . callVibrateState = callVibrateState ;
this . messageRingtone = messageRingtone ;
this . callRingtone = callRingtone ;
this . color = color ;
this . seenInviteReminder = seenInviteReminder ;
this . defaultSubscriptionId = defaultSubscriptionId ;
this . expireMessages = expireMessages ;
this . registered = registered ;
this . profileKey = profileKey ;
this . systemDisplayName = systemDisplayName ;
this . systemContactPhoto = systemContactPhoto ;
this . systemPhoneLabel = systemPhoneLabel ;
this . systemContactUri = systemContactUri ;
this . signalProfileName = signalProfileName ;
this . signalProfileAvatar = signalProfileAvatar ;
this . profileSharing = profileSharing ;
this . notificationChannel = notificationChannel ;
this . unidentifiedAccessMode = unidentifiedAccessMode ;
2019-04-12 19:22:38 +00:00
this . forceSmsSelection = forceSmsSelection ;
2015-06-23 22:10:50 +00:00
}
2015-06-30 16:16:05 +00:00
public @Nullable MaterialColor getColor ( ) {
2015-06-23 22:10:50 +00:00
return color ;
2015-06-09 14:37:20 +00:00
}
public boolean isBlocked ( ) {
return blocked ;
}
public long getMuteUntil ( ) {
return muteUntil ;
}
2018-02-16 19:10:35 +00:00
public @NonNull VibrateState getMessageVibrateState ( ) {
return messageVibrateState ;
}
public @NonNull VibrateState getCallVibrateState ( ) {
return callVibrateState ;
}
public @Nullable Uri getMessageRingtone ( ) {
return messageRingtone ;
2015-06-09 14:37:20 +00:00
}
2018-02-16 19:10:35 +00:00
public @Nullable Uri getCallRingtone ( ) {
return callRingtone ;
2015-06-09 14:37:20 +00:00
}
2015-10-14 04:44:01 +00:00
public boolean hasSeenInviteReminder ( ) {
return seenInviteReminder ;
}
2016-02-06 00:10:33 +00:00
public Optional < Integer > getDefaultSubscriptionId ( ) {
2017-10-16 20:11:42 +00:00
return defaultSubscriptionId ! = - 1 ? Optional . of ( defaultSubscriptionId ) : Optional . absent ( ) ;
2016-02-06 00:10:33 +00:00
}
2016-08-16 03:23:56 +00:00
public int getExpireMessages ( ) {
return expireMessages ;
}
2017-08-07 21:24:53 +00:00
2017-08-22 17:44:04 +00:00
public RegisteredState getRegistered ( ) {
2017-08-07 21:24:53 +00:00
return registered ;
}
2017-08-07 22:31:12 +00:00
2018-05-22 09:13:10 +00:00
public @Nullable byte [ ] getProfileKey ( ) {
2017-08-15 01:11:13 +00:00
return profileKey ;
}
public @Nullable String getSystemDisplayName ( ) {
2017-08-07 22:31:12 +00:00
return systemDisplayName ;
}
2017-08-15 01:11:13 +00:00
2017-11-26 18:45:39 +00:00
public @Nullable String getSystemContactPhotoUri ( ) {
return systemContactPhoto ;
}
public @Nullable String getSystemPhoneLabel ( ) {
return systemPhoneLabel ;
}
public @Nullable String getSystemContactUri ( ) {
return systemContactUri ;
}
2017-08-15 01:11:13 +00:00
public @Nullable String getProfileName ( ) {
return signalProfileName ;
}
public @Nullable String getProfileAvatar ( ) {
return signalProfileAvatar ;
}
2017-08-17 04:49:41 +00:00
public boolean isProfileSharing ( ) {
return profileSharing ;
}
2018-08-16 16:47:43 +00:00
public @Nullable String getNotificationChannel ( ) {
return notificationChannel ;
}
2018-05-22 09:13:10 +00:00
public @NonNull UnidentifiedAccessMode getUnidentifiedAccessMode ( ) {
return unidentifiedAccessMode ;
}
2019-04-12 19:22:38 +00:00
public boolean isForceSmsSelection ( ) {
return forceSmsSelection ;
}
2015-06-09 14:37:20 +00:00
}
2016-08-26 23:53:23 +00:00
2018-08-16 16:47:43 +00:00
public static class RecipientReader implements Closeable {
2016-08-26 23:53:23 +00:00
private final Context context ;
2018-08-16 16:47:43 +00:00
private final Cursor cursor ;
2016-08-26 23:53:23 +00:00
2018-08-16 16:47:43 +00:00
RecipientReader ( Context context , Cursor cursor ) {
2016-08-26 23:53:23 +00:00
this . context = context ;
this . cursor = cursor ;
}
2017-08-01 15:56:00 +00:00
public @NonNull Recipient getCurrent ( ) {
String serialized = cursor . getString ( cursor . getColumnIndexOrThrow ( ADDRESS ) ) ;
2017-08-22 01:32:38 +00:00
return Recipient . from ( context , Address . fromSerialized ( serialized ) , false ) ;
2016-08-26 23:53:23 +00:00
}
2017-08-01 15:56:00 +00:00
public @Nullable Recipient getNext ( ) {
2018-09-25 22:41:42 +00:00
if ( cursor ! = null & & ! cursor . moveToNext ( ) ) {
2016-08-26 23:53:23 +00:00
return null ;
}
return getCurrent ( ) ;
}
2018-08-16 16:47:43 +00:00
public void close ( ) {
cursor . close ( ) ;
}
2016-08-26 23:53:23 +00:00
}
2017-11-26 18:45:39 +00:00
private static class PendingContactInfo {
private final String displayName ;
private final String photoUri ;
private final String phoneLabel ;
private final String contactUri ;
private PendingContactInfo ( String displayName , String photoUri , String phoneLabel , String contactUri ) {
this . displayName = displayName ;
this . photoUri = photoUri ;
this . phoneLabel = phoneLabel ;
this . contactUri = contactUri ;
}
}
2015-06-09 14:37:20 +00:00
}