From d71ec9809dcc54d839c4a758d72e6df2dfac2962 Mon Sep 17 00:00:00 2001 From: akwizgran <michael@briarproject.org> Date: Mon, 4 Mar 2013 19:45:31 +0000 Subject: [PATCH] Android UI for reading a message (text/plain only for now). --- briar-android/AndroidManifest.xml | 4 + .../res/drawable-hdpi/social_reply.png | Bin 0 -> 1487 bytes .../res/drawable-mdpi/social_reply.png | Bin 0 -> 1340 bytes .../res/drawable-xhdpi/social_reply.png | Bin 0 -> 1701 bytes .../android/contact/ContactListActivity.java | 8 +- .../android/contact/ContactListAdapter.java | 4 +- .../messages/ConversationActivity.java | 39 +++- .../android/messages/ConversationAdapter.java | 20 +- .../android/messages/ConversationItem.java | 67 ++++++ .../messages/ConversationListActivity.java | 14 +- .../messages/ConversationListAdapter.java | 10 +- .../messages/ConversationListItem.java | 10 +- .../android/messages/ReadMessageActivity.java | 201 ++++++++++++++++++ .../net/sf/briar/api/db/MessageHeader.java | 4 +- .../src/net/sf/briar/db/H2DatabaseTest.java | 16 +- 15 files changed, 343 insertions(+), 54 deletions(-) create mode 100644 briar-android/res/drawable-hdpi/social_reply.png create mode 100644 briar-android/res/drawable-mdpi/social_reply.png create mode 100644 briar-android/res/drawable-xhdpi/social_reply.png create mode 100644 briar-android/src/net/sf/briar/android/messages/ConversationItem.java create mode 100644 briar-android/src/net/sf/briar/android/messages/ReadMessageActivity.java diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml index ba01be5bfc..6b12578c8c 100644 --- a/briar-android/AndroidManifest.xml +++ b/briar-android/AndroidManifest.xml @@ -48,5 +48,9 @@ android:name=".android.messages.ConversationListActivity" android:label="@string/messages_title" > </activity> + <activity + android:name=".android.messages.ReadMessageActivity" + android:label="@string/messages_title" > + </activity> </application> </manifest> diff --git a/briar-android/res/drawable-hdpi/social_reply.png b/briar-android/res/drawable-hdpi/social_reply.png new file mode 100644 index 0000000000000000000000000000000000000000..b0fcbf356e9d6363ca546c2d803f15f01cbcb21d GIT binary patch literal 1487 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@%qp275hW46K32*3xq68pHF_1f1wh>l3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|8<fR&VF+bTgE72zA8;GAESs$i;Tpqp%9W~g9hqGxDg zU}<8hqhMrUXrOOsq;FuZYiM9)YHnp<r~m~@K--E^(yW49+@N*=dA3R!B_#z``ugSN z<$C4Ddih1^`i7R4mih)p`bI{&Koz>hm3bwJ6}oxF$}kgLQj3#|G7CyF^YauyCMG83 zmzLNn0bL65LT&-v*t}wBFaZNhzap_f-%!s0<RzFwUtj!6b93RUi%Wu15$?rmaB)aw zL8^XGYH@yPQ8F;%(v(3~6<9eJr6!i-7lq{K=fFZSAS1sdzc?emK*2fKRL@YsH!(Rg z4<rKC;p=PVnO9trn3tUD>0+w{G(#^lGsVi((%jPA(9p@$$-u?X(ACh=#L&{!#L3yw z&D7A`#K{n**Cju>G&eP`1g19yq1PFwUQlAlEdbi=l3J8mmYU*Ll%J~r_Ow+dZns$C zG!Lpb1-Dxqaq86vIz}H9wMbD769T3m5EGtofgE_!Pt60S_ab1zW=(s?$H2hk=;`7Z zQgQ3e%=6kTi4w=lul-Y)A~WTYmeL_NzEiEUWn!ml{1XZnXMd{E7bEjT!6i33h|l}f zL9L*mX{p!W{n+=_XRmMCy}5JC)5<>W`+9Pl@owAswe!DM-Yd`8wa)#J&7z4D_*ECM zOlc6}C_C~-Z&{G0`Te_$aSgBCCZ9~HnctXbpsD;rA!vp8nx<_A>ll@Phz0P>ZV;@| z)@WVJ_3@y%vRz-sjOOWz0vR6;*(pfWCa9ixaF?T}k@bQ^%7?978y<)|#suVksS+`0 z>JJsP5zg7P<6PR>H`^KH|MWk&m%nW)lXHE{>W1(SEbk7QR(y*(;Q8SXkBaWP&AD#; zZ>N7!;O0BnE6(KC{9JX-r6p&UCN!mG3&=Fu&T`bOO<>ZwF0g0v!Jt>&ap#MwS6-0K zm~nz#g3;UT<#Ue*OgWd<%YXJ<!r*pr&KgON=Cx8PCKnQvyxw<T<C{EVS*xCE>m09) zEgqlMyuSQg;;_W&`<WAOm@|Jx`?`7s{dzmAZcW(*-eU(F76fhdy28K9!7ohxnd+Nu zLdSSSeBN(%{l#$Zpva8n?=_>(I2oHBo^wX)h>Dqp>Z2x;P145`X1}|vlay?`=yJDF z)lpBCn2<Tfx4cw*3?+BpsXa8=Zh~rO`E;SoC~mh0Z@y|qFk64gIXRh`SO4L&kh0^U z+r0}I^8Q*(JUQt^u+-cM4hsINca~gMk65o-akE-6PU&62^(b-vZ55o`uWA}F#ywhl zPk!|c-f8FiuPm^!y_5aw{}C?-P7OxY59?X$SS1){Mosd28hHkom>3v5UHx3vIVCg! E0Fd4yZ~y=R literal 0 HcmV?d00001 diff --git a/briar-android/res/drawable-mdpi/social_reply.png b/briar-android/res/drawable-mdpi/social_reply.png new file mode 100644 index 0000000000000000000000000000000000000000..75723bca5710f2c2f62fb99c5413ca143a049109 GIT binary patch literal 1340 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@%qp275hW46K32*3xq68pHF_1f1wh>l3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|8<fR&VF+bTgE72zA8;GAESs$i;Tpqp%9W~g9hqGxDg zU}<8hqhMrUXrOOsq;FuZYiM9)YHnp<r~m~@K--E^(yW49+@N*=dA3R!B_#z``ugSN z<$C4Ddih1^`i7R4mih)p`bI{&Koz>hm3bwJ6}oxF$}kgLQj3#|G7CyF^YauyCMG83 zmzLNn0bL65LT&-v*t}wBFaZNhzap_f-%!s0<RzFwUtj!6b93RUi%Wu15$?rmaB)aw zL8^XGYH@yPQ8F;%(v(3~6<9eJr6!i-7lq{K=fFZSAS1sdzc?emK*2fKRL@YsH!(Rg z4<rKC;p=PVnO9trn3tUD>0+w{G(#^lGsVi((%jPA(9q4)$-u?X(ACh=#L&{!#L3yw z&D7A`#K{n**Cju>G&eP`1g19yuGh!_r(RHE$SnZc?2=lPS(cjOR+OKs0QR(1CT_R5 z;4}}aHwC9#3|(>R)dxC89~8AnQ4JFUrXLU!o^XL2c+yYJ1E%*PV8T9?$e6*vz_`xS z#WAGf)|=_)wOs;5jz2d{>zR_YH0A8H>6<2`7~Z^bgR^u0vG@g3gg&#MV2g~{P?Mvx z;--fZw^_@_p4Zi{?o{1fXZ`)}hj(dywX@8(m*4-nulSs8`Y~ZAk(em=gB!Q9T8IaP zh}YcTe)l%J(+9cZi2^J$T@EmZiak_eJ+1TLc58!b**`{?drU^9a^Vio&TU=pkUTem z^^D7$>Pk+9=dTl9^*_G$!=F*@dDHiGZY}{K_YT;uXl^%Q%BVg2;aSt`^cc_fJ<Dv( z6Xfq46E$FYc5sR7^lXVm=4@^bymOx0a@Xxh*ljUMxR51)Z~Fns7nu)N@a&p7=~?Bb zV7;b)7N!o;+kZxdvrl=gw&Q_Gg^hTd<N8TbQW4Av)3TMe*s}UIbM(8uYq);mmqNsX zg{wM#JTeJLj61rLQS%W?hzakCH8u4tvmeX~R^r}tBtOpc5~t@8J@IKdN7ufyXIdQN ztp9*vj@HFZVhU@I%@6J7t2o}Le^9Z)&zQSJ>Dhth5A9xcNI%f*PFy1FxYamU!fRpg z=Os)hzF9CG-6E^~k~u?6W_7yN#s1UZ;x}(&xXx6^D#4Hzm9{f^%|mridFJWr=d#Wz Gp$P#0tKPN% literal 0 HcmV?d00001 diff --git a/briar-android/res/drawable-xhdpi/social_reply.png b/briar-android/res/drawable-xhdpi/social_reply.png new file mode 100644 index 0000000000000000000000000000000000000000..1dc01d7df2c545b5cd55a8cd8da5768e7bccf80c GIT binary patch literal 1701 zcmaJ?c~BE)91V8`5o!<=L6=2QsAP9zLP+EaBtROH(m**zR6?>ygk;xbF+mvwN&y8g z5M)|YMbtW2w2nnZQp64&MWNuJ2x27yDiswfI?`dR-5_B9knYUxcg%b5cYN=g-4!j4 zu(x%!rBEpLf=Hf(979a+C)VV*R<ZQ~IV>Ug2}BH*N@%5Ol){x`DJURNN;jhtR4UKf z+KL8HC>AReu?a+iD2gM)lr*UcL(?g7l1-rm1nY3AEFC3)6m+vf6$p+tUI77xJP?d$ zieM2QhNdYZx2w^Z?c!M3_H-Fr4h9DS0XhyTphO8Npi^e3G#p(ZIHk)W=ca841g0QF zdLZ~dsRU6p5QeEyfJuXWWw0Lt_%Ub*17<VWYXCZozz{-*V3scebC@g+i~!RQNJdl3 zQ#le|_;f6C6$qvg1kQmVtyW9ZuBBn>%@D$7vmuxc(doV<!dH`}BBVNBmBw>Mfrn~j zY6VUxFcn}@l%`;rL?B3J`o07uJ}axzOt*<_7^IWp5JH1ZCCvauqW^~~m9uCKAwfUn z`%z&{Y!;3}5>$g_s%7NCrFxn|;hZovDkU&=EQV#wbTK*&BQQ-Gh67<SOd0}+q%wud zv@^vJi8un4hLEacsDKv;k{UFHLe62bS!@=9u$ZAR4?*|{n}M+TjL<M{I1^zpLXjCR z50hmoQ57-6m4D#+&&f5JL5Y)*d8k^k4V8zhF(oinG)M8VT>QhiT$s+CQ*W9ppF0=7 zIk^y-3}iam{~Yzq7U>_;bk?}!V%GYoiuAjhG&Vi)^*i#uxCnUMSlzv6-wD&5xK7{f z@RGR=uICQWjO$yTINGa-K?i(geo67t^IJ#GIwqw#^0Wh=JMp;}eL&As6UF=Z+Xn3V z+lHeuI)3ZwVYK$#>G~>0Uz_hxSS`@%cRcN`>noQmF3h`G<K`b^plqZ-xh~CiMyoM% zo!Mdc3Wm#_E%k=c#>GEhb<mfezB>}fNnYkpvEGnm{?-Wh%sZJx4FPvrJ~I#I4wq3D z{{1C9|Jw^uL75BNJYLq6W?LLgx@TQ}0DA0K6*HvwDfn@pL2Tm$zP#)fU3Gl~ZwB#G zk>wpRxhIUP6V@MZeNt~(iN734eAwR9W!wn{TX)MNb?wLO9QipL3R{KDON$Jn_?}CD zel{_`sn;unb-<zF)q?z{gXY<m9alZas_&W;)U1k?04?$QWSx0mbI#b}`_^CV@Gv3i z{-{qk%aQvy_fx>Pw$VtRkZ*A*j%ae}4O(&Qmp8eC$u;xL3Y(D^<9-J(UTG+Z>pak> z#EOSqujKF4pKRw?RavgODtyp+^44DW=D4RsZbrnQ(W)xFnp$bKv#}l33q$BHyoLJC zA$m{UI-8+K1wK_rl5_jtjQ7S)7OCHEtT4*!EIn71{^fW(Iy<tr4~mMhjj!spY50To zr2B4Mf^FD+*X6@Oc06&ht!Kr(5U<ychUa*@xpUy;YM!vV?aVnz#?u7zwT}w&<_nK@ zO)OC5QH`0qM`}Dml}F1<Z<`GibRGTu@P2LoJA1uftHi&3|Dm&|Mb4q%uS@Vv9+Fyd zV@p!>3;2-Fc!^u3R}_O+*4LSsvn)0A#`vNhs(6bqMHx|a+?m>MKXxNFBJJ{;?C+0^ zt4f6OBYC1-yNcSl@!n<U_I5_qRNtCMEzPN@JY>MB_Xo|qf8tW~V%;@b*-$}xlwm{t zO`>V(?T40v{P<?~u@f%t)f?Aw|9S8_V0mZDs70AL&B}St?&`@FJ$x*>ufx@BK)nBX zdPH(7#P=y2T_yI9uzA(6I%KGz<ieV-%ijIqeXVHLk?#$9esCYff->Q0Xjr(ZUTpet N2>4>&nb73?e*s@?j?e%A literal 0 HcmV?d00001 diff --git a/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java b/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java index a6e7e3f321..d0d7ace2fd 100644 --- a/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java +++ b/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java @@ -88,8 +88,6 @@ implements OnClickListener, DatabaseListener, ConnectionListener { // Bind to the service so we can wait for the DB to be opened bindService(new Intent(BriarService.class.getName()), serviceConnection, 0); - // Load the contact list from the DB - reloadContactList(); // Add some fake contacts to the database in a background thread // FIXME: Remove this @@ -118,6 +116,12 @@ implements OnClickListener, DatabaseListener, ConnectionListener { }); } + @Override + public void onResume() { + super.onResume(); + reloadContactList(); + } + @Override public void onDestroy() { super.onDestroy(); diff --git a/briar-android/src/net/sf/briar/android/contact/ContactListAdapter.java b/briar-android/src/net/sf/briar/android/contact/ContactListAdapter.java index 1521492c93..45bc63639d 100644 --- a/briar-android/src/net/sf/briar/android/contact/ContactListAdapter.java +++ b/briar-android/src/net/sf/briar/android/contact/ContactListAdapter.java @@ -1,6 +1,6 @@ package net.sf.briar.android.contact; -import static android.view.Gravity.CENTER; +import static android.view.Gravity.CENTER_VERTICAL; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.widget.LinearLayout.HORIZONTAL; @@ -35,7 +35,7 @@ implements OnItemClickListener { Context ctx = getContext(); LinearLayout layout = new LinearLayout(ctx); layout.setOrientation(HORIZONTAL); - layout.setGravity(CENTER); + layout.setGravity(CENTER_VERTICAL); ImageView bulb = new ImageView(ctx); bulb.setPadding(5, 5, 5, 5); diff --git a/briar-android/src/net/sf/briar/android/messages/ConversationActivity.java b/briar-android/src/net/sf/briar/android/messages/ConversationActivity.java index 82ae8a74fe..5188bbbe7e 100644 --- a/briar-android/src/net/sf/briar/android/messages/ConversationActivity.java +++ b/briar-android/src/net/sf/briar/android/messages/ConversationActivity.java @@ -28,6 +28,8 @@ import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; import android.widget.Button; import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; @@ -36,7 +38,7 @@ import android.widget.ListView; import com.google.inject.Inject; public class ConversationActivity extends BriarActivity -implements OnClickListener, DatabaseListener { +implements DatabaseListener, OnClickListener, OnItemClickListener { private static final Logger LOG = Logger.getLogger(ConversationActivity.class.getName()); @@ -48,6 +50,7 @@ implements OnClickListener, DatabaseListener { @Inject @DatabaseExecutor private Executor dbExecutor; private ConversationAdapter adapter = null; + private String contactName = null; private volatile ContactId contactId = null; @Override @@ -55,12 +58,12 @@ implements OnClickListener, DatabaseListener { super.onCreate(null); Intent i = getIntent(); + contactName = i.getStringExtra("net.sf.briar.CONTACT_NAME"); + if(contactName == null) throw new IllegalStateException(); + setTitle(contactName); int id = i.getIntExtra("net.sf.briar.CONTACT_ID", -1); if(id == -1) throw new IllegalStateException(); contactId = new ContactId(id); - String contactName = i.getStringExtra("net.sf.briar.CONTACT_NAME"); - if(contactName == null) throw new IllegalStateException(); - setTitle(contactName); LinearLayout layout = new LinearLayout(this); layout.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT)); @@ -72,7 +75,7 @@ implements OnClickListener, DatabaseListener { // Give me all the width and all the unused height list.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT, 1f)); list.setAdapter(adapter); - list.setOnItemClickListener(adapter); + list.setOnItemClickListener(this); layout.addView(list); Button composeButton = new Button(this); @@ -92,7 +95,11 @@ implements OnClickListener, DatabaseListener { // Bind to the service so we can wait for the DB to be opened bindService(new Intent(BriarService.class.getName()), serviceConnection, 0); - // Load the message headers from the DB + } + + @Override + public void onResume() { + super.onResume(); reloadMessageHeaders(); } @@ -103,10 +110,6 @@ implements OnClickListener, DatabaseListener { unbindService(serviceConnection); } - public void onClick(View view) { - // FIXME: Hook this button up to an activity - } - public void eventOccurred(DatabaseEvent e) { if(e instanceof MessageAddedEvent) { if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading"); @@ -152,4 +155,20 @@ implements OnClickListener, DatabaseListener { } }); } + + public void onClick(View view) { + // FIXME: Hook this button up to an activity + } + + public void onItemClick(AdapterView<?> parent, View view, int position, + long id) { + PrivateMessageHeader item = adapter.getItem(position); + Intent i = new Intent(this, ReadMessageActivity.class); + i.putExtra("net.sf.briar.CONTACT_NAME", contactName); + i.putExtra("net.sf.briar.MESSAGE_ID", item.getId().getBytes()); + i.putExtra("net.sf.briar.CONTENT_TYPE", item.getContentType()); + i.putExtra("net.sf.briar.TIMESTAMP", item.getTimestamp()); + i.putExtra("net.sf.briar.STARRED", item.isStarred()); + startActivity(i); + } } diff --git a/briar-android/src/net/sf/briar/android/messages/ConversationAdapter.java b/briar-android/src/net/sf/briar/android/messages/ConversationAdapter.java index 31fdd1dcc8..8f2b7a96b2 100644 --- a/briar-android/src/net/sf/briar/android/messages/ConversationAdapter.java +++ b/briar-android/src/net/sf/briar/android/messages/ConversationAdapter.java @@ -1,7 +1,7 @@ package net.sf.briar.android.messages; import static android.graphics.Typeface.BOLD; -import static android.view.Gravity.CENTER; +import static android.view.Gravity.CENTER_VERTICAL; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.widget.LinearLayout.HORIZONTAL; import static java.text.DateFormat.SHORT; @@ -14,16 +14,13 @@ import android.content.Context; import android.text.format.DateUtils; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; import android.widget.TextView; -class ConversationAdapter extends ArrayAdapter<PrivateMessageHeader> -implements OnItemClickListener { +class ConversationAdapter extends ArrayAdapter<PrivateMessageHeader> { ConversationAdapter(Context ctx) { super(ctx, android.R.layout.simple_expandable_list_item_1, @@ -36,12 +33,11 @@ implements OnItemClickListener { Context ctx = getContext(); LinearLayout layout = new LinearLayout(ctx); layout.setOrientation(HORIZONTAL); - layout.setGravity(CENTER); + layout.setGravity(CENTER_VERTICAL); ImageView star = new ImageView(ctx); star.setPadding(5, 5, 5, 5); - if(item.getStarred()) - star.setImageResource(R.drawable.rating_important); + if(item.isStarred()) star.setImageResource(R.drawable.rating_important); else star.setImageResource(R.drawable.rating_not_important); layout.addView(star); @@ -57,7 +53,8 @@ implements OnItemClickListener { subject.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT, 1)); subject.setTextSize(14); - if(!item.getRead()) subject.setTypeface(null, BOLD); + subject.setMaxLines(2); + if(!item.isRead()) subject.setTypeface(null, BOLD); subject.setText(item.getSubject()); layout.addView(subject); @@ -70,9 +67,4 @@ implements OnItemClickListener { return layout; } - - public void onItemClick(AdapterView<?> parent, View view, int position, - long id) { - // FIXME - } } diff --git a/briar-android/src/net/sf/briar/android/messages/ConversationItem.java b/briar-android/src/net/sf/briar/android/messages/ConversationItem.java new file mode 100644 index 0000000000..2640bc105e --- /dev/null +++ b/briar-android/src/net/sf/briar/android/messages/ConversationItem.java @@ -0,0 +1,67 @@ +package net.sf.briar.android.messages; + +import net.sf.briar.api.db.PrivateMessageHeader; +import net.sf.briar.api.messaging.MessageId; + +class ConversationItem { + + private final PrivateMessageHeader header; + private final byte[] body; + private final boolean expanded; + + ConversationItem(PrivateMessageHeader header) { + this.header = header; + body = null; + expanded = false; + } + + // Collapse an existing item + ConversationItem(ConversationItem item) { + this.header = item.header; + body = null; + expanded = false; + } + + // Expand an existing item + ConversationItem(ConversationItem item, byte[] body) { + this.header = item.header; + this.body = body; + expanded = true; + } + + MessageId getId() { + return header.getId(); + } + + String getContentType() { + return header.getContentType(); + } + + String getSubject() { + return header.getSubject(); + } + + long getTimestamp() { + return header.getTimestamp(); + } + + boolean isRead() { + return header.isRead(); + } + + boolean isStarred() { + return header.isStarred(); + } + + boolean isIncoming() { + return header.isIncoming(); + } + + byte[] getBody() { + return body; + } + + boolean isExpanded() { + return expanded; + } +} diff --git a/briar-android/src/net/sf/briar/android/messages/ConversationListActivity.java b/briar-android/src/net/sf/briar/android/messages/ConversationListActivity.java index b54a41827e..a0d337641a 100644 --- a/briar-android/src/net/sf/briar/android/messages/ConversationListActivity.java +++ b/briar-android/src/net/sf/briar/android/messages/ConversationListActivity.java @@ -92,8 +92,6 @@ implements OnClickListener, DatabaseListener { // Bind to the service so we can wait for the DB to be opened bindService(new Intent(BriarService.class.getName()), serviceConnection, 0); - // Load the message headers from the DB - reloadMessageHeaders(); // Add some fake messages to the database in a background thread // FIXME: Remove this @@ -115,13 +113,10 @@ implements OnClickListener, DatabaseListener { "text/plain", "First message is short".getBytes("UTF-8")); db.addLocalPrivateMessage(m, contactId); - db.setReadFlag(m.getId(), true); - db.setStarredFlag(m.getId(), true); - Thread.sleep(1000); m = messageFactory.createPrivateMessage(m.getId(), "image/jpeg", new byte[1000]); db.receiveMessage(contactId, m); - Thread.sleep(1000); + db.setReadFlag(m.getId(), true); m = messageFactory.createPrivateMessage(m.getId(), "text/plain", ("Third message is quite long to test line" @@ -129,6 +124,7 @@ implements OnClickListener, DatabaseListener { + " all that fun stuff").getBytes("UTF-8")); db.addLocalPrivateMessage(m, contactId); db.setReadFlag(m.getId(), true); + db.setStarredFlag(m.getId(), true); } } catch(DbException e) { if(LOG.isLoggable(WARNING)) @@ -148,6 +144,12 @@ implements OnClickListener, DatabaseListener { }); } + @Override + public void onResume() { + super.onResume(); + reloadMessageHeaders(); + } + @Override public void onDestroy() { super.onDestroy(); diff --git a/briar-android/src/net/sf/briar/android/messages/ConversationListAdapter.java b/briar-android/src/net/sf/briar/android/messages/ConversationListAdapter.java index 234af5266e..42b68a5d0d 100644 --- a/briar-android/src/net/sf/briar/android/messages/ConversationListAdapter.java +++ b/briar-android/src/net/sf/briar/android/messages/ConversationListAdapter.java @@ -1,7 +1,7 @@ package net.sf.briar.android.messages; import static android.graphics.Typeface.BOLD; -import static android.view.Gravity.CENTER; +import static android.view.Gravity.CENTER_VERTICAL; import static android.view.Gravity.LEFT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.widget.LinearLayout.HORIZONTAL; @@ -38,12 +38,11 @@ implements OnItemClickListener { Context ctx = getContext(); LinearLayout layout = new LinearLayout(ctx); layout.setOrientation(HORIZONTAL); - layout.setGravity(CENTER); + layout.setGravity(CENTER_VERTICAL); ImageView star = new ImageView(ctx); star.setPadding(5, 5, 5, 5); - if(item.getStarred()) - star.setImageResource(R.drawable.rating_important); + if(item.isStarred()) star.setImageResource(R.drawable.rating_important); else star.setImageResource(R.drawable.rating_not_important); layout.addView(star); @@ -61,7 +60,8 @@ implements OnItemClickListener { TextView subject = new TextView(ctx); subject.setTextSize(14); - if(!item.getRead()) subject.setTypeface(null, BOLD); + subject.setMaxLines(2); + if(!item.isRead()) subject.setTypeface(null, BOLD); subject.setText(item.getSubject()); innerLayout.addView(subject); layout.addView(innerLayout); diff --git a/briar-android/src/net/sf/briar/android/messages/ConversationListItem.java b/briar-android/src/net/sf/briar/android/messages/ConversationListItem.java index 24b2e63a44..d0842efca0 100644 --- a/briar-android/src/net/sf/briar/android/messages/ConversationListItem.java +++ b/briar-android/src/net/sf/briar/android/messages/ConversationListItem.java @@ -23,10 +23,10 @@ class ConversationListItem { subject = headers.get(0).getSubject(); timestamp = headers.get(0).getTimestamp(); length = headers.size(); - boolean allRead = false, anyStarred = false; + boolean allRead = true, anyStarred = false; for(PrivateMessageHeader h : headers) { - allRead &= h.getRead(); - anyStarred |= h.getStarred(); + allRead &= h.isRead(); + anyStarred |= h.isStarred(); } read = allRead; starred = anyStarred; @@ -48,11 +48,11 @@ class ConversationListItem { return timestamp; } - boolean getRead() { + boolean isRead() { return read; } - boolean getStarred() { + boolean isStarred() { return starred; } diff --git a/briar-android/src/net/sf/briar/android/messages/ReadMessageActivity.java b/briar-android/src/net/sf/briar/android/messages/ReadMessageActivity.java new file mode 100644 index 0000000000..ea3f0dffd0 --- /dev/null +++ b/briar-android/src/net/sf/briar/android/messages/ReadMessageActivity.java @@ -0,0 +1,201 @@ +package net.sf.briar.android.messages; + +import static android.view.Gravity.CENTER_VERTICAL; +import static android.view.Gravity.RIGHT; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import static android.widget.LinearLayout.HORIZONTAL; +import static android.widget.LinearLayout.VERTICAL; +import static java.text.DateFormat.SHORT; +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; + +import java.io.UnsupportedEncodingException; +import java.util.concurrent.Executor; +import java.util.logging.Logger; + +import net.sf.briar.R; +import net.sf.briar.android.BriarActivity; +import net.sf.briar.android.BriarService; +import net.sf.briar.android.BriarService.BriarServiceConnection; +import net.sf.briar.api.db.DatabaseComponent; +import net.sf.briar.api.db.DatabaseExecutor; +import net.sf.briar.api.db.DbException; +import net.sf.briar.api.messaging.MessageId; +import android.content.Intent; +import android.os.Bundle; +import android.text.format.DateUtils; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.LinearLayout.LayoutParams; +import android.widget.TextView; + +import com.google.inject.Inject; + +public class ReadMessageActivity extends BriarActivity +implements OnClickListener { + + private static final Logger LOG = + Logger.getLogger(ReadMessageActivity.class.getName()); + + private final BriarServiceConnection serviceConnection = + new BriarServiceConnection(); + + @Inject private DatabaseComponent db; + @Inject @DatabaseExecutor private Executor dbExecutor; + + private MessageId messageId = null; + private boolean starred = false; + private ImageButton starButton = null, replyButton = null; + + @Override + public void onCreate(Bundle state) { + super.onCreate(null); + + Intent i = getIntent(); + String contactName = i.getStringExtra("net.sf.briar.CONTACT_NAME"); + if(contactName == null) throw new IllegalStateException(); + setTitle(contactName); + byte[] id = i.getByteArrayExtra("net.sf.briar.MESSAGE_ID"); + if(id == null) throw new IllegalStateException(); + messageId = new MessageId(id); + String contentType = i.getStringExtra("net.sf.briar.CONTENT_TYPE"); + if(contentType == null) throw new IllegalStateException(); + long timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1); + if(timestamp == -1) throw new IllegalStateException(); + starred = i.getBooleanExtra("net.sf.briar.STARRED", false); + + LinearLayout layout = new LinearLayout(this); + layout.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT)); + layout.setOrientation(VERTICAL); + + LinearLayout header = new LinearLayout(this); + header.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT)); + header.setOrientation(HORIZONTAL); + header.setGravity(CENTER_VERTICAL); + + starButton = new ImageButton(this); + starButton.setPadding(5, 5, 5, 5); + starButton.setBackgroundResource(0); + if(starred) starButton.setImageResource(R.drawable.rating_important); + else starButton.setImageResource(R.drawable.rating_not_important); + starButton.setOnClickListener(this); + header.addView(starButton); + + replyButton = new ImageButton(this); + replyButton.setPadding(5, 5, 5, 5); + replyButton.setBackgroundResource(0); + replyButton.setImageResource(R.drawable.social_reply); + replyButton.setOnClickListener(this); + header.addView(replyButton); + + TextView date = new TextView(this); + // Give me all the unused width + date.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT, 1)); + date.setTextSize(14); + date.setPadding(10, 0, 10, 0); + date.setGravity(RIGHT); + long now = System.currentTimeMillis(); + date.setText(DateUtils.formatSameDayTime(timestamp, now, SHORT, SHORT)); + header.addView(date); + layout.addView(header); + + if(contentType.equals("text/plain")) { + // Load and display the message body + TextView content = new TextView(this); + content.setPadding(10, 10, 10, 10); + layout.addView(content); + loadMessageBody(messageId, content); + } + + setContentView(layout); + + // Bind to the service so we can wait for the DB to be opened + bindService(new Intent(BriarService.class.getName()), + serviceConnection, 0); + } + + private void loadMessageBody(final MessageId id, final TextView view) { + dbExecutor.execute(new Runnable() { + public void run() { + try { + // Wait for the service to be bound and started + serviceConnection.waitForStartup(); + // Load the message body from the database + byte[] body = db.getMessageBody(id); + final String text = new String(body, "UTF-8"); + // Display the message body + runOnUiThread(new Runnable() { + public void run() { + view.setText(text); + } + }); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } catch(InterruptedException e) { + if(LOG.isLoggable(INFO)) + LOG.info("Interrupted while waiting for service"); + Thread.currentThread().interrupt(); + } catch(UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + }); + } + + @Override + public void onResume() { + super.onResume(); + final MessageId id = messageId; + dbExecutor.execute(new Runnable() { + public void run() { + try { + // Wait for the service to be bound and started + serviceConnection.waitForStartup(); + // Mark the message as read + db.setReadFlag(id, true); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } catch(InterruptedException e) { + if(LOG.isLoggable(INFO)) + LOG.info("Interrupted while waiting for service"); + Thread.currentThread().interrupt(); + } + } + }); + } + + @Override + public void onDestroy() { + super.onDestroy(); + unbindService(serviceConnection); + } + + @Override + public void onClick(View view) { + if(view == starButton) { + final MessageId id = messageId; + final boolean starredNow = !starred; + dbExecutor.execute(new Runnable() { + public void run() { + try { + db.setStarredFlag(id, starredNow); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } + } + }); + starred = starredNow; + if(starred) + starButton.setImageResource(R.drawable.rating_important); + else starButton.setImageResource(R.drawable.rating_not_important); + } else if(view == replyButton) { + // FIXME: Hook this up to an activity + } + } +} diff --git a/briar-api/src/net/sf/briar/api/db/MessageHeader.java b/briar-api/src/net/sf/briar/api/db/MessageHeader.java index 2cbab3f51c..95a927d3e7 100644 --- a/briar-api/src/net/sf/briar/api/db/MessageHeader.java +++ b/briar-api/src/net/sf/briar/api/db/MessageHeader.java @@ -49,12 +49,12 @@ public abstract class MessageHeader { } /** Returns true if the message has been read. */ - public boolean getRead() { + public boolean isRead() { return read; } /** Returns true if the message has been starred. */ - public boolean getStarred() { + public boolean isStarred() { return starred; } } diff --git a/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java b/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java index 3126208083..b59bdd71d1 100644 --- a/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java +++ b/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java @@ -1310,13 +1310,13 @@ public class H2DatabaseTest extends BriarTestCase { GroupMessageHeader header = it.next(); if(messageId.equals(header.getId())) { assertHeadersMatch(message, header); - assertTrue(header.getRead()); - assertFalse(header.getStarred()); + assertTrue(header.isRead()); + assertFalse(header.isStarred()); messageFound = true; } else if(messageId1.equals(header.getId())) { assertHeadersMatch(message1, header); - assertFalse(header.getRead()); - assertFalse(header.getStarred()); + assertFalse(header.isRead()); + assertFalse(header.isStarred()); message1Found = true; } else { fail(); @@ -1326,13 +1326,13 @@ public class H2DatabaseTest extends BriarTestCase { header = it.next(); if(messageId.equals(header.getId())) { assertHeadersMatch(message, header); - assertTrue(header.getRead()); - assertFalse(header.getStarred()); + assertTrue(header.isRead()); + assertFalse(header.isStarred()); messageFound = true; } else if(messageId1.equals(header.getId())) { assertHeadersMatch(message1, header); - assertFalse(header.getRead()); - assertFalse(header.getStarred()); + assertFalse(header.isRead()); + assertFalse(header.isStarred()); message1Found = true; } else { fail(); -- GitLab