From 634ab7ec38eafde2aff4477419ff06a8fa1856a0 Mon Sep 17 00:00:00 2001 From: jendib Date: Sun, 22 Nov 2015 13:31:23 +0100 Subject: [PATCH 01/13] #45: Android: Show comments --- docs-android/app/app.iml | 14 ++- docs-android/app/build.gradle | 2 +- .../docs/activity/DocumentViewActivity.java | 50 +++++++++ .../docs/adapter/CommentListAdapter.java | 102 ++++++++++++++++++ .../docs/resource/CommentResource.java | 35 ++++++ .../src/main/res/layout/comment_list_item.xml | 57 ++++++++++ .../res/layout/document_view_activity.xml | 60 +++++++++++ .../main/res/menu/document_view_activity.xml | 6 ++ .../app/src/main/res/values/strings.xml | 4 + 9 files changed, 321 insertions(+), 9 deletions(-) create mode 100644 docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java create mode 100644 docs-android/app/src/main/java/com/sismics/docs/resource/CommentResource.java create mode 100644 docs-android/app/src/main/res/layout/comment_list_item.xml diff --git a/docs-android/app/app.iml b/docs-android/app/app.iml index afd2300c..0bab8df3 100644 --- a/docs-android/app/app.iml +++ b/docs-android/app/app.iml @@ -71,12 +71,11 @@ - + - + - @@ -84,17 +83,16 @@ - - - - + - + + + diff --git a/docs-android/app/build.gradle b/docs-android/app/build.gradle index e162b17a..ae3b0aeb 100644 --- a/docs-android/app/build.gradle +++ b/docs-android/app/build.gradle @@ -3,7 +3,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.3.0' + classpath 'com.android.tools.build:gradle:1.5.0' } } apply plugin: 'com.android.application' diff --git a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java index ea2e8cbe..1e056730 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java +++ b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java @@ -31,6 +31,7 @@ import android.widget.Toast; import com.sismics.docs.R; import com.sismics.docs.adapter.AclListAdapter; +import com.sismics.docs.adapter.CommentListAdapter; import com.sismics.docs.adapter.FilePagerAdapter; import com.sismics.docs.event.DocumentDeleteEvent; import com.sismics.docs.event.DocumentEditEvent; @@ -40,6 +41,7 @@ import com.sismics.docs.event.FileDeleteEvent; import com.sismics.docs.fragment.DocShareFragment; import com.sismics.docs.listener.JsonHttpResponseHandler; import com.sismics.docs.model.application.ApplicationContext; +import com.sismics.docs.resource.CommentResource; import com.sismics.docs.resource.DocumentResource; import com.sismics.docs.resource.FileResource; import com.sismics.docs.service.FileUploadService; @@ -241,6 +243,9 @@ public class DocumentViewActivity extends AppCompatActivity { } }); + // Grab the comments + updateComments(); + // Grab the attached files updateFiles(); @@ -268,6 +273,15 @@ public class DocumentViewActivity extends AppCompatActivity { } return true; + case R.id.comments: + drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + if (drawerLayout.isDrawerVisible(GravityCompat.START)) { + drawerLayout.closeDrawer(GravityCompat.START); + } else { + drawerLayout.openDrawer(GravityCompat.START); + } + return true; + case R.id.download_file: downloadCurrentFile(); return true; @@ -572,6 +586,42 @@ public class DocumentViewActivity extends AppCompatActivity { }); } + /** + * Refresh comments list. + */ + private void updateComments() { + if (document == null) return; + + final View progressBar = findViewById(R.id.commentProgressView); + final TextView emptyView = (TextView) findViewById(R.id.commentEmptyView); + final ListView listView = (ListView) findViewById(R.id.commentListView); + progressBar.setVisibility(View.VISIBLE); + emptyView.setVisibility(View.GONE); + listView.setVisibility(View.GONE); + + CommentResource.list(this, document.optString("id"), new JsonHttpResponseHandler() { + @Override + public void onSuccess(int statusCode, Header[] headers, JSONObject response) { + JSONArray comments = response.optJSONArray("comments"); + listView.setAdapter(new CommentListAdapter(comments)); + listView.setVisibility(View.VISIBLE); + progressBar.setVisibility(View.GONE); + if (comments.length() == 0) { + listView.setVisibility(View.GONE); + emptyView.setVisibility(View.VISIBLE); + } + } + + @Override + public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) { + emptyView.setText(R.string.error_loading_comments); + progressBar.setVisibility(View.GONE); + listView.setVisibility(View.GONE); + emptyView.setVisibility(View.VISIBLE); + } + }); + } + /** * Refresh files list. */ diff --git a/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java b/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java new file mode 100644 index 00000000..7ef0384d --- /dev/null +++ b/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java @@ -0,0 +1,102 @@ +package com.sismics.docs.adapter; + +import android.content.Context; +import android.graphics.Bitmap; +import android.text.format.DateFormat; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import com.androidquery.AQuery; +import com.androidquery.callback.BitmapAjaxCallback; +import com.sismics.docs.R; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Comment list adapter. + * + * @author bgamard. + */ +public class CommentListAdapter extends BaseAdapter { + /** + * AQuery. + */ + private AQuery aq; + + /** + * Tags. + */ + private List commentList = new ArrayList<>(); + + /** + * Comment list adapter. + * + * @param commentsArray Comments + */ + public CommentListAdapter(JSONArray commentsArray) { + for (int i = 0; i < commentsArray.length(); i++) { + commentList.add(commentsArray.optJSONObject(i)); + } + } + + @Override + public int getCount() { + return commentList.size(); + } + + @Override + public JSONObject getItem(int position) { + return commentList.get(position); + } + + @Override + public long getItemId(int position) { + return getItem(position).optString("id").hashCode(); + } + + @Override + public View getView(int position, View view, ViewGroup parent) { + if (view == null) { + LayoutInflater vi = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = vi.inflate(R.layout.comment_list_item, parent, false); + } + + if (aq == null) { + aq = new AQuery(view); + } else { + aq.recycle(view); + } + + // Fill the view + JSONObject comment = getItem(position); + TextView creatorTextView = (TextView) view.findViewById(R.id.creatorTextView); + TextView dateTextView = (TextView) view.findViewById(R.id.dateTextView); + TextView contentTextView = (TextView) view.findViewById(R.id.contentTextView); + ImageView gravatarImageView = (ImageView) view.findViewById(R.id.gravatarImageView); + creatorTextView.setText(comment.optString("creator")); + dateTextView.setText(DateFormat.getDateFormat(dateTextView.getContext()).format(new Date(comment.optLong("create_date")))); + contentTextView.setText(comment.optString("content")); + + // Gravatar image + String gravatarUrl = "http://www.gravatar.com/avatar/" + comment.optString("creator_gravatar") + "?s=128d=identicon"; + if (aq.shouldDelay(position, view, parent, gravatarUrl)) { + aq.id(gravatarImageView).image((Bitmap) null); + } else { + aq.id(gravatarImageView).image(new BitmapAjaxCallback() + .url(gravatarUrl) + .animation(AQuery.FADE_IN_NETWORK) + ); + } + + return view; + } +} diff --git a/docs-android/app/src/main/java/com/sismics/docs/resource/CommentResource.java b/docs-android/app/src/main/java/com/sismics/docs/resource/CommentResource.java new file mode 100644 index 00000000..cf7bec0b --- /dev/null +++ b/docs-android/app/src/main/java/com/sismics/docs/resource/CommentResource.java @@ -0,0 +1,35 @@ +package com.sismics.docs.resource; + +import android.content.Context; + +import com.sismics.docs.listener.JsonHttpResponseHandler; + + +/** + * Access to /comment API. + * + * @author bgamard + */ +public class CommentResource extends BaseResource { + /** + * GET /comment/id. + * + * @param context Context + * @param documentId Document ID + * @param responseHandler Callback + */ + public static void list(Context context, String documentId, JsonHttpResponseHandler responseHandler) { + init(context); + + client.get(getApiUrl(context) + "/comment/" + documentId, responseHandler); + } + + /** + * Cancel pending requests. + * + * @param context Context + */ + public static void cancel(Context context) { + client.cancelRequests(context, true); + } +} diff --git a/docs-android/app/src/main/res/layout/comment_list_item.xml b/docs-android/app/src/main/res/layout/comment_list_item.xml new file mode 100644 index 00000000..7396aac6 --- /dev/null +++ b/docs-android/app/src/main/res/layout/comment_list_item.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/docs-android/app/src/main/res/layout/document_view_activity.xml b/docs-android/app/src/main/res/layout/document_view_activity.xml index 19744f59..7a0d3cf9 100644 --- a/docs-android/app/src/main/res/layout/document_view_activity.xml +++ b/docs-android/app/src/main/res/layout/document_view_activity.xml @@ -37,6 +37,66 @@ + + + + + + + + + + + + + + + + + + + + + + + All languages Toggle informations Who can access + Show comments + Comments + No comments + Error loading comments From 60ee000b6ccbafa681b95d0ffc6e55eaa9a5ef48 Mon Sep 17 00:00:00 2001 From: jendib Date: Sun, 22 Nov 2015 20:32:26 +0100 Subject: [PATCH 02/13] #45: Android: Add comments --- .../docs/activity/DocumentViewActivity.java | 35 ++++++++++++++ .../docs/resource/CommentResource.java | 18 +++++++ .../drawable-xhdpi/ic_comment_black_24dp.png | Bin 0 -> 296 bytes .../drawable-xhdpi/ic_send_grey600_24dp.png | Bin 0 -> 448 bytes .../drawable-xxhdpi/ic_comment_black_24dp.png | Bin 0 -> 359 bytes .../drawable-xxhdpi/ic_send_grey600_24dp.png | Bin 0 -> 565 bytes .../src/main/res/layout/comment_list_item.xml | 2 +- .../res/layout/document_view_activity.xml | 44 +++++++++++++++++- .../main/res/menu/document_view_activity.xml | 2 +- .../app/src/main/res/values/strings.xml | 5 +- 10 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 docs-android/app/src/main/res/drawable-xhdpi/ic_comment_black_24dp.png create mode 100644 docs-android/app/src/main/res/drawable-xhdpi/ic_send_grey600_24dp.png create mode 100644 docs-android/app/src/main/res/drawable-xxhdpi/ic_comment_black_24dp.png create mode 100644 docs-android/app/src/main/res/drawable-xxhdpi/ic_send_grey600_24dp.png diff --git a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java index 1e056730..5076978c 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java +++ b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java @@ -24,6 +24,8 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; @@ -243,6 +245,39 @@ public class DocumentViewActivity extends AppCompatActivity { } }); + // TODO Delete comment button + + ImageButton imageButton = (ImageButton) findViewById(R.id.addCommentBtn); + imageButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + final EditText commentEditText = (EditText) findViewById(R.id.commentEditText); + if (commentEditText.getText().length() == 0) { + // No content for the new comment + return; + } + + Toast.makeText(DocumentViewActivity.this, R.string.adding_comment, Toast.LENGTH_LONG).show(); + + CommentResource.add(DocumentViewActivity.this, + DocumentViewActivity.this.document.optString("id"), + commentEditText.getText().toString(), + new JsonHttpResponseHandler() { + public void onSuccess(int statusCode, Header[] headers, JSONObject response) { + // TODO Send a new comment event and update the adapter properly + // if there is no adapter yet (comments not loaded), do nothing + commentEditText.setText(""); + updateComments(); + } + + @Override + public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) { + Toast.makeText(DocumentViewActivity.this, R.string.comment_add_failure, Toast.LENGTH_LONG).show(); + } + }); + } + }); + // Grab the comments updateComments(); diff --git a/docs-android/app/src/main/java/com/sismics/docs/resource/CommentResource.java b/docs-android/app/src/main/java/com/sismics/docs/resource/CommentResource.java index cf7bec0b..ca330ee1 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/resource/CommentResource.java +++ b/docs-android/app/src/main/java/com/sismics/docs/resource/CommentResource.java @@ -2,6 +2,7 @@ package com.sismics.docs.resource; import android.content.Context; +import com.loopj.android.http.RequestParams; import com.sismics.docs.listener.JsonHttpResponseHandler; @@ -24,6 +25,23 @@ public class CommentResource extends BaseResource { client.get(getApiUrl(context) + "/comment/" + documentId, responseHandler); } + /** + * PUT /comment. + * + * @param context Context + * @param documentId Document ID + * @param content Comment content + * @param responseHandler Callback + */ + public static void add(Context context, String documentId, String content, JsonHttpResponseHandler responseHandler) { + init(context); + + RequestParams params = new RequestParams(); + params.put("id", documentId); + params.put("content", content); + client.put(getApiUrl(context) + "/comment", params, responseHandler); + } + /** * Cancel pending requests. * diff --git a/docs-android/app/src/main/res/drawable-xhdpi/ic_comment_black_24dp.png b/docs-android/app/src/main/res/drawable-xhdpi/ic_comment_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..412de9b052754c39e9092a17cef71d680f8558f7 GIT binary patch literal 296 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}tg=CK)Uj~LMH3o);76yi2K%s^g z3=E|}g|8AA7_4S6Fo+k-*%fF5lweBoc6VX;-`;;_Kaj^+;1OBOz`!jG!i)^F=12eq z7kIijhD02Gd&8Tn#es+A!mgedx?gS9UCHKDdX@BN+v*LQKVK#s6vQ6X{$$Bg+QzZW)r12K}oUC{@4Tk;C&L8-};t*zKurWNP zflcAH41+@S#lMZJ3=Uz;db@ueIKZZ5w&3RvTh;~^)hgA5%l~6javscHz%ZYM(}F{) lKp;UlLHNm%GEok7>nArq`YfE_wE*Z=22WQ%mvv4FO#lgHWsd*= literal 0 HcmV?d00001 diff --git a/docs-android/app/src/main/res/drawable-xhdpi/ic_send_grey600_24dp.png b/docs-android/app/src/main/res/drawable-xhdpi/ic_send_grey600_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..e138046223025f5a9422590d78a47b3d4984a4c5 GIT binary patch literal 448 zcmV;x0YCnUP)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00002VoOIv0RM-N%)bBt010qN zS#tmY3ljhU3ljkVnw%H_00B5jL_t(o!|j*RZNe}RKwl68lmVbpnUO_o37l?F!W@Ot zK}uE#0{Ri95J<3nw!fOoXQDj&*}gkx3(`h50R9tT%aRYObbu2hO5R!VlMVoIWP@g# z4p7H8I*za_;7oS+t|Z5V3VQ}kcOb_!!ekZAKEH}h zEldh{Z2?|700oM@kD~d_52qGI2199EWZM|~7Fn_8*u72N$JiBrQc@hW*1n0KDilSU zR8di&AJ1+az!NFq089Q1E18`MEl$yQmLdUoN%3vGyruZGj)pv5>v-S%DybBE`|i*y q$S)uf`sqHvC(AfehHLBqh#kMS#iSv<*6SGn0000I(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9;eUJonf*W>XMsm#F#`j)FbFd;%$g$s z6uj^0;uw-~@9j-*z9s_+*N4V|-xQ=voKo0#nY+6rU1+sXH2kq9IB-JamcO3wc1r)a zWb$&sf+<{g=kzp5J+I_dwfwB;=k;{%r+r;V-(TiDU1;&`%rD2tg1*BW->{1YI^3=c zkYz1-#lBM`n?=ZD!Nq$gI~(+vI7Q^1lr!F@62Ee^e=0hzp0qM21?avK$%CUCQGYM0GnZkqS;-y^eY4tJNVSfCe_a=~2F z>c!*~hAV0_8Mb;xC@1!|m004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00002VoOIv0RM-N%)bBt010qN zS#tmY3ljhU3ljkVnw%H_00FH@L_t(&-tF02Zh}w{fZ^Z91=I!9ChZ!#5l9regNo~@ ztuCap1xzmwgjACP=Q1;r=HRO)U*-ql9A-~U>zE}_0wquaO$xMQ#S6`JK%d-D^URu$ z>3{%EY{}V82h<)1a{GWoAb^^Z!ZP3(s0*0!b`8)Mv)*qS1#|})1@r?M1oQ_f2Mh(N z1-t}(d;eZMU@S;HpbV%BxaK$=P#dr!j|(UZYL7iRo6vw7ps0Xapx}U-prC;IAkTm{ zpe|s>yB}!5!`Wxa+&7t9I|8c6*h?pSJOkW$Br7PWB~EIe8T6gh85>UFmwng_Mk4wAF~-lp z;kp{Wb;!-2KT_p>>(IQp_C~q}XfTrf1KLO=J5WPKKK=w`1M-Muu?+HxqzMX&qy-9& zqydUrt4eRjIISgYHmr`6l)4*M?jXTPu^{nCF`$}4K2j*?K&BIE2-Np#K{b*e$RLtC z$S9HvNMQ3)4rKm@YzXwq%080R?neS8Py!{;KLz~&$HDh8V7w~v00000NkvXXu0mjf D^l|4S literal 0 HcmV?d00001 diff --git a/docs-android/app/src/main/res/layout/comment_list_item.xml b/docs-android/app/src/main/res/layout/comment_list_item.xml index 7396aac6..eb444391 100644 --- a/docs-android/app/src/main/res/layout/comment_list_item.xml +++ b/docs-android/app/src/main/res/layout/comment_list_item.xml @@ -1,7 +1,7 @@ diff --git a/docs-android/app/src/main/res/layout/document_view_activity.xml b/docs-android/app/src/main/res/layout/document_view_activity.xml index 7a0d3cf9..b437c238 100644 --- a/docs-android/app/src/main/res/layout/document_view_activity.xml +++ b/docs-android/app/src/main/res/layout/document_view_activity.xml @@ -52,13 +52,22 @@ + + + + + + + + + + + + diff --git a/docs-android/app/src/main/res/menu/document_view_activity.xml b/docs-android/app/src/main/res/menu/document_view_activity.xml index bcf880e0..66ca28bf 100644 --- a/docs-android/app/src/main/res/menu/document_view_activity.xml +++ b/docs-android/app/src/main/res/menu/document_view_activity.xml @@ -12,7 +12,7 @@ + android:title="@string/comments"> All languages Toggle informations Who can access - Show comments Comments No comments Error loading comments + Send + Add a comment + Error adding a comment + Adding a comment... From 978fbf2cf9fcd52ff7207fed3a750ab1b897be35 Mon Sep 17 00:00:00 2001 From: jendib Date: Sun, 22 Nov 2015 23:59:21 +0100 Subject: [PATCH 03/13] Closes #45: Android: Delete comments --- .../com/sismics/docs/MainApplication.java | 2 +- .../docs/activity/DocumentViewActivity.java | 95 +++++++++++++++++-- .../docs/adapter/CommentListAdapter.java | 25 +++++ .../sismics/docs/event/CommentAddEvent.java | 33 +++++++ .../docs/event/CommentDeleteEvent.java | 31 ++++++ .../docs/resource/CommentResource.java | 13 +++ .../res/layout/document_view_activity.xml | 1 + .../app/src/main/res/values/strings.xml | 5 +- 8 files changed, 197 insertions(+), 8 deletions(-) create mode 100644 docs-android/app/src/main/java/com/sismics/docs/event/CommentAddEvent.java create mode 100644 docs-android/app/src/main/java/com/sismics/docs/event/CommentDeleteEvent.java diff --git a/docs-android/app/src/main/java/com/sismics/docs/MainApplication.java b/docs-android/app/src/main/java/com/sismics/docs/MainApplication.java index 340abdbf..6d636c78 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/MainApplication.java +++ b/docs-android/app/src/main/java/com/sismics/docs/MainApplication.java @@ -20,7 +20,6 @@ public class MainApplication extends Application { JSONObject json = PreferenceUtil.getCachedJson(getApplicationContext(), PreferenceUtil.PREF_CACHED_USER_INFO_JSON); ApplicationContext.getInstance().setUserInfo(getApplicationContext(), json); - // TODO google docs app: right drawer with all actions, with acls, with deep metadatas // TODO Provide documents to intent action get content super.onCreate(); @@ -28,6 +27,7 @@ public class MainApplication extends Application { @Override public void onLowMemory() { + super.onLowMemory(); BitmapAjaxCallback.clearCache(); } } diff --git a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java index 5076978c..d6bc8f12 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java +++ b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java @@ -19,10 +19,12 @@ import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.text.format.DateFormat; +import android.view.ContextMenu; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import android.widget.AdapterView; import android.widget.Button; import android.widget.EditText; import android.widget.ImageButton; @@ -35,6 +37,8 @@ import com.sismics.docs.R; import com.sismics.docs.adapter.AclListAdapter; import com.sismics.docs.adapter.CommentListAdapter; import com.sismics.docs.adapter.FilePagerAdapter; +import com.sismics.docs.event.CommentAddEvent; +import com.sismics.docs.event.CommentDeleteEvent; import com.sismics.docs.event.DocumentDeleteEvent; import com.sismics.docs.event.DocumentEditEvent; import com.sismics.docs.event.DocumentFullscreenEvent; @@ -87,6 +91,11 @@ public class DocumentViewActivity extends AppCompatActivity { */ private FilePagerAdapter filePagerAdapter; + /** + * Comment list adapter. + */ + private CommentListAdapter commentListAdapter; + /** * Document displayed. */ @@ -245,8 +254,7 @@ public class DocumentViewActivity extends AppCompatActivity { } }); - // TODO Delete comment button - + // Button add a comment ImageButton imageButton = (ImageButton) findViewById(R.id.addCommentBtn); imageButton.setOnClickListener(new View.OnClickListener() { @Override @@ -264,10 +272,8 @@ public class DocumentViewActivity extends AppCompatActivity { commentEditText.getText().toString(), new JsonHttpResponseHandler() { public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - // TODO Send a new comment event and update the adapter properly - // if there is no adapter yet (comments not loaded), do nothing + EventBus.getDefault().post(new CommentAddEvent(response)); commentEditText.setText(""); - updateComments(); } @Override @@ -556,6 +562,36 @@ public class DocumentViewActivity extends AppCompatActivity { } } + /** + * A comment add event has been fired. + * + * @param event Comment add event + */ + public void onEventMainThread(CommentAddEvent event) { + if (commentListAdapter == null) return; + TextView emptyView = (TextView) findViewById(R.id.commentEmptyView); + ListView listView = (ListView) findViewById(R.id.commentListView); + emptyView.setVisibility(View.GONE); + listView.setVisibility(View.VISIBLE); + commentListAdapter.add(event.getComment()); + } + + /** + * A comment delete event has been fired. + * + * @param event Comment add event + */ + public void onEventMainThread(CommentDeleteEvent event) { + if (commentListAdapter == null) return; + TextView emptyView = (TextView) findViewById(R.id.commentEmptyView); + ListView listView = (ListView) findViewById(R.id.commentListView); + commentListAdapter.remove(event.getCommentId()); + if (commentListAdapter.getCount() == 0) { + emptyView.setVisibility(View.VISIBLE); + listView.setVisibility(View.GONE); + } + } + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (document == null) return; @@ -621,6 +657,51 @@ public class DocumentViewActivity extends AppCompatActivity { }); } + @Override + public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { + switch (view.getId()) { + case R.id.commentListView: + if (commentListAdapter == null || document == null) return; + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; + JSONObject comment = commentListAdapter.getItem(info.position); + boolean writable = document.optBoolean("writable"); + String creator = comment.optString("creator"); + String username = ApplicationContext.getInstance().getUserInfo().optString("username"); + if (writable || creator.equals(username)) { + menu.add(Menu.NONE, 0, 0, getString(R.string.comment_delete)); + } + } + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + // Use real ids if more than one item someday + if (item.getItemId() == 0) { + // Delete a comment + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo(); + if (commentListAdapter == null) return false; + JSONObject comment = commentListAdapter.getItem(info.position); + final String commentId = comment.optString("id"); + Toast.makeText(DocumentViewActivity.this, R.string.deleting_comment, Toast.LENGTH_LONG).show(); + + CommentResource.remove(DocumentViewActivity.this, commentId, new JsonHttpResponseHandler() { + @Override + public void onSuccess(int statusCode, Header[] headers, JSONObject response) { + EventBus.getDefault().post(new CommentDeleteEvent(commentId)); + } + + @Override + public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) { + Toast.makeText(DocumentViewActivity.this, R.string.error_deleting_comment, Toast.LENGTH_LONG).show(); + } + }); + + return true; + } + + return false; + } + /** * Refresh comments list. */ @@ -633,12 +714,14 @@ public class DocumentViewActivity extends AppCompatActivity { progressBar.setVisibility(View.VISIBLE); emptyView.setVisibility(View.GONE); listView.setVisibility(View.GONE); + registerForContextMenu(listView); CommentResource.list(this, document.optString("id"), new JsonHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { JSONArray comments = response.optJSONArray("comments"); - listView.setAdapter(new CommentListAdapter(comments)); + commentListAdapter = new CommentListAdapter(comments); + listView.setAdapter(commentListAdapter); listView.setVisibility(View.VISIBLE); progressBar.setVisibility(View.GONE); if (comments.length() == 0) { diff --git a/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java b/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java index 7ef0384d..3a11585c 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java +++ b/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java @@ -99,4 +99,29 @@ public class CommentListAdapter extends BaseAdapter { return view; } + + /** + * Add a new comment. + * + * @param comment Comment + */ + public void add(JSONObject comment) { + commentList.add(comment); + notifyDataSetChanged(); + } + + /** + * Remove a comment. + * + * @param commentId Comment ID + */ + public void remove(String commentId) { + for (JSONObject comment : commentList) { + if (comment.optString("id").equals(commentId)) { + commentList.remove(comment); + notifyDataSetChanged(); + return; + } + } + } } diff --git a/docs-android/app/src/main/java/com/sismics/docs/event/CommentAddEvent.java b/docs-android/app/src/main/java/com/sismics/docs/event/CommentAddEvent.java new file mode 100644 index 00000000..374fda6d --- /dev/null +++ b/docs-android/app/src/main/java/com/sismics/docs/event/CommentAddEvent.java @@ -0,0 +1,33 @@ +package com.sismics.docs.event; + +import org.json.JSONObject; + +/** + * Comment add event. + * + * @author bgamard. + */ +public class CommentAddEvent { + /** + * Comment. + */ + private JSONObject comment; + + /** + * Create a comment add event. + * + * @param comment Comment + */ + public CommentAddEvent(JSONObject comment) { + this.comment = comment; + } + + /** + * Getter of comment. + * + * @return comment + */ + public JSONObject getComment() { + return comment; + } +} diff --git a/docs-android/app/src/main/java/com/sismics/docs/event/CommentDeleteEvent.java b/docs-android/app/src/main/java/com/sismics/docs/event/CommentDeleteEvent.java new file mode 100644 index 00000000..4d25863d --- /dev/null +++ b/docs-android/app/src/main/java/com/sismics/docs/event/CommentDeleteEvent.java @@ -0,0 +1,31 @@ +package com.sismics.docs.event; + +/** + * Comment delete event. + * + * @author bgamard. + */ +public class CommentDeleteEvent { + /** + * Comment ID. + */ + private String commentId; + + /** + * Create a comment add event. + * + * @param commentId Comment ID + */ + public CommentDeleteEvent(String commentId) { + this.commentId = commentId; + } + + /** + * Getter of commentId. + * + * @return commentId + */ + public String getCommentId() { + return commentId; + } +} diff --git a/docs-android/app/src/main/java/com/sismics/docs/resource/CommentResource.java b/docs-android/app/src/main/java/com/sismics/docs/resource/CommentResource.java index ca330ee1..6e545956 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/resource/CommentResource.java +++ b/docs-android/app/src/main/java/com/sismics/docs/resource/CommentResource.java @@ -42,6 +42,19 @@ public class CommentResource extends BaseResource { client.put(getApiUrl(context) + "/comment", params, responseHandler); } + /** + * DELETE /comment/id. + * + * @param context Context + * @param commentId Comment ID + * @param responseHandler Callback + */ + public static void remove(Context context, String commentId, JsonHttpResponseHandler responseHandler) { + init(context); + + client.delete(getApiUrl(context) + "/comment/" + commentId, responseHandler); + } + /** * Cancel pending requests. * diff --git a/docs-android/app/src/main/res/layout/document_view_activity.xml b/docs-android/app/src/main/res/layout/document_view_activity.xml index b437c238..a0609e89 100644 --- a/docs-android/app/src/main/res/layout/document_view_activity.xml +++ b/docs-android/app/src/main/res/layout/document_view_activity.xml @@ -75,6 +75,7 @@ android:layout_height="0dp" android:choiceMode="singleChoice" android:divider="@android:color/transparent" + android:transcriptMode="normal" android:dividerHeight="0dp"/> Send Add a comment Error adding a comment - Adding a comment... + Adding a comment + Delete comment + Deleting comment + Error deleting comment From 2948c0c860d4fd6bdde0e15e16a077a8e59283d3 Mon Sep 17 00:00:00 2001 From: jendib Date: Mon, 23 Nov 2015 00:01:39 +0100 Subject: [PATCH 04/13] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 15ebe71a..9caba2a6 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ Features - Tag system with relations - Multi-users ACL system - Audit log +- Comments - Document sharing by URL - RESTful Web API - Fully featured Android client From dd671795e6f2e2cc6f5af8c0f0cde063fe80b8f9 Mon Sep 17 00:00:00 2001 From: jendib Date: Mon, 23 Nov 2015 20:32:32 +0100 Subject: [PATCH 05/13] Android Studio 2 + small colors --- docs-android/app/app.iml | 9 ++++++++- docs-android/app/build.gradle | 6 +++--- docs-android/gradle/wrapper/gradle-wrapper.properties | 4 ++-- docs-web/src/main/webapp/src/index.html | 2 +- .../main/webapp/src/partial/docs/document.default.html | 6 +++--- docs-web/src/main/webapp/src/style/main.less | 5 +++++ 6 files changed, 22 insertions(+), 10 deletions(-) diff --git a/docs-android/app/app.iml b/docs-android/app/app.iml index 0bab8df3..704ef345 100644 --- a/docs-android/app/app.iml +++ b/docs-android/app/app.iml @@ -83,12 +83,19 @@ + + + + + + + @@ -110,12 +117,12 @@ - + \ No newline at end of file diff --git a/docs-android/app/build.gradle b/docs-android/app/build.gradle index ae3b0aeb..827a75b4 100644 --- a/docs-android/app/build.gradle +++ b/docs-android/app/build.gradle @@ -3,7 +3,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.5.0' + classpath 'com.android.tools.build:gradle:2.0.0-alpha1' } } apply plugin: 'com.android.application' @@ -14,7 +14,7 @@ repositories { android { compileSdkVersion 22 - buildToolsVersion "23.0.2" + buildToolsVersion '23.0.2' defaultConfig { minSdkVersion 14 @@ -54,6 +54,6 @@ dependencies { compile 'com.android.support:recyclerview-v7:22.+' compile 'com.loopj.android:android-async-http:1.4.6' compile 'it.sephiroth.android.library.imagezoom:imagezoom:1.0.5' - compile 'de.greenrobot:eventbus:2.4.0' + compile 'de.greenrobot:eventbus:2.4.1' compile 'com.shamanland:fab:0.0.6' } diff --git a/docs-android/gradle/wrapper/gradle-wrapper.properties b/docs-android/gradle/wrapper/gradle-wrapper.properties index 695bbdaa..476556cf 100644 --- a/docs-android/gradle/wrapper/gradle-wrapper.properties +++ b/docs-android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Nov 26 21:58:48 CET 2014 +#Mon Nov 23 20:12:30 CET 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip diff --git a/docs-web/src/main/webapp/src/index.html b/docs-web/src/main/webapp/src/index.html index 9fb47b64..dc74d15c 100644 --- a/docs-web/src/main/webapp/src/index.html +++ b/docs-web/src/main/webapp/src/index.html @@ -71,7 +71,7 @@ -