mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
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:
parent
9451b00a15
commit
1b62ac4d2d
22 changed files with 381 additions and 195 deletions
|
|
@ -88,6 +88,9 @@ dependencies {
|
|||
implementation "com.android.support:customtabs:$SUPPORT_LIB_VERSION"
|
||||
implementation "com.android.support:cardview-v7:$SUPPORT_LIB_VERSION"
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||
//swipe_layout
|
||||
implementation 'com.daimajia.swipelayout:library:1.2.0@aar'
|
||||
implementation 'com.nineoldandroids:library:2.4.0'
|
||||
}
|
||||
|
||||
android {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ import android.widget.CheckBox;
|
|||
import android.widget.Toast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
|
@ -56,7 +55,6 @@ import fr.free.nrw.commons.nearby.NearbyController;
|
|||
import fr.free.nrw.commons.nearby.NearbyNotificationCardView;
|
||||
import fr.free.nrw.commons.nearby.Place;
|
||||
import fr.free.nrw.commons.notification.NotificationController;
|
||||
import fr.free.nrw.commons.notification.UnreadNotificationsCheckAsync;
|
||||
import fr.free.nrw.commons.settings.Prefs;
|
||||
import fr.free.nrw.commons.upload.UploadService;
|
||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||
|
|
@ -87,7 +85,6 @@ public class ContributionsFragment
|
|||
@Inject @Named("default_preferences") BasicKvStore defaultKvStore;
|
||||
@Inject ContributionDao contributionDao;
|
||||
@Inject MediaWikiApi mediaWikiApi;
|
||||
@Inject NotificationController notificationController;
|
||||
@Inject NearbyController nearbyController;
|
||||
|
||||
private ArrayList<DataSetObserver> observersWaitingForLoad = new ArrayList<>();
|
||||
|
|
@ -213,7 +210,6 @@ public class ContributionsFragment
|
|||
if (((MainActivity)getActivity()).isAuthCookieAcquired && !isFragmentAttachedBefore) {
|
||||
onAuthCookieAcquired(((MainActivity)getActivity()).uploadServiceIntent);
|
||||
isFragmentAttachedBefore = true;
|
||||
new UnreadNotificationsCheckAsync((MainActivity) getActivity(), notificationController).execute();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -478,14 +474,6 @@ public class ContributionsFragment
|
|||
displayUploadCount(betaUploadCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates notification indicator on toolbar to indicate there are unread notifications
|
||||
* @param isThereUnreadNotifications true if user checked notifications before last notification date
|
||||
*/
|
||||
public void updateNotificationsNotification(boolean isThereUnreadNotifications) {
|
||||
((MainActivity)getActivity()).updateNotificationIcon(isThereUnreadNotifications);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package fr.free.nrw.commons.contributions;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
|
|
@ -8,7 +9,6 @@ import android.support.design.widget.TabLayout;
|
|||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentPagerAdapter;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
|
|
@ -16,6 +16,7 @@ import android.view.MenuInflater;
|
|||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.esafirm.imagepicker.features.ImagePicker;
|
||||
import com.esafirm.imagepicker.model.Image;
|
||||
|
|
@ -35,10 +36,15 @@ import fr.free.nrw.commons.kvstore.BasicKvStore;
|
|||
import fr.free.nrw.commons.location.LocationServiceManager;
|
||||
import fr.free.nrw.commons.nearby.NearbyFragment;
|
||||
import fr.free.nrw.commons.nearby.NearbyNotificationCardView;
|
||||
import fr.free.nrw.commons.notification.Notification;
|
||||
import fr.free.nrw.commons.notification.NotificationActivity;
|
||||
import fr.free.nrw.commons.notification.NotificationController;
|
||||
import fr.free.nrw.commons.upload.UploadService;
|
||||
import fr.free.nrw.commons.utils.ImageUtils;
|
||||
import fr.free.nrw.commons.utils.IntentUtils;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static android.content.ContentResolver.requestSync;
|
||||
|
|
@ -58,6 +64,8 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
|
|||
@Inject
|
||||
@Named("default_preferences")
|
||||
public BasicKvStore defaultKvStore;
|
||||
@Inject
|
||||
NotificationController notificationController;
|
||||
|
||||
|
||||
public Intent uploadServiceIntent;
|
||||
|
|
@ -69,10 +77,12 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
|
|||
|
||||
public boolean isContributionsFragmentVisible = true; // False means nearby fragment is visible
|
||||
private Menu menu;
|
||||
private boolean isThereUnreadNotifications = false;
|
||||
|
||||
private boolean onOrientationChanged = false;
|
||||
|
||||
private MenuItem notificationsMenuItem;
|
||||
private TextView notificationCount;
|
||||
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_contributions);
|
||||
|
|
@ -82,6 +92,7 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
|
|||
initDrawer();
|
||||
setTitle(getString(R.string.navigation_item_home)); // Should I create a new string variable with another name instead?
|
||||
|
||||
|
||||
if (savedInstanceState != null ) {
|
||||
onOrientationChanged = true; // Will be used in nearby fragment to determine significant update of map
|
||||
|
||||
|
|
@ -126,9 +137,7 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
|
|||
tabLayout.getTabAt(1).setCustomView(nearbyTabLinearLayout);
|
||||
|
||||
nearbyInfo.setOnClickListener(view ->
|
||||
new AlertDialog.Builder(MainActivity.this)
|
||||
.setTitle(R.string.title_activity_nearby)
|
||||
.setMessage(R.string.showcase_view_whole_nearby_activity)
|
||||
new AlertDialog.Builder(MainActivity.this).setTitle(R.string.title_activity_nearby).setMessage(R.string.showcase_view_whole_nearby_activity)
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, id) -> dialog.cancel())
|
||||
.create()
|
||||
|
|
@ -278,18 +287,33 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
|
|||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.contribution_activity_notification_menu, menu);
|
||||
|
||||
if (!isThereUnreadNotifications) {
|
||||
// TODO: used vectors are not compatible with API 19 and below, change them
|
||||
menu.findItem(R.id.notifications).setIcon(ContextCompat.getDrawable(this, R.drawable.ic_notification_white_clip_art));
|
||||
} else {
|
||||
menu.findItem(R.id.notifications).setIcon(ContextCompat.getDrawable(this, R.drawable.ic_notification_white_clip_art_dot));
|
||||
notificationsMenuItem = menu.findItem(R.id.notifications);
|
||||
final View notification = notificationsMenuItem.getActionView();
|
||||
notificationCount = notification.findViewById(R.id.notification_count_badge);
|
||||
notification.setOnClickListener(view -> NotificationActivity.startYourself(MainActivity.this));
|
||||
this.menu = menu;
|
||||
updateMenuItem();
|
||||
setNotificationCount();
|
||||
return true;
|
||||
}
|
||||
|
||||
this.menu = menu;
|
||||
@SuppressLint("CheckResult")
|
||||
private void setNotificationCount() {
|
||||
Observable.fromCallable(() -> notificationController.getNotifications())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::initNotificationViews,
|
||||
throwable -> Timber.e(throwable, "Error occurred while loading notifications"));
|
||||
}
|
||||
|
||||
updateMenuItem();
|
||||
|
||||
return true;
|
||||
private void initNotificationViews(List<Notification> notificationList) {
|
||||
Timber.d("Number of notifications is %d", notificationList.size());
|
||||
if (notificationList.isEmpty()) {
|
||||
notificationCount.setVisibility(View.GONE);
|
||||
} else {
|
||||
notificationCount.setVisibility(View.VISIBLE);
|
||||
notificationCount.setText(String.valueOf(notificationList.size()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -319,7 +343,7 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
|
|||
// Starts notification activity on click to notification icon
|
||||
NotificationActivity.startYourself(this);
|
||||
return true;
|
||||
case R.id.list_sheet:
|
||||
case R.id.list_sheet:NotificationActivity.startYourself(this);
|
||||
if (contributionsActivityPagerAdapter.getItem(1) != null) {
|
||||
((NearbyFragment)contributionsActivityPagerAdapter.getItem(1)).listOptionMenuIteClicked();
|
||||
}
|
||||
|
|
@ -335,21 +359,6 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
|
|||
pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update notification icon if there is an unread notification
|
||||
* @param isThereUnreadNotifications true if user didn't visit notifications activity since
|
||||
* latest notification came to account
|
||||
*/
|
||||
public void updateNotificationIcon(boolean isThereUnreadNotifications) {
|
||||
if (!isThereUnreadNotifications) {
|
||||
this.isThereUnreadNotifications = false;
|
||||
menu.findItem(R.id.notifications).setIcon(ContextCompat.getDrawable(this, R.drawable.ic_notification_white_clip_art));
|
||||
} else {
|
||||
this.isThereUnreadNotifications = true;
|
||||
menu.findItem(R.id.notifications).setIcon(ContextCompat.getDrawable(this, R.drawable.ic_notification_white_clip_art_dot));
|
||||
}
|
||||
}
|
||||
|
||||
public class ContributionsActivityPagerAdapter extends FragmentPagerAdapter {
|
||||
FragmentManager fragmentManager;
|
||||
private boolean isContributionsListFragment = true; // to know what to put in first tab, Contributions of Media Details
|
||||
|
|
|
|||
|
|
@ -164,17 +164,6 @@ public class CommonsApplicationModule {
|
|||
return new JsonKvStore(context, "direct_nearby_upload_prefs", gson);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is used to determine when user is viewed notifications activity last
|
||||
* @param context
|
||||
* @return date of lastReadNotificationDate
|
||||
*/
|
||||
@Provides
|
||||
@Named("last_read_notification_date")
|
||||
public BasicKvStore providesLastReadNotificationDateKvStore(Context context) {
|
||||
return new BasicKvStore(context, "last_read_notification_date");
|
||||
}
|
||||
|
||||
@Provides
|
||||
public UploadController providesUploadController(SessionManager sessionManager,
|
||||
@Named("default_preferences") BasicKvStore kvStore,
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ import fr.free.nrw.commons.notification.Notification;
|
|||
import fr.free.nrw.commons.notification.NotificationUtils;
|
||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||
import fr.free.nrw.commons.utils.DateUtils;
|
||||
import fr.free.nrw.commons.utils.StringUtils;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import in.yuvi.http.fluent.Http;
|
||||
import io.reactivex.Observable;
|
||||
|
|
@ -587,6 +588,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
.param("meta", "notifications")
|
||||
.param("notformat", "model")
|
||||
.param("notwikis", "wikidatawiki|commonswiki|enwiki")
|
||||
.param("notfilter","!read")
|
||||
.get()
|
||||
.getNode("/api/query/notifications/list");
|
||||
} catch (IOException e) {
|
||||
|
|
@ -599,11 +601,26 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
|| notificationNode.getDocument().getChildNodes().getLength() == 0) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
NodeList childNodes = notificationNode.getDocument().getChildNodes();
|
||||
return NotificationUtils.getNotificationsFromList(context, childNodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markNotificationAsRead(Notification notification) throws IOException {
|
||||
Timber.d("Trying to mark notification as read: %s", notification.toString());
|
||||
String result = api.action("echomarkread")
|
||||
.param("token", getEditToken())
|
||||
.param("list", notification.notificationId)
|
||||
.post()
|
||||
.getString("/api/query/echomarkread/@result");
|
||||
|
||||
if (StringUtils.isNullOrWhiteSpace(result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return result.equals("success");
|
||||
}
|
||||
|
||||
/**
|
||||
* The method takes categoryName as input and returns a List of Subcategories
|
||||
* It uses the generator query API to get the subcategories in a category, 500 at a time.
|
||||
|
|
|
|||
|
|
@ -84,6 +84,9 @@ public interface MediaWikiApi {
|
|||
@NonNull
|
||||
List<Notification> getNotifications() throws IOException;
|
||||
|
||||
@NonNull
|
||||
boolean markNotificationAsRead(Notification notification) throws IOException;
|
||||
|
||||
@NonNull
|
||||
Observable<String> searchTitles(String title, int searchCatsLimit);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
@ -67,6 +69,41 @@ public class NotificationActivity extends NavigationBaseActivity {
|
|||
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);
|
||||
|
|
@ -88,12 +125,6 @@ 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) {
|
||||
Observable.fromCallable(() -> {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
|
|
@ -104,7 +135,13 @@ public class NotificationActivity extends NavigationBaseActivity {
|
|||
.subscribe(notificationList -> {
|
||||
Collections.reverse(notificationList);
|
||||
Timber.d("Number of notifications is %d", notificationList.size());
|
||||
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 -> {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,4 +36,7 @@ public class NotificationController {
|
|||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
public boolean markAsRead(Notification notification) throws IOException{
|
||||
return mediaWikiApi.markNotificationAsRead(notification);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,6 +102,7 @@ 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) {
|
||||
|
|
@ -68,5 +118,6 @@ public class NotificationRenderer extends Renderer<Notification> {
|
|||
|
||||
public interface NotificationClicked {
|
||||
void notificationClicked(Notification notification);
|
||||
void markNotificationAsRead(Notification notification);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
5
app/src/main/res/drawable/ic_delete_black_24dp.xml
Normal file
5
app/src/main/res/drawable/ic_delete_black_24dp.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#959594"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M20,18.69L7.84,6.14 5.27,3.49 4,4.76l2.8,2.8v0.01c-0.52,0.99 -0.8,2.16 -0.8,3.42v5l-2,2v1h13.73l2,2L21,19.72l-1,-1.03zM12,22c1.11,0 2,-0.89 2,-2h-4c0,1.11 0.89,2 2,2zM18,14.68L18,11c0,-3.08 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68c-0.15,0.03 -0.29,0.08 -0.42,0.12 -0.1,0.03 -0.2,0.07 -0.3,0.11h-0.01c-0.01,0 -0.01,0 -0.02,0.01 -0.23,0.09 -0.46,0.2 -0.68,0.31 0,0 -0.01,0 -0.01,0.01L18,14.68z"/>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/notification_badge.xml
Normal file
10
app/src/main/res/drawable/notification_badge.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
|
||||
<solid android:color="#F00" />
|
||||
|
||||
<size
|
||||
android:width="12dp"
|
||||
android:height="12dp" />
|
||||
</shape>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/drawer_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
|
@ -30,6 +31,45 @@
|
|||
/>
|
||||
</RelativeLayout>
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:id="@+id/no_notification_background"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="118dp"
|
||||
android:layout_height="172dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/textView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.501"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.763"
|
||||
app:srcCompat="@drawable/ic_notifications_off_black_24dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginBottom="188dp"
|
||||
android:text="@string/no_notification"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
||||
<android.support.design.widget.NavigationView
|
||||
android:id="@+id/navigation_view"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,36 @@
|
|||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:foreground="?selectableItemBackground"
|
||||
android:minHeight="72dp">
|
||||
<com.daimajia.swipe.SwipeLayout android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/swipeLayout"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/bottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<RelativeLayout
|
||||
android:id="@+id/bottom_wrapper_child1"
|
||||
android:background="@color/deleteRed"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<ImageView
|
||||
android:id="@+id/star"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
app:srcCompat="@drawable/ic_done_black_24dp"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
<android.support.v7.widget.AppCompatImageView
|
||||
android:id="@+id/icon"
|
||||
|
|
@ -16,9 +42,9 @@
|
|||
android:background="@android:color/white"
|
||||
android:scaleType="centerCrop"
|
||||
app:srcCompat="@drawable/ic_message_black_24dp"
|
||||
app:tint="@color/primaryDarkColor"
|
||||
/>
|
||||
|
||||
/>
|
||||
<!--app:tint="@color/primaryDarkColor"-->
|
||||
<TextView
|
||||
android:id="@+id/time"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
@ -48,3 +74,5 @@
|
|||
android:padding="12dp"
|
||||
/>
|
||||
</RelativeLayout>
|
||||
</com.daimajia.swipe.SwipeLayout>
|
||||
</RelativeLayout>
|
||||
38
app/src/main/res/layout/notification_icon.xml
Normal file
38
app/src/main/res/layout/notification_icon.xml
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center"
|
||||
tools:background="@color/black">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/cart_icon_image_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:gravity="center"
|
||||
android:layout_marginRight="8dp"
|
||||
app:srcCompat="@drawable/ic_notifications_white_24dp" />
|
||||
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/notification_count_badge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignTop="@id/cart_icon_image_view"
|
||||
android:layout_alignEnd="@id/cart_icon_image_view"
|
||||
android:layout_alignRight="@id/cart_icon_image_view"
|
||||
android:background="@drawable/notification_badge"
|
||||
android:gravity="center"
|
||||
android:padding="2dp"
|
||||
android:textColor="#ffffffff"
|
||||
android:textSize="7sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
tools:text="9+"
|
||||
tools:visibility="visible" />
|
||||
</RelativeLayout>
|
||||
|
|
@ -3,9 +3,9 @@
|
|||
<item android:id="@+id/notifications"
|
||||
android:title="@string/notifications"
|
||||
app:showAsAction="ifRoom|withText"
|
||||
android:icon="@drawable/ic_notifications_white_24dp"
|
||||
android:menuCategory="secondary"
|
||||
app:actionLayout="@layout/notification_icon"
|
||||
/>
|
||||
|
||||
<item android:id="@+id/list_sheet"
|
||||
android:title="@string/list_sheet"
|
||||
app:showAsAction="ifRoom|withText"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Main application background color -->
|
||||
<color name="main_background_dark">#303030</color>
|
||||
<color name="main_background_light">#fafafa</color>
|
||||
|
|
@ -57,4 +57,6 @@
|
|||
<color name="opak_middle_grey">#757575</color>
|
||||
<color name="white">#FFFFFF</color>
|
||||
<color name="black">#000000</color>
|
||||
|
||||
<color name="swipe_red" tools:ignore="MissingDefaultResource">#FF0000</color>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -460,5 +460,7 @@ Upload your first media by touching the camera or gallery icon above.</string>
|
|||
<string name="no_image">No images used</string>
|
||||
<string name="no_image_reverted">No images reverted</string>
|
||||
<string name="no_image_uploaded">No images uploaded</string>
|
||||
|
||||
<string name="no_notification">You have no unread Notification</string>
|
||||
<string name="share_logs_using">Share logs using</string>
|
||||
</resources>
|
||||
|
|
|
|||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue