Commit 500b0e78 authored by Yusuke Iwaki's avatar Yusuke Iwaki

add NotificationItem model.

parent 8e0983e2
...@@ -23,6 +23,8 @@ public class RoomSubscription extends RealmObject { ...@@ -23,6 +23,8 @@ public class RoomSubscription extends RealmObject {
private boolean open; private boolean open;
private boolean alert; private boolean alert;
private int unread; private int unread;
private long _updatedAt;
private long ls; //last seen.
public String get_id() { public String get_id() {
return _id; return _id;
...@@ -80,7 +82,35 @@ public class RoomSubscription extends RealmObject { ...@@ -80,7 +82,35 @@ public class RoomSubscription extends RealmObject {
this.unread = unread; this.unread = unread;
} }
public long get_updatedAt() {
return _updatedAt;
}
public void set_updatedAt(long _updatedAt) {
this._updatedAt = _updatedAt;
}
public long getLs() {
return ls;
}
public void setLs(long ls) {
this.ls = ls;
}
public static JSONObject customizeJson(JSONObject roomSubscriptionJson) throws JSONException { public static JSONObject customizeJson(JSONObject roomSubscriptionJson) throws JSONException {
if (!roomSubscriptionJson.isNull("ls")) {
long ls = roomSubscriptionJson.getJSONObject("ls").getLong("$date");
roomSubscriptionJson.remove("ls");
roomSubscriptionJson.put("ls", ls);
}
if (!roomSubscriptionJson.isNull("_updatedAt")) {
long updatedAt = roomSubscriptionJson.getJSONObject("_updatedAt").getLong("$date");
roomSubscriptionJson.remove("_updatedAt");
roomSubscriptionJson.put("_updatedAt", updatedAt);
}
return roomSubscriptionJson; return roomSubscriptionJson;
} }
} }
package chat.rocket.android.model.internal;
import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;
/**
* View model for notification.
*/
public class NotificationItem extends RealmObject {
@PrimaryKey private String roomId;
private String title;
private String description;
private int unreadCount;
private String senderName;
private long contentUpdatedAt; //subscription._updatedAt
private long lastSeenAt; //max(notification dismissed, subscription.ls)
public String getRoomId() {
return roomId;
}
public void setRoomId(String roomId) {
this.roomId = roomId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getUnreadCount() {
return unreadCount;
}
public void setUnreadCount(int unreadCount) {
this.unreadCount = unreadCount;
}
public String getSenderName() {
return senderName;
}
public void setSenderName(String senderName) {
this.senderName = senderName;
}
public long getContentUpdatedAt() {
return contentUpdatedAt;
}
public void setContentUpdatedAt(long contentUpdatedAt) {
this.contentUpdatedAt = contentUpdatedAt;
}
public long getLastSeenAt() {
return lastSeenAt;
}
public void setLastSeenAt(long lastSeenAt) {
this.lastSeenAt = lastSeenAt;
}
}
...@@ -22,7 +22,7 @@ import chat.rocket.android.service.observer.GetUsersOfRoomsProcedureObserver; ...@@ -22,7 +22,7 @@ import chat.rocket.android.service.observer.GetUsersOfRoomsProcedureObserver;
import chat.rocket.android.service.observer.LoadMessageProcedureObserver; import chat.rocket.android.service.observer.LoadMessageProcedureObserver;
import chat.rocket.android.service.observer.MethodCallObserver; import chat.rocket.android.service.observer.MethodCallObserver;
import chat.rocket.android.service.observer.NewMessageObserver; import chat.rocket.android.service.observer.NewMessageObserver;
import chat.rocket.android.service.observer.ReactiveNotificationCancelManager; import chat.rocket.android.service.observer.NotificationItemObserver;
import chat.rocket.android.service.observer.ReactiveNotificationManager; import chat.rocket.android.service.observer.ReactiveNotificationManager;
import chat.rocket.android.service.observer.SessionObserver; import chat.rocket.android.service.observer.SessionObserver;
import chat.rocket.android.service.observer.TokenLoginObserver; import chat.rocket.android.service.observer.TokenLoginObserver;
...@@ -49,7 +49,7 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -49,7 +49,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
NewMessageObserver.class, NewMessageObserver.class,
CurrentUserObserver.class, CurrentUserObserver.class,
ReactiveNotificationManager.class, ReactiveNotificationManager.class,
ReactiveNotificationCancelManager.class NotificationItemObserver.class
}; };
private final Context appContext; private final Context appContext;
private final String serverConfigId; private final String serverConfigId;
......
...@@ -5,6 +5,8 @@ import chat.rocket.android.api.DDPClientWraper; ...@@ -5,6 +5,8 @@ import chat.rocket.android.api.DDPClientWraper;
import chat.rocket.android.model.ddp.RoomSubscription; import chat.rocket.android.model.ddp.RoomSubscription;
import chat.rocket.android.realm_helper.RealmHelper; import chat.rocket.android.realm_helper.RealmHelper;
import io.realm.RealmObject; import io.realm.RealmObject;
import org.json.JSONException;
import org.json.JSONObject;
public class StreamNotifyUserSubscriptionsChanged extends AbstractStreamNotifyUserEventSubscriber { public class StreamNotifyUserSubscriptionsChanged extends AbstractStreamNotifyUserEventSubscriber {
public StreamNotifyUserSubscriptionsChanged(Context context, String hostname, public StreamNotifyUserSubscriptionsChanged(Context context, String hostname,
...@@ -20,6 +22,10 @@ public class StreamNotifyUserSubscriptionsChanged extends AbstractStreamNotifyUs ...@@ -20,6 +22,10 @@ public class StreamNotifyUserSubscriptionsChanged extends AbstractStreamNotifyUs
return RoomSubscription.class; return RoomSubscription.class;
} }
@Override protected JSONObject customizeFieldJson(JSONObject json) throws JSONException {
return RoomSubscription.customizeJson(super.customizeFieldJson(json));
}
@Override protected String getPrimaryKeyForModel() { @Override protected String getPrimaryKeyForModel() {
return "rid"; return "rid";
} }
......
...@@ -3,6 +3,7 @@ package chat.rocket.android.service.observer; ...@@ -3,6 +3,7 @@ package chat.rocket.android.service.observer;
import android.content.Context; import android.content.Context;
import chat.rocket.android.api.DDPClientWraper; import chat.rocket.android.api.DDPClientWraper;
import chat.rocket.android.api.MethodCallHelper; import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.LogcatIfError;
import chat.rocket.android.model.ddp.User; import chat.rocket.android.model.ddp.User;
import chat.rocket.android.realm_helper.RealmHelper; import chat.rocket.android.realm_helper.RealmHelper;
import chat.rocket.android.service.Registerable; import chat.rocket.android.service.Registerable;
...@@ -65,7 +66,7 @@ public class CurrentUserObserver extends AbstractModelObserver<User> { ...@@ -65,7 +66,7 @@ public class CurrentUserObserver extends AbstractModelObserver<User> {
listeners.add(listener); listeners.add(listener);
} }
return null; return null;
}); }).continueWith(new LogcatIfError());
} }
@DebugLog @DebugLog
......
package chat.rocket.android.service.observer;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.content.ContextCompat;
import bolts.Task;
import chat.rocket.android.R;
import chat.rocket.android.activity.MainActivity;
import chat.rocket.android.api.DDPClientWraper;
import chat.rocket.android.helper.Avatar;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.model.ServerConfig;
import chat.rocket.android.model.internal.NotificationItem;
import chat.rocket.android.realm_helper.RealmHelper;
import chat.rocket.android.realm_helper.RealmStore;
import io.realm.Realm;
import io.realm.RealmResults;
import java.util.List;
/**
* observes NotificationItem and notify/cancel notification.
*/
public class NotificationItemObserver extends AbstractModelObserver<NotificationItem> {
public NotificationItemObserver(Context context, String hostname, RealmHelper realmHelper,
DDPClientWraper ddpClient) {
super(context, hostname, realmHelper, ddpClient);
}
@Override public RealmResults<NotificationItem> queryItems(Realm realm) {
return realm.where(NotificationItem.class).findAll();
}
@Override public void onUpdateResults(List<NotificationItem> results) {
if (results.isEmpty()) {
return;
}
for (NotificationItem item : results) {
final String notificationId = item.getRoomId();
if (item.getUnreadCount() > 0
&& item.getContentUpdatedAt() > item.getLastSeenAt()) {
generateNotificationFor(item)
.onSuccess(task -> {
Notification notification = task.getResult();
if (notification != null) {
NotificationManagerCompat.from(context)
.notify(notificationId.hashCode(), notification);
}
return null;
});
} else {
NotificationManagerCompat.from(context).cancel(notificationId.hashCode());
}
}
}
private Task<Notification> generateNotificationFor(NotificationItem item) {
final String username = item.getSenderName();
final String roomId = item.getRoomId();
final String title = item.getTitle();
final String description = TextUtils.or(item.getDescription(), "").toString();
final int unreadCount = item.getUnreadCount();
if (TextUtils.isEmpty(username)) {
return Task.forResult(generateNotification(roomId, title, description, unreadCount, null));
}
int size = context.getResources().getDimensionPixelSize(R.dimen.notification_avatar_size);
return new Avatar(hostname, username).getBitmap(context, size)
.continueWithTask(task -> {
Bitmap icon = task.isFaulted() ? null : task.getResult();
final Notification notification =
generateNotification(roomId, title, description, unreadCount, icon);
return Task.forResult(notification);
});
}
private Notification generateNotification(String roomId, String title,
@NonNull String description, int unreadCount, @Nullable Bitmap icon) {
Intent intent = new Intent(context, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP);
ServerConfig config = RealmStore.getDefault().executeTransactionForRead(realm ->
realm.where(ServerConfig.class).equalTo("hostname", hostname).findFirst());
if (config != null) {
intent.putExtra("serverConfigId", config.getServerConfigId());
intent.putExtra("roomId", roomId);
}
PendingIntent pendingIntent = PendingIntent.getActivity(context.getApplicationContext(),
(int) (System.currentTimeMillis() % Integer.MAX_VALUE),
intent, PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setContentTitle(title)
.setContentText(description)
.setNumber(unreadCount)
.setColor(ContextCompat.getColor(context, R.color.colorPrimary))
.setSmallIcon(R.drawable.rocket_chat_notification_24dp)
.setContentIntent(pendingIntent);
if (icon != null) {
builder.setLargeIcon(icon);
}
if (description.length() > 20) {
return new NotificationCompat.BigTextStyle(builder)
.bigText(description)
.build();
} else {
return builder.build();
}
}
}
package chat.rocket.android.service.observer;
import android.content.Context;
import android.support.v4.app.NotificationManagerCompat;
import chat.rocket.android.api.DDPClientWraper;
import chat.rocket.android.model.ddp.RoomSubscription;
import chat.rocket.android.realm_helper.RealmHelper;
import hugo.weaving.DebugLog;
import io.realm.Realm;
import io.realm.RealmResults;
import java.util.List;
/**
* observing room subscriptions with unread>0.
*/
public class ReactiveNotificationCancelManager extends AbstractModelObserver<RoomSubscription> {
public ReactiveNotificationCancelManager(Context context, String hostname,
RealmHelper realmHelper, DDPClientWraper ddpClient) {
super(context, hostname, realmHelper, ddpClient);
}
@Override public RealmResults<RoomSubscription> queryItems(Realm realm) {
return realm.where(RoomSubscription.class)
.equalTo("open", true)
.equalTo("unread", 0)
.findAll();
}
@DebugLog
@Override public void onUpdateResults(List<RoomSubscription> roomSubscriptions) {
// TODO implement!
for (RoomSubscription roomSubscription : roomSubscriptions) {
final String roomId = roomSubscription.getRid();
NotificationManagerCompat.from(context).cancel(roomId.hashCode());
}
}
}
package chat.rocket.android.service.observer; package chat.rocket.android.service.observer;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.content.ContextCompat;
import chat.rocket.android.R;
import chat.rocket.android.activity.MainActivity;
import chat.rocket.android.api.DDPClientWraper; import chat.rocket.android.api.DDPClientWraper;
import chat.rocket.android.model.ServerConfig; import chat.rocket.android.helper.LogcatIfError;
import chat.rocket.android.log.RCLog;
import chat.rocket.android.model.ddp.RoomSubscription; import chat.rocket.android.model.ddp.RoomSubscription;
import chat.rocket.android.model.internal.NotificationItem;
import chat.rocket.android.realm_helper.RealmHelper; import chat.rocket.android.realm_helper.RealmHelper;
import chat.rocket.android.realm_helper.RealmStore;
import hugo.weaving.DebugLog; import hugo.weaving.DebugLog;
import io.realm.Realm; import io.realm.Realm;
import io.realm.RealmResults; import io.realm.RealmResults;
import java.util.List; import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/** /**
* observing room subscriptions with unread>0. * observing room subscriptions with unread>0.
...@@ -31,7 +27,6 @@ public class ReactiveNotificationManager extends AbstractModelObserver<RoomSubsc ...@@ -31,7 +27,6 @@ public class ReactiveNotificationManager extends AbstractModelObserver<RoomSubsc
@Override public RealmResults<RoomSubscription> queryItems(Realm realm) { @Override public RealmResults<RoomSubscription> queryItems(Realm realm) {
return realm.where(RoomSubscription.class) return realm.where(RoomSubscription.class)
.equalTo("open", true) .equalTo("open", true)
.greaterThan("unread", 0)
.findAll(); .findAll();
} }
...@@ -39,30 +34,37 @@ public class ReactiveNotificationManager extends AbstractModelObserver<RoomSubsc ...@@ -39,30 +34,37 @@ public class ReactiveNotificationManager extends AbstractModelObserver<RoomSubsc
@Override public void onUpdateResults(List<RoomSubscription> roomSubscriptions) { @Override public void onUpdateResults(List<RoomSubscription> roomSubscriptions) {
// TODO implement! // TODO implement!
JSONArray notifications = new JSONArray();
for (RoomSubscription roomSubscription : roomSubscriptions) { for (RoomSubscription roomSubscription : roomSubscriptions) {
final String roomId = roomSubscription.getRid(); final String roomId = roomSubscription.getRid();
NotificationItem item = realmHelper.executeTransactionForRead(realm ->
realm.where(NotificationItem.class).equalTo("roomId", roomId).findFirst());
Intent intent = new Intent(context, MainActivity.class); long lastSeenAt = Math.max(item != null ? item.getLastSeenAt() : 0, roomSubscription.getLs());
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP); try {
ServerConfig config = RealmStore.getDefault().executeTransactionForRead(realm -> JSONObject notification = new JSONObject()
realm.where(ServerConfig.class).equalTo("hostname", hostname).findFirst()); .put("roomId", roomSubscription.getRid())
if (config != null) { .put("title", roomSubscription.getName())
intent.putExtra("serverConfigId", config.getServerConfigId()); .put("description", "new message")
intent.putExtra("roomId", roomId); .put("unreadCount", roomSubscription.getUnread())
} .put("contentUpdatedAt", roomSubscription.get_updatedAt())
.put("lastSeenAt", lastSeenAt);
PendingIntent pendingIntent = PendingIntent.getActivity(context.getApplicationContext(),
(int) (System.currentTimeMillis() % Integer.MAX_VALUE),
intent, PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context) if (RoomSubscription.TYPE_DIRECT_MESSAGE.equals(roomSubscription.getT())) {
.setContentTitle(roomSubscription.getName()) notification.put("senderName", roomSubscription.getName());
.setColor(ContextCompat.getColor(context, R.color.colorPrimary)) } else {
.setSmallIcon(R.drawable.rocket_chat_notification_24dp) notification.put("senderName", JSONObject.NULL);
.setContentIntent(pendingIntent); }
Notification notification = builder.build(); notifications.put(notification);
NotificationManagerCompat.from(context).notify(roomId.hashCode(), notification); } catch (JSONException exception) {
RCLog.w(exception);
}
} }
realmHelper.executeTransaction(realm -> {
realm.createOrUpdateAllFromJson(NotificationItem.class, notifications);
return null;
}).continueWith(new LogcatIfError());
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment