Code to retrive unknown notification and UI (#2340)

* request change, changed notification icon

* Completed task 1 of the work

* commit changes

* commit changes

* updated notification class

* before notification id

* gradle reverted

* Minor changes to mark notifications as read

* commit changes

* delete on swipe

* notification count

* sipe to delete

* changes

* worked on changes requested

* commit changes

* Fix notification count

* reviewed changes

* round icon, swipe with icon

* Fix pending NPE issues with notifications

* final commit

* graddle changes

* removed changes for testing
This commit is contained in:
Vanshika Arora 2019-02-02 19:21:58 +05:30 committed by Vivek Maskara
parent 9451b00a15
commit 1b62ac4d2d
22 changed files with 381 additions and 195 deletions

View file

@ -12,8 +12,9 @@ public class Notification {
public String link;
public String iconUrl;
public String dateWithYear;
public String notificationId;
public Notification(NotificationType notificationType, String notificationText, String date, String description, String link, String iconUrl, String dateWithYear) {
public Notification(NotificationType notificationType, String notificationText, String date, String description, String link, String iconUrl, String dateWithYear, String notificationId) {
this.notificationType = notificationType;
this.notificationText = notificationText;
this.date = date;
@ -21,5 +22,20 @@ public class Notification {
this.link = link;
this.iconUrl = iconUrl;
this.dateWithYear = dateWithYear;
this.notificationId=notificationId;
}
@Override
public String toString() {
return "Notification" +
"notificationType='" + notificationType + '\'' +
", notificationText='" + notificationText + '\'' +
", date='" + date + '\'' +
", description='" + description + '\'' +
", link='" + link + '\'' +
", iconUrl='" + iconUrl + '\'' +
", dateWithYear=" + dateWithYear +
", notificationId='" + notificationId + '\'' +
'}';
}
}

View file

@ -1,11 +1,11 @@
package fr.free.nrw.commons.notification;
import android.annotation.SuppressLint;
import android.app.FragmentManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.constraint.ConstraintLayout;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
@ -13,23 +13,19 @@ import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.pedrogomez.renderers.RVRendererAdapter;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.kvstore.BasicKvStore;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.theme.NavigationBaseActivity;
import fr.free.nrw.commons.utils.NetworkUtils;
import fr.free.nrw.commons.utils.ViewUtil;
@ -44,17 +40,23 @@ import timber.log.Timber;
public class NotificationActivity extends NavigationBaseActivity {
NotificationAdapterFactory notificationAdapterFactory;
@BindView(R.id.listView) RecyclerView recyclerView;
@BindView(R.id.progressBar) ProgressBar progressBar;
@BindView(R.id.container) RelativeLayout relativeLayout;
@Inject NotificationController controller;
@Inject MediaWikiApi mediaWikiApi;
@Inject @Named("last_read_notification_date") BasicKvStore kvStore;
@BindView(R.id.listView)
RecyclerView recyclerView;
@BindView(R.id.progressBar)
ProgressBar progressBar;
@BindView(R.id.container)
RelativeLayout relativeLayout;
@BindView(R.id.no_notification_background)
ConstraintLayout no_notification;
/* @BindView(R.id.swipe_bg)
TextView swipe_bg;*/
@Inject
NotificationController controller;
private static final String TAG_NOTIFICATION_WORKER_FRAGMENT = "NotificationWorkerFragment";
private NotificationWorkerFragment mNotificationWorkerFragment;
private RVRendererAdapter<Notification> adapter;
private List<Notification> notificationList;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -62,11 +64,46 @@ public class NotificationActivity extends NavigationBaseActivity {
setContentView(R.layout.activity_notification);
ButterKnife.bind(this);
mNotificationWorkerFragment = (NotificationWorkerFragment) getFragmentManager()
.findFragmentByTag(TAG_NOTIFICATION_WORKER_FRAGMENT);
.findFragmentByTag(TAG_NOTIFICATION_WORKER_FRAGMENT);
initListView();
initDrawer();
}
@SuppressLint("CheckResult")
public void removeNotification(Notification notification) {
Observable.fromCallable(() -> controller.markAsRead(notification))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (result){
notificationList.remove(notification);
setAdapter(notificationList);
adapter.notifyDataSetChanged();
Snackbar snackbar = Snackbar
.make(relativeLayout,"Notification marked as read", Snackbar.LENGTH_LONG);
snackbar.show();
if (notificationList.size()==0){
relativeLayout.setVisibility(View.GONE);
no_notification.setVisibility(View.VISIBLE);
}
}
else {
adapter.notifyDataSetChanged();
setAdapter(notificationList);
Toast.makeText(NotificationActivity.this, "There was some error!", Toast.LENGTH_SHORT).show();
}
}, throwable -> {
Timber.e(throwable, "Error occurred while loading notifications");
throwable.printStackTrace();
ViewUtil.showShortSnackbar(relativeLayout, R.string.error_notifications);
progressBar.setVisibility(View.GONE);
});
}
private void initListView() {
recyclerView.setLayoutManager(new LinearLayoutManager(this));
DividerItemDecoration itemDecor = new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL);
@ -77,9 +114,9 @@ public class NotificationActivity extends NavigationBaseActivity {
private void refresh() {
if (!NetworkUtils.isInternetConnectionEstablished(this)) {
progressBar.setVisibility(View.GONE);
Snackbar.make(relativeLayout , R.string.no_internet, Snackbar.LENGTH_INDEFINITE)
Snackbar.make(relativeLayout, R.string.no_internet, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.retry, view -> refresh()).show();
}else {
} else {
progressBar.setVisibility(View.VISIBLE);
addNotifications();
}
@ -88,13 +125,7 @@ public class NotificationActivity extends NavigationBaseActivity {
@SuppressLint("CheckResult")
private void addNotifications() {
Timber.d("Add notifications");
// Store when add notification is called last
long currentDate = new Date(System.currentTimeMillis()).getTime();
kvStore.putLong("last_read_notification_date", currentDate);
Timber.d("Set last notification read date to current date:"+ currentDate);
if(mNotificationWorkerFragment == null){
if (mNotificationWorkerFragment == null) {
Observable.fromCallable(() -> {
progressBar.setVisibility(View.VISIBLE);
return controller.getNotifications();
@ -104,7 +135,13 @@ public class NotificationActivity extends NavigationBaseActivity {
.subscribe(notificationList -> {
Collections.reverse(notificationList);
Timber.d("Number of notifications is %d", notificationList.size());
setAdapter(notificationList);
this.notificationList = notificationList;
if (notificationList.size()==0){
relativeLayout.setVisibility(View.GONE);
no_notification.setVisibility(View.VISIBLE);
} else {
setAdapter(notificationList);
}
progressBar.setVisibility(View.GONE);
}, throwable -> {
Timber.e(throwable, "Error occurred while loading notifications");
@ -112,7 +149,8 @@ public class NotificationActivity extends NavigationBaseActivity {
progressBar.setVisibility(View.GONE);
});
} else {
setAdapter(mNotificationWorkerFragment.getNotificationList());
notificationList = mNotificationWorkerFragment.getNotificationList();
setAdapter(notificationList);
}
}
@ -126,13 +164,28 @@ public class NotificationActivity extends NavigationBaseActivity {
private void setAdapter(List<Notification> notificationList) {
if (notificationList == null || notificationList.isEmpty()) {
ViewUtil.showShortSnackbar(relativeLayout, R.string.no_notifications);
/*progressBar.setVisibility(View.GONE);
recyclerView.setVisibility(View.GONE);*/
relativeLayout.setVisibility(View.GONE);
no_notification.setVisibility(View.VISIBLE);
return;
}
notificationAdapterFactory = new NotificationAdapterFactory(notification -> {
Timber.d("Notification clicked %s", notification.link);
handleUrl(notification.link);
notificationAdapterFactory = new NotificationAdapterFactory(new NotificationRenderer.NotificationClicked() {
@Override
public void notificationClicked(Notification notification) {
Timber.d("Notification clicked %s", notification.link);
handleUrl(notification.link);
}
@Override
public void markNotificationAsRead(Notification notification) {
Timber.d("Notification to mark as read %s", notification.notificationId);
removeNotification(notification);
}
});
RVRendererAdapter<Notification> adapter = notificationAdapterFactory.create(notificationList);
adapter = notificationAdapterFactory.create(notificationList);
relativeLayout.setVisibility(View.VISIBLE);
no_notification.setVisibility(View.GONE);
recyclerView.setAdapter(adapter);
}
@ -141,5 +194,4 @@ public class NotificationActivity extends NavigationBaseActivity {
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(intent);
}
}

View file

@ -36,4 +36,7 @@ public class NotificationController {
}
return new ArrayList<>();
}
public boolean markAsRead(Notification notification) throws IOException{
return mediaWikiApi.markNotificationAsRead(notification);
}
}

View file

@ -1,36 +1,55 @@
package fr.free.nrw.commons.notification;
import android.graphics.Color;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.nineoldandroids.view.ViewHelper;
import com.pedrogomez.renderers.Renderer;
import com.daimajia.swipe.SwipeLayout;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import fr.free.nrw.commons.R;
import timber.log.Timber;
/**
* Created by root on 19.12.2017.
*/
public class NotificationRenderer extends Renderer<Notification> {
@BindView(R.id.title) TextView title;
@BindView(R.id.time) TextView time;
@BindView(R.id.icon) ImageView icon;
@BindView(R.id.title)
TextView title;
@BindView(R.id.time)
TextView time;
@BindView(R.id.icon)
ImageView icon;
@BindView(R.id.swipeLayout)
SwipeLayout swipeLayout;
@BindView(R.id.bottom)
LinearLayout bottomLayout;
private NotificationClicked listener;
NotificationRenderer(NotificationClicked listener) {
this.listener = listener;
}
@OnClick(R.id.bottom)
void onBottomLayoutClicked(){
Notification notification = getContent();
Timber.d("NotificationID: %s", notification.notificationId);
listener.markNotificationAsRead(notification);
}
@Override
protected void setUpView(View view) { }
protected void setUpView(View rootView) {
}
@Override
protected void hookListeners(View rootView) {
rootView.setOnClickListener(v -> listener.notificationClicked(getContent()));
@ -40,9 +59,39 @@ public class NotificationRenderer extends Renderer<Notification> {
protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) {
View inflatedView = layoutInflater.inflate(R.layout.item_notification, viewGroup, false);
ButterKnife.bind(this, inflatedView);
swipeLayout.addDrag(SwipeLayout.DragEdge.Top, bottomLayout);
swipeLayout.addRevealListener(R.id.bottom_wrapper_child1, (child, edge, fraction, distance) -> {
View star = child.findViewById(R.id.star);
float d = child.getHeight() / 2 - star.getHeight() / 2;
ViewHelper.setTranslationY(star, d * fraction);
ViewHelper.setScaleX(star, fraction + 0.6f);
ViewHelper.setScaleY(star, fraction + 0.6f);
int c = (Integer) evaluate(fraction, Color.parseColor("#dddddd"), Color.parseColor("#90960a0a"));
child.setBackgroundColor(c);
});
return inflatedView;
}
public Object evaluate(float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
int startA = (startInt >> 24) & 0xff;
int startR = (startInt >> 16) & 0xff;
int startG = (startInt >> 8) & 0xff;
int startB = startInt & 0xff;
int endInt = (Integer) endValue;
int endA = (endInt >> 24) & 0xff;
int endR = (endInt >> 16) & 0xff;
int endG = (endInt >> 8) & 0xff;
int endB = endInt & 0xff;
return (int) ((startA + (int) (fraction * (endA - startA))) << 24) |
(int) ((startR + (int) (fraction * (endR - startR))) << 16) |
(int) ((startG + (int) (fraction * (endG - startG))) << 8) |
(int) ((startB + (int) (fraction * (endB - startB))));
}
@Override
public void render() {
Notification notification = getContent();
@ -53,20 +102,22 @@ public class NotificationRenderer extends Renderer<Notification> {
/**
* Cleans up the notification text and sets it as the title
* Clean up is required to fix escaped HTML string and extra white spaces at the beginning of the notification
*
* @param notificationText
*/
private void setTitle(String notificationText) {
notificationText = notificationText.trim().replaceAll("(^\\s*)|(\\s*$)", "");
notificationText = Html.fromHtml(notificationText).toString();
if(notificationText.length()>280){
notificationText = notificationText.substring(0,279);
if (notificationText.length() > 280) {
notificationText = notificationText.substring(0, 279);
notificationText = notificationText.concat("...");
}
notificationText = notificationText.concat(" ");
title.setText(notificationText);
}
public interface NotificationClicked{
public interface NotificationClicked {
void notificationClicked(Notification notification);
void markNotificationAsRead(Notification notification);
}
}

View file

@ -74,6 +74,11 @@ public class NotificationUtils {
return NotificationType.handledValueOf(type);
}
public static String getNotificationId(Node document) {
Element element = (Element) document;
return element.getAttribute("id");
}
public static List<Notification> getNotificationsFromBundle(Context context, Node document) {
Element bundledNotifications = getBundledNotifications(document);
NodeList childNodes = bundledNotifications.getChildNodes();
@ -154,7 +159,8 @@ public class NotificationUtils {
notificationText = getWelcomeMessage(context, document);
break;
}
return new Notification(type, notificationText, getTimestamp(document), description, link, iconUrl, getTimestampWithYear(document));
return new Notification(type, notificationText, getTimestamp(document), description, link, iconUrl, getTimestampWithYear(document),
getNotificationId(document));
}
private static String getNotificationText(Node document) {

View file

@ -1,81 +0,0 @@
package fr.free.nrw.commons.notification;
import android.os.AsyncTask;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Date;
import java.util.List;
import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.contributions.ContributionsFragment;
import timber.log.Timber;
/**
* This asynctask will check unread notifications after a date (date user check notifications last)
*/
public class UnreadNotificationsCheckAsync extends AsyncTask<Void, Void, Notification> {
WeakReference<MainActivity> context;
NotificationController notificationController;
public UnreadNotificationsCheckAsync(MainActivity context, NotificationController notificationController) {
this.context = new WeakReference<>(context);
this.notificationController = notificationController;
}
@Override
protected Notification doInBackground(Void... voids) {
Notification lastNotification = null;
try {
lastNotification = findLastNotification(notificationController.getNotifications());
} catch (IOException e) {
e.printStackTrace();
}
return lastNotification;
}
@Override
protected void onPostExecute(Notification lastNotification) {
super.onPostExecute(lastNotification);
if (lastNotification == null) {
return;
}
Date lastNotificationCheckDate = new Date(context.get()
.getSharedPreferences("defaultKvStore",0)
.getLong("last_read_notification_date", 0));
Timber.d("You may have unread notifications since"+lastNotificationCheckDate);
boolean isThereUnreadNotifications;
Date lastReadNotificationDate = new java.util.Date(Long.parseLong(lastNotification.dateWithYear)*1000);
if (lastNotificationCheckDate.before(lastReadNotificationDate)) {
isThereUnreadNotifications = true;
} else {
isThereUnreadNotifications = false;
}
// Check if activity is still running
if (context.get().getWindow().getDecorView().isShown() && !context.get().isFinishing()) {
// Check if fragment is not null and visible
if (context.get().isContributionsFragmentVisible && context.get().contributionsActivityPagerAdapter.getItem(0) != null) {
((ContributionsFragment)(context.get().contributionsActivityPagerAdapter.getItem(0))).updateNotificationsNotification(isThereUnreadNotifications);
}
}
}
private Notification findLastNotification(List<Notification> allNotifications) {
if (allNotifications.size() > 0) {
return allNotifications.get(allNotifications.size()-1);
} else {
return null;
}
}
}