From 8d82033855d9bd8858a6714384e3f7abe10f844b Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Tue, 27 Oct 2015 12:18:02 -0700 Subject: [PATCH] Notify when contacts join Signal Closes #4314 // FREEBIE --- .../ic_favorite_grey600_24dp.png | Bin 0 -> 483 bytes .../ic_favorite_grey600_24dp.png | Bin 0 -> 344 bytes .../ic_favorite_grey600_24dp.png | Bin 0 -> 575 bytes .../ic_favorite_grey600_24dp.png | Bin 0 -> 834 bytes .../ic_favorite_grey600_24dp.png | Bin 0 -> 1043 bytes res/values/strings.xml | 2 + res/xml/syncadapter.xml | 2 +- .../securesms/ContactSelectionActivity.java | 9 ++- .../securesms/ConversationAdapter.java | 14 +++-- .../securesms/ConversationUpdateItem.java | 9 ++- .../securesms/contacts/ContactsDatabase.java | 55 +++++++++++++---- .../contacts/ContactsSyncAdapter.java | 3 +- .../securesms/database/MmsSmsColumns.java | 5 ++ .../securesms/database/SmsDatabase.java | 4 +- .../database/model/DisplayRecord.java | 4 ++ .../database/model/MessageRecord.java | 2 + .../database/model/ThreadRecord.java | 2 + .../securesms/jobs/DirectoryRefreshJob.java | 3 +- .../service/DirectoryRefreshListener.java | 4 +- .../securesms/sms/IncomingJoinedMessage.java | 22 +++++++ .../securesms/sms/IncomingTextMessage.java | 4 ++ .../securesms/util/DirectoryHelper.java | 58 +++++++++++++----- 22 files changed, 163 insertions(+), 39 deletions(-) create mode 100644 res/drawable-hdpi/ic_favorite_grey600_24dp.png create mode 100644 res/drawable-mdpi/ic_favorite_grey600_24dp.png create mode 100644 res/drawable-xhdpi/ic_favorite_grey600_24dp.png create mode 100644 res/drawable-xxhdpi/ic_favorite_grey600_24dp.png create mode 100644 res/drawable-xxxhdpi/ic_favorite_grey600_24dp.png create mode 100644 src/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java diff --git a/res/drawable-hdpi/ic_favorite_grey600_24dp.png b/res/drawable-hdpi/ic_favorite_grey600_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..2bdad7d3f145aa089789dc4d791daa5eed260408 GIT binary patch literal 483 zcmV<90UZ8`P)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00002VoOIv0RM-N%)bBt010qN zS#tmY3ljhU3ljkVnw%H_00CP`L_t(Y$K}?|D@0)w$MJhFGb^@ST+`Ev!(svYM^3oKiB`u$qh{H8mx9nVZc}WIQ&Ed1jvb_FTK^EYIn)JLh){ zjQ=U4ouC8@WLRef8&m37q*-Hw4D%$sp+OG#K-BA&Wh@K~I#{LD(0ON{0XJw4r(yKB zj_D!EsiIS1Rt4F-DuxAu0!5cjmC0r(#+A#kcU{scLrYqWK(Hu7N7~S#3_WN=_cBz| zhCXHJO&hApP*EEy$k3iPv@1h1+R(HNndG&YlhzfP;wv;$Bc)o}tdi&#?g<|r= z-0o37<-kmdq}PQUqY{XDV#M#%rg-sXoWDEdb^Oyll!F~KMQom3nMWqV#L+l+N~X+c z*jyXtriHm?NGBlsxR980dUcbwn_QjA(G?}K<75#Cvc#iizr_yQKY1JRi7=affOh+j Zz5$6Lf)5LHQ^f!P002ovPDHLkV1kPj$MXOH literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_favorite_grey600_24dp.png b/res/drawable-mdpi/ic_favorite_grey600_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..63f13a33b4002215b26ca0cfd30d8f89b71f14fb GIT binary patch literal 344 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1g=CK)Uj~LMH3o);76yi2K%s^g z3=E|P3=FRl7#OT(FffQ0%-I!a1C(G&@^*J&_}|`tWO=~G=WkKx z!PCVt#Nu?aMydj@(S^X~u$PN2O<(%oD}IWe-c{yqi79%mT{=2f9j*%m{o4G+Ugn?X zSS(dh@}3rIpiRrKVGk1&mIIy%(9-IA{EDpVGE}$*m7E3ll{H zrKTyHSO_gWC}$~WCJ?eu%=FQb#>58ypO=FSzbr|N`X##jlm1e*8$$D?`+oe3d)1@8 zwTelYVOf!R&B@yl_bYf ikCTg<94s7K85nMfG4g-Wd3qPJ%=P)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00002VoOIv0RM-N%)bBt010qN zS#tmY3ljhU3ljkVnw%H_00Fm2L_t(o!|j*7OG053#;^26Lm3gJnUxR(MSnz8W6%=R z&=gG#682kypsBGy2%Mz7A%cjMq-bmif`*2|$h6I*NkzR)`So7!z25seC1*Y7`N4ZH z=RA*s(%Mqmfd2|`6Cgs8JVjDO*u^9Cc!4Ni6v>n1njJ>W0h|QMq1IJ_Fs^dXD5n&J zo-D`o8v*8s3xj`^W*G&A6*3Kb-k8+`%<A^hE+Htm{dSi1nf%%?1_LGseoy%RdFv({#JVzElC5+H(J-1QsmDY--AhhY|Cfx>Rq%A z+kkbWn;fzwe`0*}yScM0e?ylU+cLX9$5jT zldvEkqC;lFad228e}F@Ngz}di_I9f>+8TLsthO=)F0#;$v28$I{Q}S_1JczfnQs69 N002ovPDHLkV1g9>>@xrW literal 0 HcmV?d00001 diff --git a/res/drawable-xxhdpi/ic_favorite_grey600_24dp.png b/res/drawable-xxhdpi/ic_favorite_grey600_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..11f108dad156b8acd2cac550efe453c7b0c02f25 GIT binary patch literal 834 zcmV-I1HJr-P)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00002VoOIv0RM-N%)bBt010qN zS#tmY3ljhU3ljkVnw%H_00O#6L_t(&-tF2wXcJ)^$ML%+)})HfAf#?0I*Oo!IEcZB zgD4_)6hy?g#qEpGE|pf=q2O8v6$A&-67W4~d;tf?S_i>F4UK}YgEpp#v^f`B%*%6k z&)swPbU2>*|L`G~Joo(nzZe)RYZ(zJ0!5$*wBkWAGK^55M1=-5W+^htUgEY@tJudF zPbgEPL4^_phS@`xKTwK6K4NuTpSeW3V^trcd=XTY86f2iWOAAxf^C;pl_3&MRY``K zcc}W#NlaN#in|WmF0D6gU9Q^3TbETidSyU;%(&dx!s0gvF)%nxRif%u=Lg>T(|Iki zv4zC~M>xuYWYsk5+<;8i59|L`qwb+ z3yTYOpqTI&3~tRfpWe-&Oz0d7i``<-h!)UAF{q#g^jHikX#u?#gDP4;b7Ih<7SN&? zG^YjhQw%C=0hPp{q83nI47#iZG%f}m(gHdl2CbzQI%l39ySM$#&_OrsuOAt`uhw>u z$;(hVFIq3L?N`|Sp@On*x{iCHadPh3piPdwrfGOp zdiyk=0>0X>mz{p>;!j|lIy=40hmHpZI_7m?6-*a0a#cC!ig;M%X2uvNr<8Mt znBSpig1j=$LsqL83nzK1gfrDLkMuF8UZa3iY`%#39h~?0Y_szuRZsF0Jn+o9@8)62 zM+@ED_QbhGcTibJoa@q@Yfe9Of8k4)WbZa^e+;TfgG`AzBL*QPXEpwiW zV!3cr=>(Ji!ls1(-PDBJF(#Ohdqo7D+ne%da`exJ2o!-Lkb!~m7b5uxE2rfLmjD0& M07*qoM6N<$f-&=Ui2wiq literal 0 HcmV?d00001 diff --git a/res/drawable-xxxhdpi/ic_favorite_grey600_24dp.png b/res/drawable-xxxhdpi/ic_favorite_grey600_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..e8ee60e7e68b23c2b9e68740ce2dafc7f6f004a2 GIT binary patch literal 1043 zcmV+u1nm2XP)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00002VoOIv0RM-N%)bBt010qN zS#tmY3ljhU3ljkVnw%H_00WCjL_t(|+U=W7NEJ~O$KNx3eLxsl=Bf{kHbS(C#2^q7 zwMArDnn7kkWiDF%GBk`bkw9&cE!r2jX_fL!G2B!{fkYJK2S!vzqFGw$<2|QEA85Uq zx$}JohOrh0gb4XNLIFSk5C8-K0YCr{00aQ;0c220J5ffMB1VE4M(CxL zGBOP1S;ryT=wp-_62zEdm?&)=V69UC5i02+fua%#9#F+f-8`!}!GK1dI8iD{vlk$Z zTHdNIxi}_hV6|$VbQ+ly%JYUAQtblB=Y`Osf8ry@2>+KQOf{bj<#|q?b$}G=H6FKm zbd$LxPZsx0WQkKpiWPteohFw374O*jH%}fPOy{{n#4JFBsOe=D6uwbJh!FeuZYfW< z7J}Bx(`jj01%-JEDI#V$&u#S=Ngbfx^0EpFv&`C(2crP_raoQgBF-)&0BHs{PB&sO z`MHoBpjJBh3JRx10V0B@uG=w|?1GUDpi)Zt3JT?706o$Ibcq0DNVfkh-HX=f1C&ZD zUqNBNK0v$F0Im7}QK29P5yz&6pJ`hI_G zq3Qr7QUa8S1B7`Zo&0A>O7wV&ghhM@&?GB}Aqk98m z9`yq-m{!N+w-}Xg&b1h(#S!^ejmtM{y9{&1cKMBN`$p~E zk?OoKzMiv9eyvx|-IF$)&Izi#v>umRyd%wX&};8>u?0)tEPG`zfA8kADfv@sHZu3! z>)RPOEdRaOecvtneYU~w=4&>|Hb0QXps@U>te0^BBAo#t`F$Gxc8@1GR&ZBWekaRi z9zj{gElv41^xWv)&(MUqtSbManfs;}LqIi2t{4eU`Du!&>>u0C9Ho96!zx%*44Y<; zALqz+{63$@+3wdtxNL?QVvC%s called you Called %s Missed call from %s + %s is on Signal, say hey! @@ -447,6 +448,7 @@ Called you Missed call Media message + %s is on Signal, say hey! You do not have an identity key. diff --git a/res/xml/syncadapter.xml b/res/xml/syncadapter.xml index 67a058349f..7804e40217 100644 --- a/res/xml/syncadapter.xml +++ b/res/xml/syncadapter.xml @@ -3,6 +3,6 @@ android:contentAuthority="com.android.contacts" android:accountType="org.thoughtcrime.securesms" android:userVisible="true" - android:supportsUploading="false" + android:supportsUploading="true" android:allowParallelSyncs="false" android:isAlwaysSyncable="true"/> \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/ContactSelectionActivity.java b/src/org/thoughtcrime/securesms/ContactSelectionActivity.java index ff7f43d078..8517f6e275 100644 --- a/src/org/thoughtcrime/securesms/ContactSelectionActivity.java +++ b/src/org/thoughtcrime/securesms/ContactSelectionActivity.java @@ -61,6 +61,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB protected ContactSelectionListFragment contactsFragment; + private MasterSecret masterSecret; private Toolbar toolbar; private EditText searchText; private AnimatingToggle toggle; @@ -79,6 +80,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB @Override protected void onCreate(Bundle icicle, MasterSecret masterSecret) { setContentView(R.layout.contact_selection_activity); + this.masterSecret = masterSecret; initializeToolbar(); initializeResources(); @@ -216,16 +218,19 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB private static class RefreshDirectoryTask extends AsyncTask { private final WeakReference activity; + private final MasterSecret masterSecret; private RefreshDirectoryTask(ContactSelectionActivity activity) { - this.activity = new WeakReference<>(activity); + this.activity = new WeakReference<>(activity); + this.masterSecret = activity.masterSecret; } @Override protected Void doInBackground(Context... params) { + try { - DirectoryHelper.refreshDirectory(params[0]); + DirectoryHelper.refreshDirectory(params[0], masterSecret); } catch (IOException e) { Log.w(TAG, e); } diff --git a/src/org/thoughtcrime/securesms/ConversationAdapter.java b/src/org/thoughtcrime/securesms/ConversationAdapter.java index 9a2b599ed9..23d7fa5e34 100644 --- a/src/org/thoughtcrime/securesms/ConversationAdapter.java +++ b/src/org/thoughtcrime/securesms/ConversationAdapter.java @@ -158,13 +158,17 @@ public class ConversationAdapter @Override public int getItemViewType(@NonNull Cursor cursor) { - long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsColumns.ID)); - String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT)); + long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsColumns.ID)); + String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT)); MessageRecord messageRecord = getMessageRecord(id, cursor, type); - if (messageRecord.isGroupAction() || messageRecord.isCallLog()) return MESSAGE_TYPE_UPDATE; - else if (messageRecord.isOutgoing()) return MESSAGE_TYPE_OUTGOING; - else return MESSAGE_TYPE_INCOMING; + if (messageRecord.isGroupAction() || messageRecord.isCallLog() || messageRecord.isJoined()) { + return MESSAGE_TYPE_UPDATE; + } else if (messageRecord.isOutgoing()) { + return MESSAGE_TYPE_OUTGOING; + } else { + return MESSAGE_TYPE_INCOMING; + } } private MessageRecord getMessageRecord(long messageId, Cursor cursor, String type) { diff --git a/src/org/thoughtcrime/securesms/ConversationUpdateItem.java b/src/org/thoughtcrime/securesms/ConversationUpdateItem.java index c31b8b68a3..39fa1d82d3 100644 --- a/src/org/thoughtcrime/securesms/ConversationUpdateItem.java +++ b/src/org/thoughtcrime/securesms/ConversationUpdateItem.java @@ -70,7 +70,8 @@ public class ConversationUpdateItem extends LinearLayout if (messageRecord.isGroupAction()) setGroupRecord(messageRecord); else if (messageRecord.isCallLog()) setCallRecord(messageRecord); - else throw new AssertionError("Neither group no log."); + else if (messageRecord.isJoined()) setJoinedRecord(messageRecord); + else throw new AssertionError("Neither group nor log nor joined."); } private void setCallRecord(MessageRecord messageRecord) { @@ -99,6 +100,12 @@ public class ConversationUpdateItem extends LinearLayout date.setVisibility(View.GONE); } + private void setJoinedRecord(MessageRecord messageRecord) { + icon.setImageResource(R.drawable.ic_favorite_grey600_24dp); + body.setText(messageRecord.getDisplayBody()); + date.setVisibility(View.GONE); + } + @Override public void onModified(Recipients recipients) { onModified(recipients.getPrimaryRecipient()); diff --git a/src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java b/src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java index 24b59ef62f..b9cc8a17d8 100644 --- a/src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java +++ b/src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java @@ -32,14 +32,18 @@ import android.provider.ContactsContract.RawContacts; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; +import android.util.Log; import android.util.Pair; import org.thoughtcrime.securesms.R; import org.whispersystems.libaxolotl.util.guava.Optional; +import org.whispersystems.textsecure.api.util.InvalidNumberException; +import org.whispersystems.textsecure.api.util.PhoneNumberFormatter; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -70,11 +74,14 @@ public class ContactsDatabase { this.context = context; } - public synchronized boolean setRegisteredUsers(Account account, List e164numbers) + public synchronized @NonNull List setRegisteredUsers(@NonNull Account account, + @NonNull String localNumber, + @NonNull List e164numbers) throws RemoteException, OperationApplicationException { Map currentContacts = new HashMap<>(); Set registeredNumbers = new HashSet<>(e164numbers); + List addedNumbers = new LinkedList<>(); ArrayList operations = new ArrayList<>(); Uri currentContactsUri = RawContacts.CONTENT_URI.buildUpon() .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name) @@ -86,7 +93,16 @@ public class ContactsDatabase { 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)); + String currentNumber; + + try { + currentNumber = PhoneNumberFormatter.formatNumber(cursor.getString(1), localNumber); + } catch (InvalidNumberException e) { + Log.w(TAG, e); + currentNumber = cursor.getString(1); + } + + currentContacts.put(currentNumber, cursor.getLong(0)); } } finally { if (cursor != null) @@ -95,10 +111,11 @@ public class ContactsDatabase { for (String number : e164numbers) { if (!currentContacts.containsKey(number)) { - Optional> systemContactInfo = getSystemContactInfo(number); + Optional systemContactInfo = getSystemContactInfo(number); if (systemContactInfo.isPresent()) { - addTextSecureRawContact(operations, account, systemContactInfo.get().first, systemContactInfo.get().second); + addedNumbers.add(number); + addTextSecureRawContact(operations, account, systemContactInfo.get().number, systemContactInfo.get().id); } } } @@ -111,14 +128,15 @@ public class ContactsDatabase { if (!operations.isEmpty()) { context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations); - return true; - } else { - return false; } + + return addedNumbers; } private void addTextSecureRawContact(List operations, - Account account, String e164number, long aggregateId) + Account account, + String e164number, + long aggregateId) { int index = operations.size(); Uri dataUri = ContactsContract.Data.CONTENT_URI.buildUpon() @@ -254,10 +272,11 @@ public class ContactsDatabase { return newNumberCursor; } - private Optional> getSystemContactInfo(String e164number) { + private Optional getSystemContactInfo(String e164number) { Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(e164number)); String[] projection = {ContactsContract.PhoneLookup.NUMBER, - ContactsContract.PhoneLookup._ID}; + ContactsContract.PhoneLookup._ID, + ContactsContract.PhoneLookup.DISPLAY_NAME}; Cursor numberCursor = null; Cursor idCursor = null; @@ -272,7 +291,9 @@ public class ContactsDatabase { null); if (idCursor != null && idCursor.moveToNext()) { - return Optional.of(new Pair<>(numberCursor.getString(0), idCursor.getLong(0))); + return Optional.of(new SystemContactInfo(numberCursor.getString(2), + numberCursor.getString(0), + idCursor.getLong(0))); } } } finally { @@ -381,4 +402,16 @@ public class ContactsDatabase { return null; } } + + private static class SystemContactInfo { + private final String name; + private final String number; + private final long id; + + private SystemContactInfo(String name, String number, long id) { + this.name = name; + this.number = number; + this.id = id; + } + } } diff --git a/src/org/thoughtcrime/securesms/contacts/ContactsSyncAdapter.java b/src/org/thoughtcrime/securesms/contacts/ContactsSyncAdapter.java index 996c8fc86d..383248b85c 100644 --- a/src/org/thoughtcrime/securesms/contacts/ContactsSyncAdapter.java +++ b/src/org/thoughtcrime/securesms/contacts/ContactsSyncAdapter.java @@ -8,6 +8,7 @@ import android.content.SyncResult; import android.os.Bundle; import android.util.Log; +import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.util.DirectoryHelper; import java.io.IOException; @@ -25,7 +26,7 @@ public class ContactsSyncAdapter extends AbstractThreadedSyncAdapter { ContentProviderClient provider, SyncResult syncResult) { try { - DirectoryHelper.refreshDirectory(getContext()); + DirectoryHelper.refreshDirectory(getContext(), KeyCachingService.getMasterSecret(getContext())); } catch (IOException e) { Log.w(TAG, e); } diff --git a/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java b/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java index fcb24c6660..b089b8019b 100644 --- a/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java +++ b/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java @@ -22,6 +22,7 @@ public interface MmsSmsColumns { protected static final long INCOMING_CALL_TYPE = 1; protected static final long OUTGOING_CALL_TYPE = 2; protected static final long MISSED_CALL_TYPE = 3; + protected static final long JOINED_TYPE = 4; protected static final long BASE_INBOX_TYPE = 20; protected static final long BASE_OUTBOX_TYPE = 21; @@ -114,6 +115,10 @@ public interface MmsSmsColumns { return (type & BASE_TYPE_MASK) == BASE_INBOX_TYPE; } + public static boolean isJoinedType(long type) { + return (type & BASE_TYPE_MASK) == JOINED_TYPE; + } + public static boolean isSecureType(long type) { return (type & SECURE_MESSAGE_BIT) != 0; } diff --git a/src/org/thoughtcrime/securesms/database/SmsDatabase.java b/src/org/thoughtcrime/securesms/database/SmsDatabase.java index fcdd44b1d8..0f17ebab66 100644 --- a/src/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -384,7 +384,9 @@ public class SmsDatabase extends MessagingDatabase { } protected Pair insertMessageInbox(IncomingTextMessage message, long type) { - if (message.isPreKeyBundle()) { + if (message.isJoined()) { + type = (type & (Types.TOTAL_MASK - Types.BASE_TYPE_MASK)) | Types.JOINED_TYPE; + } else if (message.isPreKeyBundle()) { type |= Types.KEY_EXCHANGE_BIT | Types.KEY_EXCHANGE_BUNDLE_BIT; } else if (message.isSecureMessage()) { type |= Types.SECURE_MESSAGE_BIT; diff --git a/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java b/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java index 67c13ff8a4..c473438828 100644 --- a/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java +++ b/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java @@ -99,6 +99,10 @@ public abstract class DisplayRecord { return SmsDatabase.Types.isCallLog(type); } + public boolean isJoined() { + return SmsDatabase.Types.isJoinedType(type); + } + public boolean isIncomingCall() { return SmsDatabase.Types.isIncomingCall(type); } diff --git a/src/org/thoughtcrime/securesms/database/model/MessageRecord.java b/src/org/thoughtcrime/securesms/database/model/MessageRecord.java index 69b7d1f7a3..5093d66161 100644 --- a/src/org/thoughtcrime/securesms/database/model/MessageRecord.java +++ b/src/org/thoughtcrime/securesms/database/model/MessageRecord.java @@ -121,6 +121,8 @@ public abstract class MessageRecord extends DisplayRecord { return emphasisAdded(context.getString(R.string.MessageRecord_called_s, getIndividualRecipient().toShortString())); } else if (isMissedCall()) { return emphasisAdded(context.getString(R.string.MessageRecord_missed_call_from, getIndividualRecipient().toShortString())); + } else if (isJoined()) { + return emphasisAdded(context.getString(R.string.MessageRecord_s_is_on_signal_say_hey, getIndividualRecipient().toShortString())); } else if (getBody().getBody().length() > MAX_DISPLAY_LENGTH) { return new SpannableString(getBody().getBody().substring(0, MAX_DISPLAY_LENGTH)); } diff --git a/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java b/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java index 96223e04df..539167646b 100644 --- a/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java +++ b/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java @@ -90,6 +90,8 @@ public class ThreadRecord extends DisplayRecord { return emphasisAdded(context.getString(org.thoughtcrime.securesms.R.string.ThreadRecord_called_you)); } else if (SmsDatabase.Types.isMissedCall(type)) { return emphasisAdded(context.getString(org.thoughtcrime.securesms.R.string.ThreadRecord_missed_call)); + } else if (SmsDatabase.Types.isJoinedType(type)) { + return emphasisAdded(context.getString(R.string.ThreadRecord_s_is_on_signal_say_hey, getRecipients().getPrimaryRecipient().toShortString())); } else { if (TextUtils.isEmpty(getBody().getBody())) { return new SpannableString(emphasisAdded(context.getString(R.string.ThreadRecord_media_message))); diff --git a/src/org/thoughtcrime/securesms/jobs/DirectoryRefreshJob.java b/src/org/thoughtcrime/securesms/jobs/DirectoryRefreshJob.java index bb80d3361f..dbadaf0a9c 100644 --- a/src/org/thoughtcrime/securesms/jobs/DirectoryRefreshJob.java +++ b/src/org/thoughtcrime/securesms/jobs/DirectoryRefreshJob.java @@ -4,6 +4,7 @@ import android.content.Context; import android.os.PowerManager; import org.thoughtcrime.securesms.crypto.SecurityEvent; +import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.util.DirectoryHelper; import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.requirements.NetworkRequirement; @@ -30,7 +31,7 @@ public class DirectoryRefreshJob extends ContextJob { try { wakeLock.acquire(); - DirectoryHelper.refreshDirectory(context); + DirectoryHelper.refreshDirectory(context, KeyCachingService.getMasterSecret(context)); SecurityEvent.broadcastSecurityUpdateEvent(context); } finally { if (wakeLock.isHeld()) wakeLock.release(); diff --git a/src/org/thoughtcrime/securesms/service/DirectoryRefreshListener.java b/src/org/thoughtcrime/securesms/service/DirectoryRefreshListener.java index 03e633a58f..9e70ce8cd9 100644 --- a/src/org/thoughtcrime/securesms/service/DirectoryRefreshListener.java +++ b/src/org/thoughtcrime/securesms/service/DirectoryRefreshListener.java @@ -14,6 +14,8 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences; public class DirectoryRefreshListener extends BroadcastReceiver { + private static final String TAG = DirectoryRefreshListener.class.getSimpleName(); + private static final String REFRESH_EVENT = "org.whispersystems.whisperpush.DIRECTORY_REFRESH"; private static final String BOOT_EVENT = "android.intent.action.BOOT_COMPLETED"; @@ -51,7 +53,7 @@ public class DirectoryRefreshListener extends BroadcastReceiver { time = System.currentTimeMillis() + INTERVAL; } - Log.w("DirectoryRefreshListener", "Scheduling for: " + time); + Log.w(TAG, "Scheduling for: " + time); alarmManager.cancel(pendingIntent); alarmManager.set(AlarmManager.RTC_WAKEUP, time, pendingIntent); diff --git a/src/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java b/src/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java new file mode 100644 index 0000000000..993f80ffdb --- /dev/null +++ b/src/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java @@ -0,0 +1,22 @@ +package org.thoughtcrime.securesms.sms; + +import org.whispersystems.libaxolotl.util.guava.Optional; +import org.whispersystems.textsecure.api.messages.TextSecureGroup; + +public class IncomingJoinedMessage extends IncomingTextMessage { + + public IncomingJoinedMessage(String sender) { + super(sender, 1, System.currentTimeMillis(), null, Optional.absent()); + } + + @Override + public boolean isJoined() { + return true; + } + + @Override + public boolean isSecureMessage() { + return true; + } + +} diff --git a/src/org/thoughtcrime/securesms/sms/IncomingTextMessage.java b/src/org/thoughtcrime/securesms/sms/IncomingTextMessage.java index 7c399fd600..5339f6d68f 100644 --- a/src/org/thoughtcrime/securesms/sms/IncomingTextMessage.java +++ b/src/org/thoughtcrime/securesms/sms/IncomingTextMessage.java @@ -188,6 +188,10 @@ public class IncomingTextMessage implements Parcelable { return false; } + public boolean isJoined() { + return false; + } + @Override public int describeContents() { return 0; diff --git a/src/org/thoughtcrime/securesms/util/DirectoryHelper.java b/src/org/thoughtcrime/securesms/util/DirectoryHelper.java index 496415d73a..4a0b97e559 100644 --- a/src/org/thoughtcrime/securesms/util/DirectoryHelper.java +++ b/src/org/thoughtcrime/securesms/util/DirectoryHelper.java @@ -7,17 +7,25 @@ import android.content.Context; import android.content.OperationApplicationException; import android.os.RemoteException; import android.provider.ContactsContract; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.util.Log; +import android.util.Pair; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.NotInDirectoryException; import org.thoughtcrime.securesms.database.TextSecureDirectory; import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob; import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; +import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory; import org.thoughtcrime.securesms.recipients.Recipients; +import org.thoughtcrime.securesms.sms.IncomingGroupMessage; +import org.thoughtcrime.securesms.sms.IncomingJoinedMessage; +import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.util.DirectoryHelper.UserCapabilities.Capability; import org.whispersystems.libaxolotl.util.guava.Optional; import org.whispersystems.textsecure.api.TextSecureAccountManager; @@ -59,13 +67,29 @@ public class DirectoryHelper { private static final String TAG = DirectoryHelper.class.getSimpleName(); - public static void refreshDirectory(final Context context) throws IOException { - refreshDirectory(context, - TextSecureCommunicationFactory.createManager(context), - TextSecurePreferences.getLocalNumber(context)); + public static void refreshDirectory(@NonNull Context context, @Nullable MasterSecret masterSecret) + throws IOException + { + List newUsers = refreshDirectory(context, + TextSecureCommunicationFactory.createManager(context), + TextSecurePreferences.getLocalNumber(context)); + + if (!newUsers.isEmpty() && TextSecurePreferences.isMultiDevice(context)) { + ApplicationContext.getInstance(context) + .getJobManager() + .add(new MultiDeviceContactUpdateJob(context)); + } + + for (String newUser : newUsers) { + IncomingJoinedMessage message = new IncomingJoinedMessage(newUser); + Pair smsAndThreadId = DatabaseFactory.getSmsDatabase(context).insertMessageInbox(message); + MessageNotifier.updateNotification(context, masterSecret, smsAndThreadId.second); + } } - public static void refreshDirectory(final Context context, final TextSecureAccountManager accountManager, final String localNumber) + public static @NonNull List refreshDirectory(@NonNull Context context, + @NonNull TextSecureAccountManager accountManager, + @NonNull String localNumber) throws IOException { TextSecureDirectory directory = TextSecureDirectory.getInstance(context); @@ -89,19 +113,15 @@ public class DirectoryHelper { } try { - boolean modified = DatabaseFactory.getContactsDatabase(context) - .setRegisteredUsers(account.get(), e164numbers); - - if (modified && TextSecurePreferences.isMultiDevice(context)) { - ApplicationContext.getInstance(context) - .getJobManager() - .add(new MultiDeviceContactUpdateJob(context)); - } + return DatabaseFactory.getContactsDatabase(context) + .setRegisteredUsers(account.get(), localNumber, e164numbers); } catch (RemoteException | OperationApplicationException e) { Log.w(TAG, e); } } } + + return new LinkedList<>(); } public static UserCapabilities refreshDirectoryFor(Context context, Recipients recipients) @@ -173,8 +193,16 @@ public class DirectoryHelper { AccountManager accountManager = AccountManager.get(context); Account[] accounts = accountManager.getAccountsByType("org.thoughtcrime.securesms"); - if (accounts.length == 0) return createAccount(context); - else return Optional.of(accounts[0]); + Optional account; + + if (accounts.length == 0) account = createAccount(context); + else account = Optional.of(accounts[0]); + + if (account.isPresent() && !ContentResolver.getSyncAutomatically(account.get(), ContactsContract.AUTHORITY)) { + ContentResolver.setSyncAutomatically(account.get(), ContactsContract.AUTHORITY, true); + } + + return account; } private static Optional createAccount(Context context) {