Skip to content
This repository was archived by the owner on Jul 25, 2024. It is now read-only.

Commit 016cbe3

Browse files
yadav-rahultimabbott
authored andcommitted
Add edit message feature.
Fixes #290.
1 parent f46f968 commit 016cbe3

File tree

15 files changed

+268
-2
lines changed

15 files changed

+268
-2
lines changed

app/src/main/java/com/zulip/android/ZulipApp.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.zulip.android.networking.response.UserConfigurationResponse;
3838
import com.zulip.android.networking.response.events.EventsBranch;
3939
import com.zulip.android.service.ZulipServices;
40+
import com.zulip.android.util.Constants;
4041
import com.zulip.android.util.GoogleAuthHelper;
4142
import com.zulip.android.util.ZLog;
4243

@@ -532,4 +533,36 @@ public void onFailure(Call<ResponseBody> call, Throwable t) {
532533
});
533534
}
534535

536+
/**
537+
* Sets message content editing parameters
538+
*
539+
* @param seconds time limit in seconds for editing message
540+
* @param param parameter indicating editing message is allowed or not
541+
*/
542+
public void setMessageContentEditParams(int seconds, boolean param) {
543+
SharedPreferences preferences = getSettings();
544+
SharedPreferences.Editor editor = preferences.edit();
545+
546+
//Firsts Checks if maximum content edit limit is already saved or not
547+
if (!preferences.getBoolean(Constants.IS_CONTENT_EDIT_PARAM_SAVED, false)) {
548+
editor.putBoolean(Constants.IS_EDITING_ALLOWED, param);
549+
editor.putInt(Constants.MAXIMUM_CONTENT_EDIT_LIMIT, seconds);
550+
editor.putBoolean(Constants.IS_CONTENT_EDIT_PARAM_SAVED, true);
551+
editor.apply();
552+
} else {
553+
//Check if any value is changed from server
554+
if (getSettings().getInt(Constants.MAXIMUM_CONTENT_EDIT_LIMIT,
555+
Constants.DEFAULT_MAXIMUM_CONTENT_EDIT_LIMIT) != seconds) {
556+
//time is changed from server and update it locally too
557+
editor.putInt(Constants.MAXIMUM_CONTENT_EDIT_LIMIT, seconds);
558+
editor.apply();
559+
}
560+
if (getSettings().getBoolean(Constants.IS_EDITING_ALLOWED,
561+
Constants.DEFAULT_EDITING_ALLOWED) != param) {
562+
//isEditingAllowed is changed from server and update it locally too
563+
editor.putBoolean(Constants.IS_EDITING_ALLOWED, param);
564+
editor.apply();
565+
}
566+
}
567+
}
535568
}

app/src/main/java/com/zulip/android/activities/MessageListFragment.java

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
import android.annotation.TargetApi;
44
import android.app.Activity;
5+
import android.app.ProgressDialog;
56
import android.content.ClipData;
67
import android.content.Context;
8+
import android.content.DialogInterface;
79
import android.os.Build;
810
import android.os.Bundle;
911
import android.support.v4.app.Fragment;
12+
import android.support.v7.app.AlertDialog;
1013
import android.support.v7.app.AppCompatActivity;
1114
import android.support.v7.widget.LinearLayoutManager;
1215
import android.support.v7.widget.RecyclerView;
@@ -16,6 +19,9 @@
1619
import android.view.MenuItem;
1720
import android.view.View;
1821
import android.view.ViewGroup;
22+
import android.view.WindowManager;
23+
import android.view.inputmethod.InputMethodManager;
24+
import android.widget.EditText;
1925
import android.widget.TextView;
2026
import android.widget.Toast;
2127

@@ -32,8 +38,11 @@
3238
import com.zulip.android.models.Stream;
3339
import com.zulip.android.networking.AsyncGetOldMessages;
3440
import com.zulip.android.networking.ZulipAsyncPushTask;
41+
import com.zulip.android.networking.response.EditResponse;
42+
import com.zulip.android.util.Constants;
3543
import com.zulip.android.util.MessageListener;
3644
import com.zulip.android.util.MutedTopics;
45+
import com.zulip.android.util.ZLog;
3746
import com.zulip.android.viewholders.HeaderSpaceItemDecoration;
3847

3948
import org.json.JSONObject;
@@ -43,6 +52,10 @@
4352
import java.util.Arrays;
4453
import java.util.List;
4554

55+
import retrofit2.Call;
56+
import retrofit2.Callback;
57+
import retrofit2.Response;
58+
4659
/**
4760
* This is a Fragment which holds the recyclerView for displaying the messages
4861
* initiated and called by {@link ZulipActivity}
@@ -208,6 +221,9 @@ public boolean onContextItemSelected(MenuItem item) {
208221
copyMessage(message);
209222
Toast.makeText(getContext(), R.string.message_copied, Toast.LENGTH_SHORT).show();
210223
return true;
224+
case R.id.edit_message:
225+
editMessage(message, adapter.getContextMenuItemSelectedPosition());
226+
return true;
211227
default:
212228
return super.onContextItemSelected(item);
213229
}
@@ -351,6 +367,89 @@ private void selectPointer() {
351367
}
352368
}
353369

370+
/**
371+
* Edit a message passed as parameter
372+
*
373+
* @param message Message to be edited
374+
*/
375+
private void editMessage(final Message message, final int position) {
376+
boolean isEditingAllowed = app.
377+
getSettings().
378+
getBoolean(Constants.IS_EDITING_ALLOWED, Constants.DEFAULT_EDITING_ALLOWED);
379+
if (!isEditingAllowed) {
380+
Toast.makeText(getContext(), R.string.editing_message_disabled, Toast.LENGTH_SHORT).show();
381+
return;
382+
}
383+
int maxMessageContentEditLimit = app.
384+
getSettings().
385+
getInt(Constants.MAXIMUM_CONTENT_EDIT_LIMIT, Constants.DEFAULT_MAXIMUM_CONTENT_EDIT_LIMIT);
386+
int timeSinceMessageSend = (int) ((System.currentTimeMillis() - message.getTimestamp().getTime()) / 1000);
387+
if (timeSinceMessageSend > maxMessageContentEditLimit) {
388+
Toast.makeText(getContext(), R.string.maximum_time_limit_error, Toast.LENGTH_SHORT).show();
389+
} else {
390+
final View dialogView = View.inflate(getContext(),
391+
R.layout.message_edit_dialog, null);
392+
//Pop up a dialog box with previous message content
393+
final AlertDialog dialog = new AlertDialog.Builder(getContext())
394+
.setTitle(R.string.edit_message)
395+
.setView(dialogView)
396+
.setPositiveButton(android.R.string.ok, null)
397+
.setNegativeButton(android.R.string.cancel, null)
398+
.create();
399+
dialog.getWindow().setSoftInputMode(
400+
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
401+
dialog.show();
402+
403+
final EditText dialogMessageEditText = (EditText) dialogView.findViewById(R.id.message_content);
404+
dialogMessageEditText.setText(message.getContent().trim());
405+
406+
//Move cursor to end of text
407+
dialogMessageEditText.setSelection(dialogMessageEditText.getText().length());
408+
409+
//OK button listener
410+
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
411+
//Show edit option only on if current user send it.
412+
@Override
413+
public void onClick(View view) {
414+
dialog.cancel();
415+
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
416+
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
417+
//Start a progress dialog indicating editing message
418+
final ProgressDialog progress = new ProgressDialog(getActivity());
419+
progress.setCancelable(false);
420+
progress.setMessage(app.getString(R.string.editing_message));
421+
progress.show();
422+
final String editedMessageContent = dialogMessageEditText.getText().toString();
423+
424+
app.getZulipServices()
425+
.editMessage(String.valueOf(message.getID()), editedMessageContent)
426+
.enqueue(new Callback<EditResponse>() {
427+
@Override
428+
public void onResponse(Call<EditResponse> call, Response<EditResponse> response) {
429+
if (response.isSuccessful()) {
430+
message.setContent(editedMessageContent);
431+
message.setFormattedContent(editedMessageContent);
432+
adapter.notifyItemChanged(position);
433+
progress.dismiss();
434+
Toast.makeText(getActivity(), R.string.message_edited, Toast.LENGTH_SHORT).show();
435+
} else {
436+
progress.dismiss();
437+
Toast.makeText(getActivity(), R.string.message_edit_failed, Toast.LENGTH_SHORT).show();
438+
}
439+
}
440+
441+
@Override
442+
public void onFailure(Call<EditResponse> call, Throwable t) {
443+
progress.dismiss();
444+
ZLog.logException(t);
445+
Toast.makeText(getActivity(), R.string.message_edit_failed, Toast.LENGTH_SHORT).show();
446+
}
447+
});
448+
}
449+
});
450+
}
451+
}
452+
354453
@SuppressWarnings("deprecation")
355454
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
356455
private void copyMessage(Message msg) {
@@ -610,6 +709,7 @@ public void showLatestMessages() {
610709
loadMessageId(app.getMaxMessageId());
611710
}
612711
}
712+
613713
public boolean scrolledToLastMessage() {
614714
Object object = adapter.getItem(linearLayoutManager.findLastVisibleItemPosition());
615715
return object instanceof Message && (((Message) object).getId() >= app.getMaxMessageId() - 2);

app/src/main/java/com/zulip/android/models/Message.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ public String getFormattedContent() {
585585
return formattedContent;
586586
}
587587

588-
private void setFormattedContent(String formattedContent) {
588+
public void setFormattedContent(String formattedContent) {
589589
this.formattedContent = formattedContent;
590590
}
591591

app/src/main/java/com/zulip/android/networking/AsyncGetEvents.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.zulip.android.models.Person;
1919
import com.zulip.android.models.Stream;
2020
import com.zulip.android.networking.response.UserConfigurationResponse;
21+
import com.zulip.android.networking.response.events.EditMessageWrapper;
2122
import com.zulip.android.networking.response.events.EventsBranch;
2223
import com.zulip.android.networking.response.events.GetEventResponse;
2324
import com.zulip.android.networking.response.events.MessageWrapper;
@@ -116,6 +117,8 @@ private void register() throws JSONException, IOException {
116117
app.setLastEventId(res.getLastEventId());
117118
app.setPointer(res.getPointer());
118119
app.setMaxMessageId(res.getMaxMessageId());
120+
app.setMessageContentEditParams(res.getRealmMessageContentEditLimitSeconds(),
121+
res.isRealmAllowMessageEditing());
119122
registeredOrGotEventsThisRun = true;
120123
processRegister(res);
121124
}
@@ -338,6 +341,13 @@ public Message convert(MessageWrapper messageWrapper) {
338341
processMessages(messages);
339342
}
340343

344+
//get message time limit events
345+
List<EventsBranch> messageTimeLimit = events.getEventsOfBranchType(EventsBranch.BranchType.EDIT_MESSAGE_TIME_LIMIT);
346+
if (!messageTimeLimit.isEmpty()) {
347+
Log.i("AsyncGetEvents", "Received " + messageTimeLimit.size()
348+
+ " realm event");
349+
processMessageEditParam(messageTimeLimit);
350+
}
341351
}
342352

343353
/**
@@ -439,4 +449,14 @@ public void run() {
439449
}
440450
});
441451
}
452+
453+
private void processMessageEditParam(List<EventsBranch> messageEditLimitEvents) {
454+
for (EventsBranch wrapper : messageEditLimitEvents) {
455+
EditMessageWrapper timeLimitResponse = (EditMessageWrapper) wrapper;
456+
app.setMessageContentEditParams(
457+
timeLimitResponse.getData().getMessageContentEditLimitSeconds(),
458+
timeLimitResponse.getData().isMessageEditingAllowed()
459+
);
460+
}
461+
}
442462
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.zulip.android.networking.response;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
5+
public class EditResponse {
6+
7+
@SerializedName("msg")
8+
private String msg;
9+
10+
@SerializedName("result")
11+
private String result;
12+
13+
public String getMsg() {
14+
return msg;
15+
}
16+
17+
public String getResult() {
18+
return result;
19+
}
20+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.zulip.android.networking.response.events;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
5+
public class EditMessageWrapper extends EventsBranch {
6+
@SerializedName("data")
7+
private Data data;
8+
9+
public Data getData() {
10+
return data;
11+
}
12+
13+
public void setData(Data data) {
14+
this.data = data;
15+
}
16+
17+
public class Data {
18+
@SerializedName("allow_message_editing")
19+
private boolean isMessageEditingAllowed;
20+
21+
@SerializedName("message_content_edit_limit_seconds")
22+
private int messageContentEditLimitSeconds;
23+
24+
public boolean isMessageEditingAllowed() {
25+
return isMessageEditingAllowed;
26+
}
27+
28+
public int getMessageContentEditLimitSeconds() {
29+
return messageContentEditLimitSeconds;
30+
}
31+
}
32+
}

app/src/main/java/com/zulip/android/networking/response/events/EventsBranch.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ public enum BranchType {
2525
MESSAGE(MessageWrapper.class, "message"),
2626
PRESENCE(PresenceWrapper.class, "presence"),
2727
SUBSCRIPTIONS(SubscriptionWrapper.class, "subscription"),
28-
MUTED_TOPICS(MutedTopicsWrapper.class, "muted_topics");
28+
MUTED_TOPICS(MutedTopicsWrapper.class, "muted_topics"),
29+
EDIT_MESSAGE_TIME_LIMIT(EditMessageWrapper.class, "realm");
30+
2931

3032
private final Class<? extends EventsBranch> klazz;
3133
private final String key;

app/src/main/java/com/zulip/android/service/ZulipServices.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.zulip.android.service;
22

33
import com.zulip.android.filters.NarrowFilter;
4+
import com.zulip.android.networking.response.EditResponse;
45
import com.zulip.android.networking.response.GetMessagesResponse;
56
import com.zulip.android.networking.response.LoginResponse;
67
import com.zulip.android.networking.response.UploadResponse;
@@ -15,9 +16,11 @@
1516
import retrofit2.http.FormUrlEncoded;
1617
import retrofit2.http.GET;
1718
import retrofit2.http.Multipart;
19+
import retrofit2.http.PATCH;
1820
import retrofit2.http.POST;
1921
import retrofit2.http.PUT;
2022
import retrofit2.http.Part;
23+
import retrofit2.http.Path;
2124
import retrofit2.http.Query;
2225

2326

@@ -58,4 +61,8 @@ Call<GetMessagesResponse> getMessages(@Query("anchor") String anchor,
5861
@Multipart
5962
@POST("v1/user_uploads")
6063
Call<UploadResponse> upload(@Part MultipartBody.Part file);
64+
65+
@FormUrlEncoded
66+
@PATCH("v1/messages/{id}")
67+
Call<EditResponse> editMessage(@Path("id") String messageId, @Field("content") String messageContent);
6168
}

app/src/main/java/com/zulip/android/util/Constants.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,11 @@
77
public class Constants {
88
public static int MILLISECONDS_IN_A_MINUTE = 1000;
99
public static String DATE_FORMAT = "dd/MM/yyyy";
10+
public final static String IS_CONTENT_EDIT_PARAM_SAVED = "isContentEditParamSaved";
11+
public final static String IS_EDITING_ALLOWED = "isEditingAllowed";
12+
public final static String MAXIMUM_CONTENT_EDIT_LIMIT = "maximumContentEditLimit";
13+
//Default maximum time limit for editing message(Same as server)
14+
public final static int DEFAULT_MAXIMUM_CONTENT_EDIT_LIMIT = 600;
15+
public final static boolean DEFAULT_EDITING_ALLOWED = true;
16+
1017
}

app/src/main/java/com/zulip/android/viewholders/MessageHolder.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,21 @@ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMen
5858
if (msg.getType().equals(MessageType.STREAM_MESSAGE)) {
5959
MenuInflater inflater = ((Activity) v.getContext()).getMenuInflater();
6060
inflater.inflate(R.menu.context_stream, menu);
61+
if (msg.getSender().getId() != ZulipApp.get().getYou().getId()){
62+
menu.findItem(R.id.edit_message).setVisible(false);
63+
}
6164
} else if (msg.getPersonalReplyTo(ZulipApp.get()).length > 1) {
6265
MenuInflater inflater = ((Activity) v.getContext()).getMenuInflater();
6366
inflater.inflate(R.menu.context_private, menu);
67+
if (msg.getSender().getId() != ZulipApp.get().getYou().getId()){
68+
menu.findItem(R.id.edit_message).setVisible(false);
69+
}
6470
} else {
6571
MenuInflater inflater = ((Activity) v.getContext()).getMenuInflater();
6672
inflater.inflate(R.menu.context_single_private, menu);
73+
if (msg.getSender().getId() != ZulipApp.get().getYou().getId()){
74+
menu.findItem(R.id.edit_message).setVisible(false);
75+
}
6776
}
6877
}
6978

0 commit comments

Comments
 (0)