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:customtabs:$SUPPORT_LIB_VERSION"
|
||||||
implementation "com.android.support:cardview-v7:$SUPPORT_LIB_VERSION"
|
implementation "com.android.support:cardview-v7:$SUPPORT_LIB_VERSION"
|
||||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
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 {
|
android {
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ import android.widget.CheckBox;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
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.NearbyNotificationCardView;
|
||||||
import fr.free.nrw.commons.nearby.Place;
|
import fr.free.nrw.commons.nearby.Place;
|
||||||
import fr.free.nrw.commons.notification.NotificationController;
|
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.settings.Prefs;
|
||||||
import fr.free.nrw.commons.upload.UploadService;
|
import fr.free.nrw.commons.upload.UploadService;
|
||||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||||
|
|
@ -87,8 +85,7 @@ public class ContributionsFragment
|
||||||
@Inject @Named("default_preferences") BasicKvStore defaultKvStore;
|
@Inject @Named("default_preferences") BasicKvStore defaultKvStore;
|
||||||
@Inject ContributionDao contributionDao;
|
@Inject ContributionDao contributionDao;
|
||||||
@Inject MediaWikiApi mediaWikiApi;
|
@Inject MediaWikiApi mediaWikiApi;
|
||||||
@Inject NotificationController notificationController;
|
@Inject NearbyController nearbyController;
|
||||||
@Inject NearbyController nearbyController;
|
|
||||||
|
|
||||||
private ArrayList<DataSetObserver> observersWaitingForLoad = new ArrayList<>();
|
private ArrayList<DataSetObserver> observersWaitingForLoad = new ArrayList<>();
|
||||||
private UploadService uploadService;
|
private UploadService uploadService;
|
||||||
|
|
@ -213,7 +210,6 @@ public class ContributionsFragment
|
||||||
if (((MainActivity)getActivity()).isAuthCookieAcquired && !isFragmentAttachedBefore) {
|
if (((MainActivity)getActivity()).isAuthCookieAcquired && !isFragmentAttachedBefore) {
|
||||||
onAuthCookieAcquired(((MainActivity)getActivity()).uploadServiceIntent);
|
onAuthCookieAcquired(((MainActivity)getActivity()).uploadServiceIntent);
|
||||||
isFragmentAttachedBefore = true;
|
isFragmentAttachedBefore = true;
|
||||||
new UnreadNotificationsCheckAsync((MainActivity) getActivity(), notificationController).execute();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -478,14 +474,6 @@ public class ContributionsFragment
|
||||||
displayUploadCount(betaUploadCount);
|
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
|
@Override
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package fr.free.nrw.commons.contributions;
|
package fr.free.nrw.commons.contributions;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
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.Fragment;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v4.app.FragmentPagerAdapter;
|
import android.support.v4.app.FragmentPagerAdapter;
|
||||||
import android.support.v4.content.ContextCompat;
|
|
||||||
import android.support.v4.view.ViewPager;
|
import android.support.v4.view.ViewPager;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
|
@ -16,6 +16,7 @@ import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.esafirm.imagepicker.features.ImagePicker;
|
import com.esafirm.imagepicker.features.ImagePicker;
|
||||||
import com.esafirm.imagepicker.model.Image;
|
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.location.LocationServiceManager;
|
||||||
import fr.free.nrw.commons.nearby.NearbyFragment;
|
import fr.free.nrw.commons.nearby.NearbyFragment;
|
||||||
import fr.free.nrw.commons.nearby.NearbyNotificationCardView;
|
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.NotificationActivity;
|
||||||
|
import fr.free.nrw.commons.notification.NotificationController;
|
||||||
import fr.free.nrw.commons.upload.UploadService;
|
import fr.free.nrw.commons.upload.UploadService;
|
||||||
import fr.free.nrw.commons.utils.ImageUtils;
|
import fr.free.nrw.commons.utils.ImageUtils;
|
||||||
import fr.free.nrw.commons.utils.IntentUtils;
|
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 timber.log.Timber;
|
||||||
|
|
||||||
import static android.content.ContentResolver.requestSync;
|
import static android.content.ContentResolver.requestSync;
|
||||||
|
|
@ -58,6 +64,8 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
|
||||||
@Inject
|
@Inject
|
||||||
@Named("default_preferences")
|
@Named("default_preferences")
|
||||||
public BasicKvStore defaultKvStore;
|
public BasicKvStore defaultKvStore;
|
||||||
|
@Inject
|
||||||
|
NotificationController notificationController;
|
||||||
|
|
||||||
|
|
||||||
public Intent uploadServiceIntent;
|
public Intent uploadServiceIntent;
|
||||||
|
|
@ -69,10 +77,12 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
|
||||||
|
|
||||||
public boolean isContributionsFragmentVisible = true; // False means nearby fragment is visible
|
public boolean isContributionsFragmentVisible = true; // False means nearby fragment is visible
|
||||||
private Menu menu;
|
private Menu menu;
|
||||||
private boolean isThereUnreadNotifications = false;
|
|
||||||
|
|
||||||
private boolean onOrientationChanged = false;
|
private boolean onOrientationChanged = false;
|
||||||
|
|
||||||
|
private MenuItem notificationsMenuItem;
|
||||||
|
private TextView notificationCount;
|
||||||
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_contributions);
|
setContentView(R.layout.activity_contributions);
|
||||||
|
|
@ -82,6 +92,7 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
|
||||||
initDrawer();
|
initDrawer();
|
||||||
setTitle(getString(R.string.navigation_item_home)); // Should I create a new string variable with another name instead?
|
setTitle(getString(R.string.navigation_item_home)); // Should I create a new string variable with another name instead?
|
||||||
|
|
||||||
|
|
||||||
if (savedInstanceState != null ) {
|
if (savedInstanceState != null ) {
|
||||||
onOrientationChanged = true; // Will be used in nearby fragment to determine significant update of map
|
onOrientationChanged = true; // Will be used in nearby fragment to determine significant update of map
|
||||||
|
|
||||||
|
|
@ -126,13 +137,11 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
|
||||||
tabLayout.getTabAt(1).setCustomView(nearbyTabLinearLayout);
|
tabLayout.getTabAt(1).setCustomView(nearbyTabLinearLayout);
|
||||||
|
|
||||||
nearbyInfo.setOnClickListener(view ->
|
nearbyInfo.setOnClickListener(view ->
|
||||||
new AlertDialog.Builder(MainActivity.this)
|
new AlertDialog.Builder(MainActivity.this).setTitle(R.string.title_activity_nearby).setMessage(R.string.showcase_view_whole_nearby_activity)
|
||||||
.setTitle(R.string.title_activity_nearby)
|
.setCancelable(true)
|
||||||
.setMessage(R.string.showcase_view_whole_nearby_activity)
|
.setPositiveButton(android.R.string.ok, (dialog, id) -> dialog.cancel())
|
||||||
.setCancelable(true)
|
.create()
|
||||||
.setPositiveButton(android.R.string.ok, (dialog, id) -> dialog.cancel())
|
.show()
|
||||||
.create()
|
|
||||||
.show()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (uploadServiceIntent != null) {
|
if (uploadServiceIntent != null) {
|
||||||
|
|
@ -278,20 +287,35 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
|
||||||
MenuInflater inflater = getMenuInflater();
|
MenuInflater inflater = getMenuInflater();
|
||||||
inflater.inflate(R.menu.contribution_activity_notification_menu, menu);
|
inflater.inflate(R.menu.contribution_activity_notification_menu, menu);
|
||||||
|
|
||||||
if (!isThereUnreadNotifications) {
|
notificationsMenuItem = menu.findItem(R.id.notifications);
|
||||||
// TODO: used vectors are not compatible with API 19 and below, change them
|
final View notification = notificationsMenuItem.getActionView();
|
||||||
menu.findItem(R.id.notifications).setIcon(ContextCompat.getDrawable(this, R.drawable.ic_notification_white_clip_art));
|
notificationCount = notification.findViewById(R.id.notification_count_badge);
|
||||||
} else {
|
notification.setOnClickListener(view -> NotificationActivity.startYourself(MainActivity.this));
|
||||||
menu.findItem(R.id.notifications).setIcon(ContextCompat.getDrawable(this, R.drawable.ic_notification_white_clip_art_dot));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.menu = menu;
|
this.menu = menu;
|
||||||
|
|
||||||
updateMenuItem();
|
updateMenuItem();
|
||||||
|
setNotificationCount();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responsible with displaying required menu items according to displayed fragment.
|
* Responsible with displaying required menu items according to displayed fragment.
|
||||||
* Notifications icon when contributions list is visible, list sheet icon when nearby is visible
|
* Notifications icon when contributions list is visible, list sheet icon when nearby is visible
|
||||||
|
|
@ -319,7 +343,7 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
|
||||||
// Starts notification activity on click to notification icon
|
// Starts notification activity on click to notification icon
|
||||||
NotificationActivity.startYourself(this);
|
NotificationActivity.startYourself(this);
|
||||||
return true;
|
return true;
|
||||||
case R.id.list_sheet:
|
case R.id.list_sheet:NotificationActivity.startYourself(this);
|
||||||
if (contributionsActivityPagerAdapter.getItem(1) != null) {
|
if (contributionsActivityPagerAdapter.getItem(1) != null) {
|
||||||
((NearbyFragment)contributionsActivityPagerAdapter.getItem(1)).listOptionMenuIteClicked();
|
((NearbyFragment)contributionsActivityPagerAdapter.getItem(1)).listOptionMenuIteClicked();
|
||||||
}
|
}
|
||||||
|
|
@ -335,21 +359,6 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
|
||||||
pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
|
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 {
|
public class ContributionsActivityPagerAdapter extends FragmentPagerAdapter {
|
||||||
FragmentManager fragmentManager;
|
FragmentManager fragmentManager;
|
||||||
private boolean isContributionsListFragment = true; // to know what to put in first tab, Contributions of Media Details
|
private boolean isContributionsListFragment = true; // to know what to put in first tab, Contributions of Media Details
|
||||||
|
|
@ -471,7 +480,7 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
|
||||||
if (!isContributionsFragmentVisible) {
|
if (!isContributionsFragmentVisible) {
|
||||||
viewPager.setCurrentItem(CONTRIBUTIONS_TAB_POSITION);
|
viewPager.setCurrentItem(CONTRIBUTIONS_TAB_POSITION);
|
||||||
|
|
||||||
// TODO: If contrib fragment is visible and location permission is not given, display permission request button
|
// TODO: If contrib fragment is visible and location permission is not given, display permission request button
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -164,17 +164,6 @@ public class CommonsApplicationModule {
|
||||||
return new JsonKvStore(context, "direct_nearby_upload_prefs", gson);
|
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
|
@Provides
|
||||||
public UploadController providesUploadController(SessionManager sessionManager,
|
public UploadController providesUploadController(SessionManager sessionManager,
|
||||||
@Named("default_preferences") BasicKvStore kvStore,
|
@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.notification.NotificationUtils;
|
||||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||||
import fr.free.nrw.commons.utils.DateUtils;
|
import fr.free.nrw.commons.utils.DateUtils;
|
||||||
|
import fr.free.nrw.commons.utils.StringUtils;
|
||||||
import fr.free.nrw.commons.utils.ViewUtil;
|
import fr.free.nrw.commons.utils.ViewUtil;
|
||||||
import in.yuvi.http.fluent.Http;
|
import in.yuvi.http.fluent.Http;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
|
|
@ -82,7 +83,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
||||||
private Gson gson;
|
private Gson gson;
|
||||||
private final OkHttpClient okHttpClient;
|
private final OkHttpClient okHttpClient;
|
||||||
private final String WIKIMEDIA_CAMPAIGNS_BASE_URL =
|
private final String WIKIMEDIA_CAMPAIGNS_BASE_URL =
|
||||||
"https://raw.githubusercontent.com/commons-app/campaigns/master/campaigns.json";
|
"https://raw.githubusercontent.com/commons-app/campaigns/master/campaigns.json";
|
||||||
|
|
||||||
private final String ERROR_CODE_BAD_TOKEN = "badtoken";
|
private final String ERROR_CODE_BAD_TOKEN = "badtoken";
|
||||||
|
|
||||||
|
|
@ -587,6 +588,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
||||||
.param("meta", "notifications")
|
.param("meta", "notifications")
|
||||||
.param("notformat", "model")
|
.param("notformat", "model")
|
||||||
.param("notwikis", "wikidatawiki|commonswiki|enwiki")
|
.param("notwikis", "wikidatawiki|commonswiki|enwiki")
|
||||||
|
.param("notfilter","!read")
|
||||||
.get()
|
.get()
|
||||||
.getNode("/api/query/notifications/list");
|
.getNode("/api/query/notifications/list");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
@ -599,11 +601,26 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
||||||
|| notificationNode.getDocument().getChildNodes().getLength() == 0) {
|
|| notificationNode.getDocument().getChildNodes().getLength() == 0) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeList childNodes = notificationNode.getDocument().getChildNodes();
|
NodeList childNodes = notificationNode.getDocument().getChildNodes();
|
||||||
return NotificationUtils.getNotificationsFromList(context, childNodes);
|
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
|
* 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.
|
* 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
|
@NonNull
|
||||||
List<Notification> getNotifications() throws IOException;
|
List<Notification> getNotifications() throws IOException;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
boolean markNotificationAsRead(Notification notification) throws IOException;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
Observable<String> searchTitles(String title, int searchCatsLimit);
|
Observable<String> searchTitles(String title, int searchCatsLimit);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,9 @@ public class Notification {
|
||||||
public String link;
|
public String link;
|
||||||
public String iconUrl;
|
public String iconUrl;
|
||||||
public String dateWithYear;
|
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.notificationType = notificationType;
|
||||||
this.notificationText = notificationText;
|
this.notificationText = notificationText;
|
||||||
this.date = date;
|
this.date = date;
|
||||||
|
|
@ -21,5 +22,20 @@ public class Notification {
|
||||||
this.link = link;
|
this.link = link;
|
||||||
this.iconUrl = iconUrl;
|
this.iconUrl = iconUrl;
|
||||||
this.dateWithYear = dateWithYear;
|
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;
|
package fr.free.nrw.commons.notification;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.FragmentManager;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.constraint.ConstraintLayout;
|
||||||
import android.support.design.widget.Snackbar;
|
import android.support.design.widget.Snackbar;
|
||||||
import android.support.v7.widget.DividerItemDecoration;
|
import android.support.v7.widget.DividerItemDecoration;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
|
@ -13,23 +13,19 @@ import android.support.v7.widget.RecyclerView;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.pedrogomez.renderers.RVRendererAdapter;
|
import com.pedrogomez.renderers.RVRendererAdapter;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
|
||||||
|
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.Utils;
|
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.theme.NavigationBaseActivity;
|
||||||
import fr.free.nrw.commons.utils.NetworkUtils;
|
import fr.free.nrw.commons.utils.NetworkUtils;
|
||||||
import fr.free.nrw.commons.utils.ViewUtil;
|
import fr.free.nrw.commons.utils.ViewUtil;
|
||||||
|
|
@ -44,17 +40,23 @@ import timber.log.Timber;
|
||||||
|
|
||||||
public class NotificationActivity extends NavigationBaseActivity {
|
public class NotificationActivity extends NavigationBaseActivity {
|
||||||
NotificationAdapterFactory notificationAdapterFactory;
|
NotificationAdapterFactory notificationAdapterFactory;
|
||||||
|
@BindView(R.id.listView)
|
||||||
@BindView(R.id.listView) RecyclerView recyclerView;
|
RecyclerView recyclerView;
|
||||||
@BindView(R.id.progressBar) ProgressBar progressBar;
|
@BindView(R.id.progressBar)
|
||||||
@BindView(R.id.container) RelativeLayout relativeLayout;
|
ProgressBar progressBar;
|
||||||
|
@BindView(R.id.container)
|
||||||
@Inject NotificationController controller;
|
RelativeLayout relativeLayout;
|
||||||
@Inject MediaWikiApi mediaWikiApi;
|
@BindView(R.id.no_notification_background)
|
||||||
@Inject @Named("last_read_notification_date") BasicKvStore kvStore;
|
ConstraintLayout no_notification;
|
||||||
|
/* @BindView(R.id.swipe_bg)
|
||||||
|
TextView swipe_bg;*/
|
||||||
|
@Inject
|
||||||
|
NotificationController controller;
|
||||||
|
|
||||||
private static final String TAG_NOTIFICATION_WORKER_FRAGMENT = "NotificationWorkerFragment";
|
private static final String TAG_NOTIFICATION_WORKER_FRAGMENT = "NotificationWorkerFragment";
|
||||||
private NotificationWorkerFragment mNotificationWorkerFragment;
|
private NotificationWorkerFragment mNotificationWorkerFragment;
|
||||||
|
private RVRendererAdapter<Notification> adapter;
|
||||||
|
private List<Notification> notificationList;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
|
@ -62,11 +64,46 @@ public class NotificationActivity extends NavigationBaseActivity {
|
||||||
setContentView(R.layout.activity_notification);
|
setContentView(R.layout.activity_notification);
|
||||||
ButterKnife.bind(this);
|
ButterKnife.bind(this);
|
||||||
mNotificationWorkerFragment = (NotificationWorkerFragment) getFragmentManager()
|
mNotificationWorkerFragment = (NotificationWorkerFragment) getFragmentManager()
|
||||||
.findFragmentByTag(TAG_NOTIFICATION_WORKER_FRAGMENT);
|
.findFragmentByTag(TAG_NOTIFICATION_WORKER_FRAGMENT);
|
||||||
initListView();
|
initListView();
|
||||||
initDrawer();
|
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() {
|
private void initListView() {
|
||||||
recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||||
DividerItemDecoration itemDecor = new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL);
|
DividerItemDecoration itemDecor = new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL);
|
||||||
|
|
@ -77,9 +114,9 @@ public class NotificationActivity extends NavigationBaseActivity {
|
||||||
private void refresh() {
|
private void refresh() {
|
||||||
if (!NetworkUtils.isInternetConnectionEstablished(this)) {
|
if (!NetworkUtils.isInternetConnectionEstablished(this)) {
|
||||||
progressBar.setVisibility(View.GONE);
|
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();
|
.setAction(R.string.retry, view -> refresh()).show();
|
||||||
}else {
|
} else {
|
||||||
progressBar.setVisibility(View.VISIBLE);
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
addNotifications();
|
addNotifications();
|
||||||
}
|
}
|
||||||
|
|
@ -88,13 +125,7 @@ public class NotificationActivity extends NavigationBaseActivity {
|
||||||
@SuppressLint("CheckResult")
|
@SuppressLint("CheckResult")
|
||||||
private void addNotifications() {
|
private void addNotifications() {
|
||||||
Timber.d("Add notifications");
|
Timber.d("Add notifications");
|
||||||
|
if (mNotificationWorkerFragment == null) {
|
||||||
// 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(() -> {
|
Observable.fromCallable(() -> {
|
||||||
progressBar.setVisibility(View.VISIBLE);
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
return controller.getNotifications();
|
return controller.getNotifications();
|
||||||
|
|
@ -104,7 +135,13 @@ public class NotificationActivity extends NavigationBaseActivity {
|
||||||
.subscribe(notificationList -> {
|
.subscribe(notificationList -> {
|
||||||
Collections.reverse(notificationList);
|
Collections.reverse(notificationList);
|
||||||
Timber.d("Number of notifications is %d", notificationList.size());
|
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);
|
progressBar.setVisibility(View.GONE);
|
||||||
}, throwable -> {
|
}, throwable -> {
|
||||||
Timber.e(throwable, "Error occurred while loading notifications");
|
Timber.e(throwable, "Error occurred while loading notifications");
|
||||||
|
|
@ -112,7 +149,8 @@ public class NotificationActivity extends NavigationBaseActivity {
|
||||||
progressBar.setVisibility(View.GONE);
|
progressBar.setVisibility(View.GONE);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setAdapter(mNotificationWorkerFragment.getNotificationList());
|
notificationList = mNotificationWorkerFragment.getNotificationList();
|
||||||
|
setAdapter(notificationList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,13 +164,28 @@ public class NotificationActivity extends NavigationBaseActivity {
|
||||||
private void setAdapter(List<Notification> notificationList) {
|
private void setAdapter(List<Notification> notificationList) {
|
||||||
if (notificationList == null || notificationList.isEmpty()) {
|
if (notificationList == null || notificationList.isEmpty()) {
|
||||||
ViewUtil.showShortSnackbar(relativeLayout, R.string.no_notifications);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
notificationAdapterFactory = new NotificationAdapterFactory(notification -> {
|
notificationAdapterFactory = new NotificationAdapterFactory(new NotificationRenderer.NotificationClicked() {
|
||||||
Timber.d("Notification clicked %s", notification.link);
|
@Override
|
||||||
handleUrl(notification.link);
|
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);
|
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);
|
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,4 +36,7 @@ public class NotificationController {
|
||||||
}
|
}
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
public boolean markAsRead(Notification notification) throws IOException{
|
||||||
|
return mediaWikiApi.markNotificationAsRead(notification);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,55 @@
|
||||||
package fr.free.nrw.commons.notification;
|
package fr.free.nrw.commons.notification;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.nineoldandroids.view.ViewHelper;
|
||||||
import com.pedrogomez.renderers.Renderer;
|
import com.pedrogomez.renderers.Renderer;
|
||||||
|
import com.daimajia.swipe.SwipeLayout;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
|
import butterknife.OnClick;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by root on 19.12.2017.
|
* Created by root on 19.12.2017.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class NotificationRenderer extends Renderer<Notification> {
|
public class NotificationRenderer extends Renderer<Notification> {
|
||||||
@BindView(R.id.title) TextView title;
|
@BindView(R.id.title)
|
||||||
@BindView(R.id.time) TextView time;
|
TextView title;
|
||||||
@BindView(R.id.icon) ImageView icon;
|
@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;
|
private NotificationClicked listener;
|
||||||
|
|
||||||
|
|
||||||
NotificationRenderer(NotificationClicked listener) {
|
NotificationRenderer(NotificationClicked listener) {
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
@OnClick(R.id.bottom)
|
||||||
|
void onBottomLayoutClicked(){
|
||||||
|
Notification notification = getContent();
|
||||||
|
Timber.d("NotificationID: %s", notification.notificationId);
|
||||||
|
listener.markNotificationAsRead(notification);
|
||||||
|
}
|
||||||
@Override
|
@Override
|
||||||
protected void setUpView(View view) { }
|
protected void setUpView(View rootView) {
|
||||||
|
|
||||||
|
}
|
||||||
@Override
|
@Override
|
||||||
protected void hookListeners(View rootView) {
|
protected void hookListeners(View rootView) {
|
||||||
rootView.setOnClickListener(v -> listener.notificationClicked(getContent()));
|
rootView.setOnClickListener(v -> listener.notificationClicked(getContent()));
|
||||||
|
|
@ -40,9 +59,39 @@ public class NotificationRenderer extends Renderer<Notification> {
|
||||||
protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) {
|
protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) {
|
||||||
View inflatedView = layoutInflater.inflate(R.layout.item_notification, viewGroup, false);
|
View inflatedView = layoutInflater.inflate(R.layout.item_notification, viewGroup, false);
|
||||||
ButterKnife.bind(this, inflatedView);
|
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;
|
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
|
@Override
|
||||||
public void render() {
|
public void render() {
|
||||||
Notification notification = getContent();
|
Notification notification = getContent();
|
||||||
|
|
@ -53,20 +102,22 @@ public class NotificationRenderer extends Renderer<Notification> {
|
||||||
/**
|
/**
|
||||||
* Cleans up the notification text and sets it as the title
|
* 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
|
* Clean up is required to fix escaped HTML string and extra white spaces at the beginning of the notification
|
||||||
|
*
|
||||||
* @param notificationText
|
* @param notificationText
|
||||||
*/
|
*/
|
||||||
private void setTitle(String notificationText) {
|
private void setTitle(String notificationText) {
|
||||||
notificationText = notificationText.trim().replaceAll("(^\\s*)|(\\s*$)", "");
|
notificationText = notificationText.trim().replaceAll("(^\\s*)|(\\s*$)", "");
|
||||||
notificationText = Html.fromHtml(notificationText).toString();
|
notificationText = Html.fromHtml(notificationText).toString();
|
||||||
if(notificationText.length()>280){
|
if (notificationText.length() > 280) {
|
||||||
notificationText = notificationText.substring(0,279);
|
notificationText = notificationText.substring(0, 279);
|
||||||
notificationText = notificationText.concat("...");
|
notificationText = notificationText.concat("...");
|
||||||
}
|
}
|
||||||
notificationText = notificationText.concat(" ");
|
notificationText = notificationText.concat(" ");
|
||||||
title.setText(notificationText);
|
title.setText(notificationText);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface NotificationClicked{
|
public interface NotificationClicked {
|
||||||
void notificationClicked(Notification notification);
|
void notificationClicked(Notification notification);
|
||||||
|
void markNotificationAsRead(Notification notification);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,11 @@ public class NotificationUtils {
|
||||||
return NotificationType.handledValueOf(type);
|
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) {
|
public static List<Notification> getNotificationsFromBundle(Context context, Node document) {
|
||||||
Element bundledNotifications = getBundledNotifications(document);
|
Element bundledNotifications = getBundledNotifications(document);
|
||||||
NodeList childNodes = bundledNotifications.getChildNodes();
|
NodeList childNodes = bundledNotifications.getChildNodes();
|
||||||
|
|
@ -154,7 +159,8 @@ public class NotificationUtils {
|
||||||
notificationText = getWelcomeMessage(context, document);
|
notificationText = getWelcomeMessage(context, document);
|
||||||
break;
|
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) {
|
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"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/drawer_layout"
|
android:id="@+id/drawer_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
@ -30,6 +31,45 @@
|
||||||
/>
|
/>
|
||||||
</RelativeLayout>
|
</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.support.design.widget.NavigationView
|
||||||
android:id="@+id/navigation_view"
|
android:id="@+id/navigation_view"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,36 @@
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:foreground="?selectableItemBackground"
|
android:foreground="?selectableItemBackground"
|
||||||
android:minHeight="72dp">
|
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.support.v7.widget.AppCompatImageView
|
||||||
android:id="@+id/icon"
|
android:id="@+id/icon"
|
||||||
|
|
@ -16,9 +42,9 @@
|
||||||
android:background="@android:color/white"
|
android:background="@android:color/white"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
app:srcCompat="@drawable/ic_message_black_24dp"
|
app:srcCompat="@drawable/ic_message_black_24dp"
|
||||||
app:tint="@color/primaryDarkColor"
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
/>
|
||||||
|
<!--app:tint="@color/primaryDarkColor"-->
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/time"
|
android:id="@+id/time"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
@ -47,4 +73,6 @@
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
||||||
android:padding="12dp"
|
android:padding="12dp"
|
||||||
/>
|
/>
|
||||||
|
</RelativeLayout>
|
||||||
|
</com.daimajia.swipe.SwipeLayout>
|
||||||
</RelativeLayout>
|
</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>
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<item android:id="@+id/notifications"
|
<item android:id="@+id/notifications"
|
||||||
android:title="@string/notifications"
|
android:title="@string/notifications"
|
||||||
app:showAsAction="ifRoom|withText"
|
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"
|
<item android:id="@+id/list_sheet"
|
||||||
android:title="@string/list_sheet"
|
android:title="@string/list_sheet"
|
||||||
app:showAsAction="ifRoom|withText"
|
app:showAsAction="ifRoom|withText"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!-- Main application background color -->
|
<!-- Main application background color -->
|
||||||
<color name="main_background_dark">#303030</color>
|
<color name="main_background_dark">#303030</color>
|
||||||
<color name="main_background_light">#fafafa</color>
|
<color name="main_background_light">#fafafa</color>
|
||||||
|
|
@ -57,4 +57,6 @@
|
||||||
<color name="opak_middle_grey">#757575</color>
|
<color name="opak_middle_grey">#757575</color>
|
||||||
<color name="white">#FFFFFF</color>
|
<color name="white">#FFFFFF</color>
|
||||||
<color name="black">#000000</color>
|
<color name="black">#000000</color>
|
||||||
|
|
||||||
|
<color name="swipe_red" tools:ignore="MissingDefaultResource">#FF0000</color>
|
||||||
</resources>
|
</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">No images used</string>
|
||||||
<string name="no_image_reverted">No images reverted</string>
|
<string name="no_image_reverted">No images reverted</string>
|
||||||
<string name="no_image_uploaded">No images uploaded</string>
|
<string name="no_image_uploaded">No images uploaded</string>
|
||||||
<string name="share_logs_using">Share logs using</string>
|
|
||||||
|
<string name="no_notification">You have no unread Notification</string>
|
||||||
|
<string name="share_logs_using">Share logs using</string>
|
||||||
</resources>
|
</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
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
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