From 1b2f52209d81598c0ce217d46e3c1eba7274b51b Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Fri, 18 Aug 2017 17:28:56 -0700 Subject: [PATCH] Display unknown sender footer for unknown senders // FREEBIE --- res/drawable-hdpi/ic_block_white_24dp.png | Bin 0 -> 478 bytes res/drawable-hdpi/ic_face_white_24dp.png | Bin 0 -> 513 bytes .../ic_person_add_white_24dp.png | Bin 0 -> 289 bytes res/drawable-mdpi/ic_block_white_24dp.png | Bin 0 -> 335 bytes res/drawable-mdpi/ic_face_white_24dp.png | Bin 0 -> 356 bytes .../ic_person_add_white_24dp.png | Bin 0 -> 204 bytes res/drawable-xhdpi/ic_block_white_24dp.png | Bin 0 -> 665 bytes res/drawable-xhdpi/ic_face_white_24dp.png | Bin 0 -> 694 bytes .../ic_person_add_white_24dp.png | Bin 0 -> 329 bytes res/drawable-xxhdpi/ic_block_white_24dp.png | Bin 0 -> 973 bytes res/drawable-xxhdpi/ic_face_white_24dp.png | Bin 0 -> 1022 bytes .../ic_person_add_white_24dp.png | Bin 0 -> 464 bytes res/drawable-xxxhdpi/ic_block_white_24dp.png | Bin 0 -> 1295 bytes res/drawable-xxxhdpi/ic_face_white_24dp.png | Bin 0 -> 1344 bytes .../ic_person_add_white_24dp.png | Bin 0 -> 610 bytes res/layout/unknown_sender_view.xml | 95 ++++++++++++++++ res/values/strings.xml | 15 ++- .../securesms/ConversationFragment.java | 33 ++++-- .../securesms/database/DatabaseFactory.java | 7 +- .../securesms/database/MmsDatabase.java | 1 + .../securesms/database/SmsDatabase.java | 2 + .../securesms/database/ThreadDatabase.java | 23 +++- .../database/loaders/ConversationLoader.java | 19 +++- .../securesms/profiles/UnknownSenderView.java | 106 ++++++++++++++++++ .../util/StickyHeaderDecoration.java | 2 +- 25 files changed, 279 insertions(+), 24 deletions(-) create mode 100644 res/drawable-hdpi/ic_block_white_24dp.png create mode 100644 res/drawable-hdpi/ic_face_white_24dp.png create mode 100644 res/drawable-hdpi/ic_person_add_white_24dp.png create mode 100644 res/drawable-mdpi/ic_block_white_24dp.png create mode 100644 res/drawable-mdpi/ic_face_white_24dp.png create mode 100644 res/drawable-mdpi/ic_person_add_white_24dp.png create mode 100644 res/drawable-xhdpi/ic_block_white_24dp.png create mode 100644 res/drawable-xhdpi/ic_face_white_24dp.png create mode 100644 res/drawable-xhdpi/ic_person_add_white_24dp.png create mode 100644 res/drawable-xxhdpi/ic_block_white_24dp.png create mode 100644 res/drawable-xxhdpi/ic_face_white_24dp.png create mode 100644 res/drawable-xxhdpi/ic_person_add_white_24dp.png create mode 100644 res/drawable-xxxhdpi/ic_block_white_24dp.png create mode 100644 res/drawable-xxxhdpi/ic_face_white_24dp.png create mode 100644 res/drawable-xxxhdpi/ic_person_add_white_24dp.png create mode 100644 res/layout/unknown_sender_view.xml create mode 100644 src/org/thoughtcrime/securesms/profiles/UnknownSenderView.java diff --git a/res/drawable-hdpi/ic_block_white_24dp.png b/res/drawable-hdpi/ic_block_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..2ccc89d2468d8279116536cb5ef67d0b10cf64b2 GIT binary patch literal 478 zcmV<40U`d0P)!uit#Z?!sMCfBkp29zgD9OYZNH)K& zhhE3KEcE;KKR9#YoC*J7H0|i>DNRk4dM<675#kMEU2yDOEMrHCRH&^~m!a0@Q0^MQ zgA;3(H02hpI`ssgE3boczX6ZF%7kiV+WLSy`Nz;T;KrN~ufi+fN*vM#d>1}!<`)23 zF=#;vZjzW6FjV{v9fL=63CvH00XS5JG`)jwNz50)J{X&+LOb9=mdFH|XRuX;y5J<4 z34$}wsX{%lCY=e^!F?4f!IE?)SO%jiGzCrROwa(67?ewAg4q~Uf<@^}uo4@s2Uevs z!A9(3UxHKVOmGe^s?augk|i^_KG>{6O=HkbWP&|VYE+@%2=o<6%)A$HAgDqLL-3lw z1h?SLd~C6`Ac^?_*cD&3x_}o&%}}?1PTlgq0}SlTA}_UP0JxRapQ9@P&(5q{)-Y?u zhI4%Y9ogpxvSp|)Z+25pcHz)iETseULL7>B#+HtIBNG!N_bzN|r2j;u_aBG;02*@F UkNbLuzyJUM07*qoM6N<$g139mdjJ3c literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_face_white_24dp.png b/res/drawable-hdpi/ic_face_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..dcb6ee96638614b0907a03b607ebb07b76fd7f61 GIT binary patch literal 513 zcmV+c0{;DpP)@@E@XLLEFBbfq|ZVTjos&>7?sv>rSNTTT?A6 zYU;^xtZ}ud?6o4am2;x94~nv*JX#b#&8c%awnvGYVjg=WPa`Fojz{dkb-~O0*i|o! z*2OGb^iTXYnpLq##ff2_3x9LR7o+H#h(!yAd1Fi%$~6ylq?wCFZ2-Jb5-z3WwhbR0 z894XDC!awp7VQJRyDt>XJ+K#xdSKO*P&hTvjYR{9CUeV@x{ztc!C6XF89MX}r|Owz zG9~JPnsAXNKr_?SK{qw*yF))Z#ZR?NGm{$j7I-QQ9c-!znPvlQ#-e%fRY@qAvJRMy zMHPL}EG$|EJ>#(`SOXn53TbXS1S^7Ar0N*FEua~*3yw^sF17~Xm9e}=3$Qrqs%-<_ zo65Y@E})gW{C5Bc9w;Zi)UrdsJEig&q{iJ!Kh4_)3T@Q%$cq|n`Vv6|A{D`e@FBO#`+%U$oRTV00000NkvXXu0mjf DXzb(~ literal 0 HcmV?d00001 diff --git a/res/drawable-hdpi/ic_person_add_white_24dp.png b/res/drawable-hdpi/ic_person_add_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..10ae5a70c4fce44cfebe24f4d7d05861ec6c4cbc GIT binary patch literal 289 zcmV++0p9+JP)4lybq4kV;{Vg=74!%q8si1@D1!Y~;t8~ngh-5DKnv{#kwsGok2b=v^KHmC z7J}w^`(N>zp`V8%r(i-$#Sso8&(OU!8p0Y@QXogebTw8F@j`?uG?DWLTvptY#UL0vRj^T=U8sC5Kp^y!Ix=4L?~RA?$$m!Ksc(e}V%44skE nX$-xPl8>R(21pO3?@vRYvpP(_{R(kj00000NkvXXu0mjfT?cqK literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_block_white_24dp.png b/res/drawable-mdpi/ic_block_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..ec1b33f0ea570391cdac55473fc61c0999d53505 GIT binary patch literal 335 zcmV-V0kHmwP)%!XrjG=rAbj z!Wrf{*dUd4Az^(OoR)Q=&Ov@sSr6x!<=_oXSr?u$%)uSTs-j+*;wA^RC0+}9ctR)T zAY4H~|L!j?`r~WCi>mjSr=9-u*t}aX@#suaJNIk~ejLs_b7`S}os0613(2jKx%HNr hp&LmQf)bw${sDammpVJ7L}&m2002ovPDHLkV1n|DlCb~) literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_face_white_24dp.png b/res/drawable-mdpi/ic_face_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..4daa9887711c1a7789985711e4378d737cf7c243 GIT binary patch literal 356 zcmV-q0h|7bP)?s+FB^Sfd~l+327rHuVPRL_znrV!uJr4w8AaRo#i!x-0gw< zgkfR48Mm?U`IL8#^UP&sUTr z2gd+fFZ@*IPy(8CyElTDmh#Ni37iAC7GfX!BF||86Tk^B8Vg*Ef=hJj1cD>1yb{WKZH>42&vz5aMRr)_(i)$Xzjq`|y|=H?IB?|w zd@O$Np@-)3bi*+vh^?Kv)9UqRZk?!waWLM02fqP=pSI&gNh=8e0000zfbBJi3%L}bpQ6wu z7sV{>vU}Oxv@~qHNm}^zmdorgllNBed5(eq4<=Fpz+C{p z`n*v7JO2WJZ)F1-{s4T4%yXia4ZH^Y<@%cWfAq}(ud;w^mVk$A=f4qj0SjNA4?G9F zmqg@)vI*dsG@#@Va4#w!1RcP>jWlo@@TW>-KB$@jZl!@fU>K8M?HfQh4U7Qo_`IM4 z*iHjuKr=osxCz)v15-dPJ};;Pj?=&ruo<5hB!ERWP>RnBE@lH$z~%V7pppHE#(*2~ zc|j}tPaXm8$L9qPvR~64U>KhlyahZ?11-QzB|86^Q$RBflpFxsk@=tl*t3xaf*$}M zm80^TCV;1cG~lWw;88?A=mF-goDT#8z?mm&=f5@wyb!X0x+%b#`7i7Oj$FzHg00B> zZvgJ(9lQnr=ofrAy8vDac|hVFfVr-6&YROS2k^5C#SXtK0uvq8jL)iLf>mCn=`Az*0d3Sjg8z-K*atBu(W8V?>T@P00000NkvXXu0mjf?Sd%& literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_face_white_24dp.png b/res/drawable-xhdpi/ic_face_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..430be5e84a40161d15ad3bfa3791525991e034ca GIT binary patch literal 694 zcmV;n0!jUeP)e4JbCbSY?&;Y~_`TZ+Jx_8t&FwY+gHuEqAjc9Vs?@1drNk0b zWQbzIChQM-nPrzCJ3OYxJ&`&`9K#%>?{NYda0ug zVvsq{2W)s;K0YuTa6JA*c*qgqX3znm?9%L=zII&WEq^)L77-m7pxH7>%&~KWXY6y5 zejUiE!--)Co&VWBx`~>ls8b_?nJ1{nNGq*rd~HJsW&Yrx35yy%=Rwwy{`$gP72P4B~)xjN_c+ z5YeXtQFajboda3KCJ`MlJVAV;&1w4t>xgL#IzSI~{{$T-hp2H$2W0YyBW||5{cURM zA*BK-s)#kZTH3yw4Mdra;DBL>hR@h1x+$t>cvCKQWKy(jtlhg8%>k07*qoM6N<$f(@=SW&i*H literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_person_add_white_24dp.png b/res/drawable-xhdpi/ic_person_add_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..7e7c289d4971337ec3693780d13b26c146c58a5f GIT binary patch literal 329 zcmV-P0k-~$P)95&=c7)Ex&ZntsOymfDWHiA zg$@)`eY^zFie0;oB?c^qK%8UTE4gLto&GPuldssShyXo3?#1bOU!tx^)93lHT2bFewlK`St_| bKmdLLcf8r0$|reu00000NkvXXu0mjflV6Dp literal 0 HcmV?d00001 diff --git a/res/drawable-xxhdpi/ic_block_white_24dp.png b/res/drawable-xxhdpi/ic_block_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..fddfa54b85576986db9096ff67fc768ee70b2bd3 GIT binary patch literal 973 zcmV;;12X)HP)0!w1IO{x1Sd{%lZ9j&aV5mHi^d0NLky-yTnWv>H(|zy7L>dI5tIaL^KYS`#wPHu zxNwMzh{CAB5Q=o|B8Gqu5r=DV=gbwq-Sx-KkqtwZWL1pbmaxYj2rjoKgDOMUFNC_A<oFWK{G)G{${gRjFZP$EKiYjHhWGXYfw*JPL)dy{^SK*CaO#7&Jxr? znYb71$7I++R)~8iMvaH$+Vq3V6&ea4LU{KG;Nu477+QNk)S<%MBslI$2qV; zpMB8(5xDF0>Bt|pK}QjILEtQBo|gy=*#$jAs_b``QzFM9BIJop&@cj*oaPt|R{$B@ zKIlI3icZHlhJbfSX}h34J|jh!Isfhq5>ZGGI*(MyJIv8K`G6GDgBFo%j&rmrql0TgFW|TnDZ6yk?)g(I;kN8 zj&robwtqhiPmxK-IodQ*#-s(ELqhUia=HkS@${fRBBbarNBa?}(VHG*m`BzLoaY3r zBlj@d1q~r^*-?&m6@hP)gS15?;-J%Na$IL?Zo=^P6xg0;{<29!+W+i$qqLccw=nPtdNr~uQ>ad zCA%-&qTv1#c!(!tQD)HJHTfVVB5c*Trw^#>Agw@=$5cu4i8A9l;QoTNJjb}mO_o@t vLX|2NR$1Z(7Z_nDCff{t*9vL{wSxWu*KBixf&sj+00000NkvXXu0mjffyKys literal 0 HcmV?d00001 diff --git a/res/drawable-xxhdpi/ic_face_white_24dp.png b/res/drawable-xxhdpi/ic_face_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..7e329149f046b23ebb1468eb5f7f0410f9d0ab43 GIT binary patch literal 1022 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 literal 0 HcmV?d00001 diff --git a/res/layout/unknown_sender_view.xml b/res/layout/unknown_sender_view.xml new file mode 100644 index 0000000000..9756e5a2ac --- /dev/null +++ b/res/layout/unknown_sender_view.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index d98b3bb676..186edc38d0 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -617,6 +617,14 @@ Signal update A new version of Signal is available, tap to update + + Block %s? + Blocked contacts will no longer be able to send you messages or call you. + Block + Share profile with %s? + The easiest way to share your profile information is to add the sender to your contacts. If you do not wish to, you can still share your profile information this way. + Share profile + Send message? Send @@ -1065,6 +1073,12 @@ Enter a name or number Add members + + The sender is not in your contact list + BLOCK + ADD TO CONTACTS + DON\'T ADD, BUT MAKE MY PROFILE VISIBLE + Learn more.]]> Tap to scan @@ -1424,7 +1438,6 @@ Transport icon - diff --git a/src/org/thoughtcrime/securesms/ConversationFragment.java b/src/org/thoughtcrime/securesms/ConversationFragment.java index f2f9c1bef2..a9290f8b8d 100644 --- a/src/org/thoughtcrime/securesms/ConversationFragment.java +++ b/src/org/thoughtcrime/securesms/ConversationFragment.java @@ -61,6 +61,7 @@ import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.Slide; +import org.thoughtcrime.securesms.profiles.UnknownSenderView; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.sms.MessageSender; @@ -100,6 +101,7 @@ public class ConversationFragment extends Fragment private RecyclerView list; private RecyclerView.ItemDecoration lastSeenDecoration; private View loadMoreView; + private UnknownSenderView unknownSenderView; private View composeDivider; private View scrollToBottomButton; private TextView scrollDateHeader; @@ -132,14 +134,12 @@ public class ConversationFragment extends Fragment list.setItemAnimator(null); loadMoreView = inflater.inflate(R.layout.load_more_header, container, false); - loadMoreView.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - Bundle args = new Bundle(); - args.putLong("limit", 0); - getLoaderManager().restartLoader(0, args, ConversationFragment.this); - } + loadMoreView.setOnClickListener(v -> { + Bundle args = new Bundle(); + args.putLong("limit", 0); + getLoaderManager().restartLoader(0, args, ConversationFragment.this); }); + return view; } @@ -184,10 +184,11 @@ public class ConversationFragment extends Fragment } private void initializeResources() { - this.recipient = RecipientFactory.getRecipientFor(getActivity(), (Address)getActivity().getIntent().getParcelableExtra(ConversationActivity.ADDRESS_EXTRA), true); - this.threadId = this.getActivity().getIntent().getLongExtra(ConversationActivity.THREAD_ID_EXTRA, -1); - this.lastSeen = this.getActivity().getIntent().getLongExtra(ConversationActivity.LAST_SEEN_EXTRA, -1); - this.firstLoad = true; + this.recipient = RecipientFactory.getRecipientFor(getActivity(), (Address) getActivity().getIntent().getParcelableExtra(ConversationActivity.ADDRESS_EXTRA), true); + this.threadId = this.getActivity().getIntent().getLongExtra(ConversationActivity.THREAD_ID_EXTRA, -1); + this.lastSeen = this.getActivity().getIntent().getLongExtra(ConversationActivity.LAST_SEEN_EXTRA, -1); + this.firstLoad = true; + this.unknownSenderView = new UnknownSenderView(getActivity(), recipient, threadId); OnScrollListener scrollListener = new ConversationScrollListener(getActivity()); list.addOnScrollListener(scrollListener); @@ -430,6 +431,12 @@ public class ConversationFragment extends Fragment setLastSeen(loader.getLastSeen()); } + if (!loader.hasSent() && recipient.getName() == null) { + getListAdapter().setHeaderView(unknownSenderView); + } else { + getListAdapter().setHeaderView(null); + } + getListAdapter().changeCursor(cursor); int lastSeenPosition = getListAdapter().findLastSeenPosition(lastSeen); @@ -456,6 +463,8 @@ public class ConversationFragment extends Fragment MessageRecord messageRecord = DatabaseFactory.getMmsDatabase(getContext()).readerFor(message, threadId).getCurrent(); if (getListAdapter() != null) { + getListAdapter().setHeaderView(null); + setLastSeen(0); getListAdapter().addFastRecord(messageRecord); } @@ -466,6 +475,8 @@ public class ConversationFragment extends Fragment MessageRecord messageRecord = DatabaseFactory.getSmsDatabase(getContext()).readerFor(message, threadId).getCurrent(); if (getListAdapter() != null) { + getListAdapter().setHeaderView(null); + setLastSeen(0); getListAdapter().addFastRecord(messageRecord); } diff --git a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java index 5cbe9e599b..c907350b3e 100644 --- a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java +++ b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java @@ -105,7 +105,8 @@ public class DatabaseFactory { private static final int INTERNAL_SYSTEM_DISPLAY_NAME = 40; private static final int PROFILES = 41; private static final int PROFILE_SHARING_APPROVAL = 42; - private static final int DATABASE_VERSION = 42; + private static final int UNSEEN_NUMBER_OFFER = 43; + private static final int DATABASE_VERSION = 43; private static final String DATABASE_NAME = "messages.db"; private static final Object lock = new Object(); @@ -1312,6 +1313,10 @@ public class DatabaseFactory { db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN profile_sharing_approval INTEGER DEFAULT 0"); } + if (oldVersion < UNSEEN_NUMBER_OFFER) { + db.execSQL("ALTER TABLE thread ADD COLUMN has_sent INTEGER DEFAULT 0"); + } + db.setTransactionSuccessful(); db.endTransaction(); } diff --git a/src/org/thoughtcrime/securesms/database/MmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsDatabase.java index 6df470108d..ca16c8455c 100644 --- a/src/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -808,6 +808,7 @@ public class MmsDatabase extends MessagingDatabase { long messageId = insertMediaMessage(masterSecret, message.getBody(), message.getAttachments(), contentValues, insertListener); DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); + DatabaseFactory.getThreadDatabase(context).setHasSent(threadId, true); jobManager.add(new TrimThreadJob(context, threadId)); return messageId; diff --git a/src/org/thoughtcrime/securesms/database/SmsDatabase.java b/src/org/thoughtcrime/securesms/database/SmsDatabase.java index 5defe8f154..7e043fa4b7 100644 --- a/src/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -615,6 +615,8 @@ public class SmsDatabase extends MessagingDatabase { DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); } + DatabaseFactory.getThreadDatabase(context).setHasSent(threadId, true); + notifyConversationListeners(threadId); if (!message.isIdentityVerified() && !message.isIdentityDefault()) { diff --git a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java index 820734aa03..b8769df859 100644 --- a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -46,6 +46,7 @@ import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.util.DelimiterUtil; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.InvalidMessageException; +import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.guava.Optional; import java.util.LinkedList; @@ -73,6 +74,7 @@ public class ThreadDatabase extends Database { public static final String RECEIPT_COUNT = "delivery_receipt_count"; public static final String EXPIRES_IN = "expires_in"; public static final String LAST_SEEN = "last_seen"; + private static final String HAS_SENT = "has_sent"; public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " + DATE + " INTEGER DEFAULT 0, " + @@ -82,7 +84,7 @@ public class ThreadDatabase extends Database { SNIPPET_TYPE + " INTEGER DEFAULT 0, " + SNIPPET_URI + " TEXT DEFAULT NULL, " + ARCHIVED + " INTEGER DEFAULT 0, " + STATUS + " INTEGER DEFAULT 0, " + RECEIPT_COUNT + " INTEGER DEFAULT 0, " + EXPIRES_IN + " INTEGER DEFAULT 0, " + - LAST_SEEN + " INTEGER DEFAULT 0);"; + LAST_SEEN + " INTEGER DEFAULT 0, " + HAS_SENT + " INTEGER DEFAULT 0);"; public static final String[] CREATE_INDEXS = { "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESS + ");", @@ -417,20 +419,19 @@ public class ThreadDatabase extends Database { notifyConversationListListeners(); } - public long getLastSeen(long threadId) { + public Pair getLastSeenAndHasSent(long threadId) { SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = db.query(TABLE_NAME, new String[]{LAST_SEEN}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null); + Cursor cursor = db.query(TABLE_NAME, new String[]{LAST_SEEN, HAS_SENT}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null); try { if (cursor != null && cursor.moveToFirst()) { - return cursor.getLong(0); + return new Pair<>(cursor.getLong(0), cursor.getLong(1) == 1); } - return -1; + return new Pair<>(-1L, false); } finally { if (cursor != null) cursor.close(); } - } public void deleteConversation(long threadId) { @@ -520,6 +521,16 @@ public class ThreadDatabase extends Database { return null; } + public void setHasSent(long threadId, boolean hasSent) { + ContentValues contentValues = new ContentValues(1); + contentValues.put(HAS_SENT, hasSent ? 1 : 0); + + databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, ID_WHERE, + new String[] {String.valueOf(threadId)}); + + notifyConversationListeners(threadId); + } + public void updateReadState(long threadId) { int unreadCount = DatabaseFactory.getMmsSmsDatabase(context).getUnreadCount(threadId); diff --git a/src/org/thoughtcrime/securesms/database/loaders/ConversationLoader.java b/src/org/thoughtcrime/securesms/database/loaders/ConversationLoader.java index 21cace94e3..3f8866b81d 100644 --- a/src/org/thoughtcrime/securesms/database/loaders/ConversationLoader.java +++ b/src/org/thoughtcrime/securesms/database/loaders/ConversationLoader.java @@ -5,17 +5,20 @@ import android.database.Cursor; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.util.AbstractCursorLoader; +import org.whispersystems.libsignal.util.Pair; public class ConversationLoader extends AbstractCursorLoader { - private final long threadId; - private long limit; - private long lastSeen; + private final long threadId; + private long limit; + private long lastSeen; + private boolean hasSent; public ConversationLoader(Context context, long threadId, long limit, long lastSeen) { super(context); this.threadId = threadId; this.limit = limit; this.lastSeen = lastSeen; + this.hasSent = true; } public boolean hasLimit() { @@ -26,10 +29,18 @@ public class ConversationLoader extends AbstractCursorLoader { return lastSeen; } + public boolean hasSent() { + return hasSent; + } + @Override public Cursor getCursor() { + Pair lastSeenAndHasSent = DatabaseFactory.getThreadDatabase(context).getLastSeenAndHasSent(threadId); + + this.hasSent = lastSeenAndHasSent.second(); + if (lastSeen == -1) { - this.lastSeen = DatabaseFactory.getThreadDatabase(context).getLastSeen(threadId); + this.lastSeen = lastSeenAndHasSent.first(); } return DatabaseFactory.getMmsSmsDatabase(context).getConversation(threadId, limit); diff --git a/src/org/thoughtcrime/securesms/profiles/UnknownSenderView.java b/src/org/thoughtcrime/securesms/profiles/UnknownSenderView.java new file mode 100644 index 0000000000..7af27e76e3 --- /dev/null +++ b/src/org/thoughtcrime/securesms/profiles/UnknownSenderView.java @@ -0,0 +1,106 @@ +package org.thoughtcrime.securesms.profiles; + + +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; +import android.provider.ContactsContract; +import android.support.annotation.NonNull; +import android.support.v7.app.AlertDialog; +import android.text.TextUtils; +import android.view.View; +import android.widget.FrameLayout; + +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.ViewUtil; + +public class UnknownSenderView extends FrameLayout { + + private final @NonNull Recipient recipient; + private final long threadId; + + public UnknownSenderView(@NonNull Context context, @NonNull Recipient recipient, long threadId) { + super(context); + this.recipient = recipient; + this.threadId = threadId; + + inflate(context, R.layout.unknown_sender_view, this); + + View block = ViewUtil.findById(this, R.id.block); + View add = ViewUtil.findById(this, R.id.add_to_contacts); + View profileAccess = ViewUtil.findById(this, R.id.share_profile); + + block.setOnClickListener(v -> handleBlock()); + add.setOnClickListener(v -> handleAdd()); + profileAccess.setOnClickListener(v -> handleProfileAccess()); + } + + private void handleBlock() { + final Context context = getContext(); + + new AlertDialog.Builder(getContext()) + .setIconAttribute(R.attr.dialog_alert_icon) + .setTitle(getContext().getString(R.string.UnknownSenderView_block_s, recipient.toShortString())) + .setMessage(R.string.UnknownSenderView_blocked_contacts_will_no_longer_be_able_to_send_you_messages_or_call_you) + .setPositiveButton(R.string.UnknownSenderView_block, (dialog, which) -> { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + DatabaseFactory.getRecipientPreferenceDatabase(context).setBlocked(recipient, true); + if (threadId != -1) DatabaseFactory.getThreadDatabase(context).setHasSent(threadId, true); + return null; + } + + @Override + protected void onPostExecute(Void result) { + recipient.setBlocked(true); + } + }.execute(); + }) + .setNegativeButton(android.R.string.cancel, null) + .show(); + } + + private void handleAdd() { + Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT); + intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE); + + if (!TextUtils.isEmpty(recipient.getProfileName())) { + intent.putExtra(ContactsContract.Intents.Insert.NAME, recipient.getProfileName()); + } + + if (recipient.getAddress().isEmail()) { + intent.putExtra(ContactsContract.Intents.Insert.EMAIL, recipient.getAddress().toEmailString()); + } + + if (recipient.getAddress().isPhone()) { + intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipient.getAddress().toPhoneString()); + } + + getContext().startActivity(intent); + if (threadId != -1) DatabaseFactory.getThreadDatabase(getContext()).setHasSent(threadId, true); + } + + private void handleProfileAccess() { + final Context context = getContext(); + + new AlertDialog.Builder(getContext()) + .setIconAttribute(R.attr.dialog_info_icon) + .setTitle(getContext().getString(R.string.UnknownSenderView_share_profile_with_s, recipient.toShortString())) + .setMessage(R.string.UnknownSenderView_the_easiest_way_to_share_your_profile_information_is_to_add_the_sender_to_your_contacts) + .setPositiveButton(R.string.UnknownSenderView_share_profile, (dialog, which) -> { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + DatabaseFactory.getRecipientPreferenceDatabase(context).setProfileSharing(recipient.getAddress(), true); + if (threadId != -1) DatabaseFactory.getThreadDatabase(context).setHasSent(threadId, true); + return null; + } + }.execute(); + }) + .setNegativeButton(android.R.string.cancel, null) + .show(); + } +} diff --git a/src/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java b/src/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java index f2115acadd..e72bb20cd1 100644 --- a/src/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java +++ b/src/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java @@ -132,7 +132,7 @@ public class StickyHeaderDecoration extends RecyclerView.ItemDecoration { { int headerHeight = getHeaderHeightForLayout(header); int top = getChildY(parent, child) - headerHeight; - if (layoutPos == 0) { + if (sticky && layoutPos == 0) { final int count = parent.getChildCount(); final long currentId = adapter.getHeaderId(adapterPos); // find next view with header and compute the offscreen push if needed