package com.wangdaye.mysplash.common.utils.manager;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.design.widget.Snackbar;
import android.text.TextUtils;
import com.google.gson.Gson;
import com.wangdaye.mysplash.Mysplash;
import com.wangdaye.mysplash.R;
import com.wangdaye.mysplash.common.data.entity.unsplash.NotificationFeed;
import com.wangdaye.mysplash.common.data.entity.unsplash.NotificationResult;
import com.wangdaye.mysplash.common.data.entity.unsplash.NotificationStream;
import com.wangdaye.mysplash.common.data.service.GetStreamService;
import com.wangdaye.mysplash.common.data.service.NotificationService;
import com.wangdaye.mysplash.common.utils.helper.NotificationHelper;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Response;
/**
* User notification manager.
*
* A manager class that is used to manage the notifications for user.
*
* */
public class UserNotificationManager {
private Gson gson;
private SimpleDateFormat format;
private GetStreamService streamService;
private NotificationService notificationService;
private OnRequestStreamListener requestStreamListener;
private OnRequestNotificationListener requestNotificationListener;
private List<OnUpdateNotificationListener> listenerList;
// data
private List<NotificationResult> notificationList;
// this value is used to save the latest notification that was seen by user.
// if a notification is update at a time later than the saved time, this notification must have
// not been seen.
private String latestSeenTime;
// this value is used to save the latest refresh time.
private long latestRefreshTime;
private String nextPage;
// primary key for the first result in the first page stream feed.
// if the first result in a new feed has a id that's same as this value,
// it means there is no unseen result.
/** {@link com.wangdaye.mysplash.common.data.entity.unsplash.NotificationStream.Results#id} */
private String firstResultId;
private boolean loadFinish;
private boolean requesting;
private static final String PREFERENCE_TIME = "mysplash_notification";
private static final String KEY_LATEST_SEEN_TIME = "latest_seen_time";
@SuppressLint("SimpleDateFormat")
UserNotificationManager() {
gson = new Gson();
format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
streamService = GetStreamService.getService();
notificationService = NotificationService.getService();
listenerList = new ArrayList<>();
notificationList = new ArrayList<>();
nextPage = null;
firstResultId = null;
loadFinish = false;
requesting = false;
SharedPreferences sharedPreferences = Mysplash.getInstance().getSharedPreferences(
PREFERENCE_TIME, Context.MODE_PRIVATE);
latestSeenTime = sharedPreferences.getString(KEY_LATEST_SEEN_TIME, "");
latestRefreshTime = -1;
}
// HTTP request.
public void requestPersonalNotifications() {
if (!loadFinish && !requesting) {
cancelRequest(true);
if (TextUtils.isEmpty(nextPage)) {
requestFirstPageNotifications();
} else {
requestNextPageNotifications();
}
}
}
private void requestFirstPageNotifications() {
requesting = true;
requestStreamListener = new OnRequestStreamListener(true);
streamService.requestFirstPageStream(requestStreamListener);
}
private void requestNextPageNotifications() {
requesting = true;
requestStreamListener = new OnRequestStreamListener(false);
streamService.requestNextPageStream(nextPage, requestStreamListener);
}
void checkToRefreshNotification() {
if (latestRefreshTime < 0) {
requestPersonalNotifications();
} else if (System.currentTimeMillis() - latestRefreshTime > 1000 * 60 * 15
// // TODO: 2017/4/22 alter time.
&& !requesting) {
cancelRequest(true);
requestFirstPageNotifications();
}
}
/**
* This method will cancel the HTTP request.
*
* @param force if set false, it means only cancel the next page HTTP request.
* */
public void cancelRequest(boolean force) {
if (force || !TextUtils.isEmpty(nextPage)) {
requesting = false;
if (requestStreamListener != null) {
requestStreamListener.setCanceled();
}
if (requestNotificationListener != null) {
requestNotificationListener.setCanceled();
}
streamService.cancel();
notificationService.cancel();
}
}
// manage.
public List<NotificationResult> getNotificationList() {
return notificationList;
}
private void addNotification(List<NotificationResult> list) {
for (int i = 0; i < list.size(); i ++) {
notificationList.add(list.get(i));
for (int j = 0; j < listenerList.size(); j ++) {
listenerList.get(j).onAddNotification(list.get(i), notificationList.size() - 1);
}
}
}
private void removeAllNotifications() {
notificationList.clear();
for (int j = 0; j < listenerList.size(); j ++) {
listenerList.get(j).onClearNotification();
}
}
public void clearNotifications(boolean logout) {
removeAllNotifications();
cancelRequest(true);
nextPage = null;
firstResultId = null;
loadFinish = false;
if (logout) {
setLatestSeenTime();
}
}
public boolean isLoadFinish() {
return loadFinish;
}
// time.
@SuppressLint("SimpleDateFormat")
public boolean hasUnseenNotification() {
return notificationList.size() != 0
&& (TextUtils.isEmpty(latestSeenTime) || isUnseenNotification(notificationList.get(0)));
}
public boolean isUnseenNotification(NotificationResult result) {
if (TextUtils.isEmpty(latestSeenTime)) {
return true;
} else {
try {
String[] savedTimes = latestSeenTime.substring(0, 19).split("T");
String[] resultTimes = result.time.substring(0, 19).split("T");
Date savedDate = format.parse(savedTimes[0] + " " + savedTimes[1]);
Date listDate = format.parse(resultTimes[0] + " " + resultTimes[1]);
return savedDate.before(listDate);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
public void setLatestSeenTime() {
if (notificationList.size() == 0) {
latestSeenTime = "";
} else {
latestSeenTime = notificationList.get(0).time;
}
SharedPreferences.Editor editor = Mysplash.getInstance().getSharedPreferences(
PREFERENCE_TIME, Context.MODE_PRIVATE).edit();
editor.putString(KEY_LATEST_SEEN_TIME, latestSeenTime);
editor.apply();
for (int i = 0; i < listenerList.size(); i ++) {
listenerList.get(i).onSetLatestTime();
}
}
public SimpleDateFormat getFormat() {
return format;
}
// interface.
// on add notification listener.
public interface OnUpdateNotificationListener {
void onRequestNotificationSucceed(List<NotificationResult> resultList);
void onRequestNotificationFailed();
void onAddNotification(NotificationResult result, int position);
void onClearNotification();
void onSetLatestTime();
}
public void addOnUpdateNotificationListener(OnUpdateNotificationListener l) {
this.listenerList.add(l);
}
public void removeOnUpdateNotificationListener(OnUpdateNotificationListener l) {
this.listenerList.remove(l);
}
// on request stream listener.
private class OnRequestStreamListener implements GetStreamService.OnRequestStreamListener {
// data
private boolean refresh;
private boolean canceled;
// life cycle.
OnRequestStreamListener(boolean refresh) {
this.refresh = refresh;
this.canceled = false;
}
// data.
void setCanceled() {
this.canceled = true;
}
// interface.
@Override
public void onRequestEnrichSucceed(Call<ResponseBody> call, Response<ResponseBody> response) {
if (canceled) {
return;
}
String json = null;
NotificationStream stream = null;
try {
json = response.body().string();
stream = gson.fromJson(json, NotificationStream.class);
} catch (Exception e) {
e.printStackTrace();
}
if (TextUtils.isEmpty(json) || stream == null) {
// failed to request.
requesting = false;
requestFailed(response.code() + " " + response.message());
} else {
// request successfully.
if (refresh) {
latestRefreshTime = System.currentTimeMillis();
}
if (TextUtils.isEmpty(stream.next)) {
nextPage = null;
loadFinish = true;
} else {
nextPage = stream.next;
}
if (stream.results != null && stream.results.size() != 0
&& (!refresh
|| TextUtils.isEmpty(firstResultId)
|| !stream.results.get(0).id.equals(firstResultId))) {
if (refresh) {
firstResultId = stream.results.get(0).id;
}
requestNotificationListener = new OnRequestNotificationListener(refresh);
notificationService.requestNotificationFeed(
streamService.getStreamUsablePart(json),
requestNotificationListener);
} else {
requesting = false;
for (int j = 0; j < listenerList.size(); j ++) {
listenerList.get(j).onRequestNotificationSucceed(
new ArrayList<NotificationResult>());
}
}
}
}
@Override
public void onRequestEnrichFailed(Call<ResponseBody> call, Throwable t) {
requestFailed(t.getMessage());
}
private void requestFailed(String msg) {
for (int j = 0; j < listenerList.size(); j ++) {
listenerList.get(j).onRequestNotificationFailed();
}
NotificationHelper.showSnackbar(
Mysplash.getInstance().getString(R.string.feedback_get_notification_failed) + "(" + msg + ")",
Snackbar.LENGTH_SHORT);
}
}
// on request notification listener.
private class OnRequestNotificationListener implements NotificationService.OnRequestNotificationListener {
// data
private boolean refresh;
private boolean canceled;
// life cycle.
OnRequestNotificationListener(boolean refresh) {
this.refresh = refresh;
this.canceled = false;
}
// data.
void setCanceled() {
this.canceled = true;
}
// interface.
@Override
public void onRequestNotificationSucceed(Call<NotificationFeed> call, Response<NotificationFeed> response) {
if (canceled) {
return;
}
requesting = false;
if (!response.isSuccessful() || response.body() == null) {
requestFailed(response.code() + " " + response.message());
} else {
// request successfully.
if (response.body().results != null && response.body().results.size() > 0) {
if (refresh
&& notificationList.size() > 0
&& !response.body().results.get(0).id.equals(notificationList.get(0).id)) {
// already have notifications in the list.
// and there are some new feeds in the request result.
removeAllNotifications();
}
addNotification(response.body().results);
}
for (int j = 0; j < listenerList.size(); j ++) {
listenerList.get(j).onRequestNotificationSucceed(response.body().results);
}
}
}
@Override
public void onRequestNotificationFailed(Call<NotificationFeed> call, Throwable t) {
requestFailed(t.getMessage());
}
private void requestFailed(String msg) {
for (int j = 0; j < listenerList.size(); j ++) {
listenerList.get(j).onRequestNotificationFailed();
}
NotificationHelper.showSnackbar(
Mysplash.getInstance().getString(R.string.feedback_get_notification_failed) + "(" + msg + ")",
Snackbar.LENGTH_SHORT);
}
}
}