From 0fdb9c156bf2c2bbcadd18e430d59f86d41b600b Mon Sep 17 00:00:00 2001 From: str4d Date: Thu, 15 May 2014 00:36:00 +0000 Subject: [PATCH] Address book list --- AndroidManifest.xml | 3 + res/drawable-hdpi/ic_social_add_person.png | Bin 0 -> 1783 bytes res/drawable-mdpi/ic_social_add_person.png | Bin 0 -> 1497 bytes res/drawable-xhdpi/ic_social_add_person.png | Bin 0 -> 2105 bytes res/layout/listitem_contact.xml | 24 +++ res/menu/address_book_list.xml | 11 ++ res/menu/main.xml | 6 + res/values/strings.xml | 4 + src/i2p/bote/android/EmailListActivity.java | 6 + .../addressbook/AddressBookActivity.java | 27 ++++ .../addressbook/AddressBookFragment.java | 139 ++++++++++++++++++ .../android/addressbook/ContactAdapter.java | 50 +++++++ src/i2p/bote/android/util/BoteHelper.java | 11 +- 13 files changed, 277 insertions(+), 4 deletions(-) create mode 100644 res/drawable-hdpi/ic_social_add_person.png create mode 100644 res/drawable-mdpi/ic_social_add_person.png create mode 100644 res/drawable-xhdpi/ic_social_add_person.png create mode 100644 res/layout/listitem_contact.xml create mode 100644 res/menu/address_book_list.xml create mode 100644 src/i2p/bote/android/addressbook/AddressBookActivity.java create mode 100644 src/i2p/bote/android/addressbook/AddressBookFragment.java create mode 100644 src/i2p/bote/android/addressbook/ContactAdapter.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index d7361e2..27a0e6d 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -29,6 +29,9 @@ + diff --git a/res/drawable-hdpi/ic_social_add_person.png b/res/drawable-hdpi/ic_social_add_person.png new file mode 100644 index 0000000000000000000000000000000000000000..d22a3ea949a96325a65857c4fa7eddb626a7494f GIT binary patch literal 1783 zcmaJ?do)ye9G`}v6nc@g>}@ECnYouiGi&mg8EeW&2umwt=Hf1M@3?a@Q+ZZL?Wz>D zQSFL7t3sotMdZ;Iu|n&xdF*mhv~6vVv+04_JB9X-wfCHRf4|@Nd_JG=^LOq6Ki^Lc z4a^NF6pEq9Ti{Qwp!S`wM}C8;uQJJHB_RwU0`I~NSR;WUM2OgjE5+nYi5A0D$rU7ZI2O(m4 z^!Gu9i2VRQhQojp6J$t1X9#d+Ga)v}VRP&O2M_{TkOK>JVL%|)$%P9-!1O~Wz2UMb zuD`%*+824^(PIcg!DX?Ml9HH7j!X=XW-dmU-cr9C(*B7nwU2XimLpu59jD9OmS*$Y5C7 z*8Z#2GbVC=wA(ssu)s*(dhvsqf^evh{7ZT7P#}EOM=88!xv#Vjf_*?r45twk!+5Z4F19$J%Sq zx0*M_iM2e=#HmY|Q%TBc$FYaWy{|Qw>Yqtpm% z8-u%aHfDbAZg-nvx+i|JjD92i#V}NDT))fKT|?g=oz9Oi(dX%I>K)P*rP$cTr+a#? z2521_tr3eSACGR$s@OcdyNfE*?9dSdBZY^}&gw+F@A50oj?1(vxPEWgsd-;qMRG)n zAGKH6X<IB|W{~X8ydTxnVEsk5@A6s+P7t32PLT&zHqMEV;4G`xV3eXkhZe3d}HO zD7YZ}%snvAYPU&2|AqBaS2mSiGyZGn*(DK5tJ>3nUrl+%C|I#uYJazBAvo;0SLEN` zj!l{ZqWep3|Ll=UDc(L_VOm0Xxvw9pyXRz5NF&hSoU{@5jY^+VKUO|Yttwx z|7oe)w~flKmWDM0%(2p6pVk%?d}2=%*O(8`M$<1Ymv*_VWG5$QolZQSA9_MJSKj1M zzT77|yA7J4)zP-(@m=LT^JDHD2>jzpuUn>o9LBMbk)CEG`{>)A4Kr1LUX5fEDT~T`)62siQgkl#*RtO+{a?agUm^uJY)2 z?&j*7j+JzDZqC|Q_z2k-r4EfhZLDt)ZV+;{c!6Q%vh_K|lcU15oIm%npCtEt)Rp+y z+q6OTMz)7)m+DuqD*ds?qN*`=px67Fy0BZ z&DVDN*UV3rXPhjw*_ieGSYPKk=G%`R?De`lkl7RTg)j9Pad73$3Dcr0z1s}#Tc=cI z2?e1EYlHem@vlZLen`qFxz%iaZlHPnp>ep2>YOoRbp1+er%#Y#JjeS@9?n@ry{zsn zmhU;--PhLoa<5)jd0AKNvBc1X9Q(nh^i7rzwtlv3;@0{6R&W6rSHBLYR-Vl3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|8hm3bwJ6}oxF$}kgLQj3#|G7CyF^YauyCMG83 zmzLNn0bL65LT&-v*t}wBFaZNhzap_f-%!s00+w{G(#^lGsViy+}zE@*uc@$$-vFf(ACh=#L&{!#L3yw z&D7A`#K{n**Cju>G&eP`1g19yq1OqgUQlAlEdbi=l3J8mmYU*Ll%J~r_Ow+dZns$A zG!Lpb1-Dx)aq86vIz}H9wMbD769T3m5EGtofgE_!Pt60S_ab1zE#->AX%$SLFE zvK77|9UXreSzC8=Y3m$qdeLmR!DLQlHQyh$CyPGUosmtu*w{Y3{Mpla#pi9~&1G2} zS4|30VlU)_;DC-1|&7JT^$LQ&l{<$Q6D=@awKRNgdaZ{X-(c>CviLAD3BFoB+pJm-}>$>rQ`t8R3-s``6n96jr^esZ0iYCu5;Hmn0Z)uX4CR@%Y zOWv*L9tehSn#H>;bl&SlQV9o3rQfN|`P^i>`Eb`|Z6!$q^+{fC&Djel%Qd7`KH2bZ zeNe#p!^MX` zZdcme`_q1j; literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_social_add_person.png b/res/drawable-xhdpi/ic_social_add_person.png new file mode 100644 index 0000000000000000000000000000000000000000..08d874d2575af33efd0b5074f50f30e762f33506 GIT binary patch literal 2105 zcmaJ?dsGuw9-gE;N)Q2yprL38N?3#>6Nr!yNLrvEU$RI?7Q_J?rRdls4{(~-Ezc-jk zc-#jFlIV;7GpGo@0LO-v5ROVDyNk(NLEKhPBIrq`dD7f)6f#IAffN$i%N-=ssa|w4 zi2L~9_1=_{IJ%I-{pd@7^2H}0h=NWcB_}5nlRb#AGM)s|XfzU;LZVRI^$2%WiX0KC z-Q_B$B?S(o5-X(&L<-As21QXUydUw!>m&U$1({-5R<8P3CVjz3YLS8j63K>?mVkWz z|A)$C%V-rMg#Hun{}fh*r6?eh5K_VWm12G4;+zbw6m+%{5+SfM42JhDRZ)-tBd{s~ zR^Zs7R3eDui^Nj7!SaE@=hJy|6(W+0As)vUuh$?-r4l;VOJ80Nn@ja4b3rfwqy%k4Z|Q)=sj~Fr8EhWaFwtO_aSMzbmdq$ zE9!mZN>+@89k3#oqz{HGC9Z3#9z*0sgw+XuN>=UFsda_P0Qd~JelC{qin4+t``#t${u2-7j2m>-$NT^fCu zxue=kfX}_=l3F9`IkoRt>GWdFqPA`LOxHyBOMa5yTaoU*_oUzRxxTaMP$YNjE*yaw zoPG|F57fF#E$Qj5Q+tL~?6o+fcNsg%2nl9U8Q3We!=y*sk+X?uRe;K8ZPcyHVy!xL z#G94M-dtx{s4+ccSLP65?%e*qj#(q|T3yVTvOkBK(}wtAaPR^9Q480S+S1DGG56Xg z?vyqJJp&wI5r|(nbeO(j=JlW+eFqFT&NYz*=P$-vTL)|Shpb}EB2H>s`)eFK)xyeK z!0x6UI!wT}&M2yIpL(!-(b^#`3FbAMY|Wy*-1+-Q8XDL`@B&*%2uipD=CR>O7Y_Bxp3 zl28RZb3PYhpYv{!tVpN2M!uOFczoN6(%axSboSW&;IwMTuJou?+*ccFN1;BwEVY2a1sz2_-I!HQnX zS3rZXALG1b&adcrY4g0tw=Lu4FLt50E*3iL>i$;lmKW%C2glE9Z7OQ8N35V|l6Lxp z@=n2n91Y4wS00G(J^#)nivq5dw|v@IqCy{s!Im`hzx3P-%!L zZ?ZjkGID`8RbNOAj3yUJXE0luFoVX$ye{M^kli`ry!&sL7O0r%!nAlVzN5UTZ~*&R z6H|AthjSVXt{GVP?lKV2`TS1ZVX+yD_*3?waHHyy#*3Sc6SP3&4I~2Rw`y?p@w)kgBOgx%Le zP%nEAGzf2}NUi0UPjKg(F;6fwcC~qBPw;}Kub!1RQ+2|%JMs8fXkxXq$S2RjgwO=O zcKZH@=HYjPKgtDbw*P%l@NIuyX!+!(-*SL8H`<=O8V*0Jofx{$I#m<(z{tat4p;_CuF>$nSb$%5o410s)@l3FGdZdASuw+BvF!gsz(9yJmrIl3x z-y3XSEFzrg6ufQ=A6k^qr;BHQ6;|$InpO>?yP{$lh#>QJ!@( S$ko{JGv@^abH4YF$^17Q7EYxA literal 0 HcmV?d00001 diff --git a/res/layout/listitem_contact.xml b/res/layout/listitem_contact.xml new file mode 100644 index 0000000..33e7c62 --- /dev/null +++ b/res/layout/listitem_contact.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/res/menu/address_book_list.xml b/res/menu/address_book_list.xml new file mode 100644 index 0000000..8641062 --- /dev/null +++ b/res/menu/address_book_list.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/res/menu/main.xml b/res/menu/main.xml index 3c656f8..5e3fd0d 100644 --- a/res/menu/main.xml +++ b/res/menu/main.xml @@ -1,6 +1,12 @@ + + I2P-Bote New email Send email + Address book Settings %s selected @@ -39,6 +40,9 @@ Compose email Email queued for sending + Address book is empty + New contact + General General settings and default identity settings Auto-check mail diff --git a/src/i2p/bote/android/EmailListActivity.java b/src/i2p/bote/android/EmailListActivity.java index 2a9fa54..1e3a6f4 100644 --- a/src/i2p/bote/android/EmailListActivity.java +++ b/src/i2p/bote/android/EmailListActivity.java @@ -2,6 +2,7 @@ package i2p.bote.android; import net.i2p.client.I2PClient; import i2p.bote.I2PBote; +import i2p.bote.android.addressbook.AddressBookActivity; import i2p.bote.android.config.SettingsActivity; import i2p.bote.android.util.MoveToDialogFragment; import i2p.bote.folder.EmailFolder; @@ -163,6 +164,11 @@ public class EmailListActivity extends ActionBarActivity implements } switch (item.getItemId()) { + case R.id.action_address_book: + Intent ai = new Intent(this, AddressBookActivity.class); + startActivity(ai); + return true; + case R.id.action_settings: Intent si = new Intent(this, SettingsActivity.class); startActivity(si); diff --git a/src/i2p/bote/android/addressbook/AddressBookActivity.java b/src/i2p/bote/android/addressbook/AddressBookActivity.java new file mode 100644 index 0000000..c83dfda --- /dev/null +++ b/src/i2p/bote/android/addressbook/AddressBookActivity.java @@ -0,0 +1,27 @@ +package i2p.bote.android.addressbook; + +import i2p.bote.packet.dht.Contact; +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; + +public class AddressBookActivity extends ActionBarActivity implements + AddressBookFragment.OnContactSelectedListener { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Enable ActionBar app icon to behave as action to go back + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + if (savedInstanceState == null) { + AddressBookFragment f = new AddressBookFragment(); + getSupportFragmentManager().beginTransaction() + .add(android.R.id.content, f).commit(); + } + } + + @Override + public void onContactSelected(Contact contact) { + // TODO + } +} diff --git a/src/i2p/bote/android/addressbook/AddressBookFragment.java b/src/i2p/bote/android/addressbook/AddressBookFragment.java new file mode 100644 index 0000000..f73519a --- /dev/null +++ b/src/i2p/bote/android/addressbook/AddressBookFragment.java @@ -0,0 +1,139 @@ +package i2p.bote.android.addressbook; + +import i2p.bote.I2PBote; +import i2p.bote.android.R; +import i2p.bote.android.util.BetterAsyncTaskLoader; +import i2p.bote.fileencryption.PasswordException; +import i2p.bote.packet.dht.Contact; + +import java.util.SortedSet; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.Loader; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.ListView; + +public class AddressBookFragment extends ListFragment implements + LoaderManager.LoaderCallbacks> { + OnContactSelectedListener mCallback; + private ContactAdapter mAdapter; + + // Container Activity must implement this interface + public interface OnContactSelectedListener { + public void onContactSelected(Contact contact); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + // This makes sure that the container activity has implemented + // the callback interface. If not, it throws an exception + try { + mCallback = (OnContactSelectedListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement OnContactSelectedListener"); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mAdapter = new ContactAdapter(getActivity()); + + setListAdapter(mAdapter); + + setListShown(false); + setEmptyText(getResources().getString( + R.string.address_book_empty)); + getLoaderManager().initLoader(0, null, this); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.address_book_list, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_new_contact: + // TODO + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public void onListItemClick(ListView parent, View view, int pos, long id) { + mCallback.onContactSelected(mAdapter.getItem(pos)); + } + + // LoaderManager.LoaderCallbacks> + + public Loader> onCreateLoader(int id, Bundle args) { + return new AddressBookLoader(getActivity()); + } + + private static class AddressBookLoader extends BetterAsyncTaskLoader> { + public AddressBookLoader(Context context) { + super(context); + } + + @Override + public SortedSet loadInBackground() { + SortedSet contacts = null; + try { + contacts = I2PBote.getInstance().getAddressBook().getAll(); + } catch (PasswordException e) { + // TODO handle, but should not get here + e.printStackTrace(); + } + return contacts; + } + + @Override + protected void onStartMonitoring() { + } + + @Override + protected void onStopMonitoring() { + } + + @Override + protected void releaseResources(SortedSet data) { + } + } + + @Override + public void onLoadFinished(Loader> loader, + SortedSet data) { + mAdapter.setData(data); + + if (isResumed()) { + setListShown(true); + } else { + setListShownNoAnimation(true); + } + } + + @Override + public void onLoaderReset(Loader> loader) { + mAdapter.setData(null); + } +} diff --git a/src/i2p/bote/android/addressbook/ContactAdapter.java b/src/i2p/bote/android/addressbook/ContactAdapter.java new file mode 100644 index 0000000..b0df711 --- /dev/null +++ b/src/i2p/bote/android/addressbook/ContactAdapter.java @@ -0,0 +1,50 @@ +package i2p.bote.android.addressbook; + +import i2p.bote.android.R; +import i2p.bote.android.util.BoteHelper; +import i2p.bote.packet.dht.Contact; + +import java.util.SortedSet; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +public class ContactAdapter extends ArrayAdapter { + private final LayoutInflater mInflater; + + public ContactAdapter(Context context) { + super(context, android.R.layout.simple_list_item_2); + mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + public void setData(SortedSet contacts) { + clear(); + if (contacts != null) { + for (Contact contact : contacts) { + add(contact); + } + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View v = mInflater.inflate(R.layout.listitem_contact, parent, false); + Contact contact = getItem(position); + + ImageView picture = (ImageView) v.findViewById(R.id.contact_picture); + TextView name = (TextView) v.findViewById(R.id.contact_name); + + String pic = contact.getPictureBase64(); + if (pic != null) + picture.setImageBitmap(BoteHelper.decodePicture(pic)); + + name.setText(contact.getName()); + + return v; + } +} diff --git a/src/i2p/bote/android/util/BoteHelper.java b/src/i2p/bote/android/util/BoteHelper.java index a363c4b..a97936c 100644 --- a/src/i2p/bote/android/util/BoteHelper.java +++ b/src/i2p/bote/android/util/BoteHelper.java @@ -91,8 +91,7 @@ public class BoteHelper extends GeneralHelper { // Address is in address book String pic = c.getPictureBase64(); if (pic != null) { - byte[] decodedPic = Base64.decode(pic, Base64.DEFAULT); - return BitmapFactory.decodeByteArray(decodedPic, 0, decodedPic.length); + return decodePicture(pic); } } else { // Address is an identity @@ -100,8 +99,7 @@ public class BoteHelper extends GeneralHelper { if (i != null) { String pic = i.getPictureBase64(); if (pic != null) { - byte[] decodedPic = Base64.decode(pic, Base64.DEFAULT); - return BitmapFactory.decodeByteArray(decodedPic, 0, decodedPic.length); + return decodePicture(pic); } } } @@ -110,4 +108,9 @@ public class BoteHelper extends GeneralHelper { // Address not found anywhere, or found and has no picture return null; } + + public static Bitmap decodePicture(String picB64) { + byte[] decodedPic = Base64.decode(picB64, Base64.DEFAULT); + return BitmapFactory.decodeByteArray(decodedPic, 0, decodedPic.length); + } }