diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e262e9088..253bdaea8 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -87,6 +87,10 @@
android:label="@string/title_activity_nearby"
android:parentActivityName=".contributions.ContributionsActivity" />
+
+
provideLruCache() {
return new LruCache<>(1024);
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java
index 19425a0c3..09e89e7b4 100644
--- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java
+++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java
@@ -1,5 +1,6 @@
package fr.free.nrw.commons.mwapi;
+import android.content.Context;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -21,6 +22,8 @@ import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.util.EntityUtils;
import org.mediawiki.api.ApiResult;
import org.mediawiki.api.MWApi;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
import java.io.IOException;
import java.io.InputStream;
@@ -36,11 +39,17 @@ import java.util.concurrent.Callable;
import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.PageTitle;
+import fr.free.nrw.commons.notification.Notification;
import in.yuvi.http.fluent.Http;
import io.reactivex.Observable;
import io.reactivex.Single;
import timber.log.Timber;
+import static fr.free.nrw.commons.notification.NotificationType.UNKNOWN;
+import static fr.free.nrw.commons.notification.NotificationUtils.getNotificationFromApiResult;
+import static fr.free.nrw.commons.notification.NotificationUtils.getNotificationType;
+import static fr.free.nrw.commons.notification.NotificationUtils.isCommonsNotification;
+
/**
* @author Addshore
*/
@@ -50,8 +59,10 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
private static final String THUMB_SIZE = "640";
private AbstractHttpClient httpClient;
private MWApi api;
+ private Context context;
- public ApacheHttpClientMediaWikiApi(String apiURL) {
+ public ApacheHttpClientMediaWikiApi(Context context, String apiURL) {
+ this.context = context;
BasicHttpParams params = new BasicHttpParams();
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
@@ -353,6 +364,42 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
.getString("/api/query/pages/page/revisions/rev");
}
+ @Override
+ @NonNull
+ public List getNotifications() {
+ ApiResult notificationNode = null;
+ try {
+ notificationNode = api.action("query")
+ .param("notprop", "list")
+ .param("format", "xml")
+ .param("meta", "notifications")
+ .param("notfilter", "!read")
+ .get()
+ .getNode("/api/query/notifications/list");
+ } catch (IOException e) {
+ Timber.e("Failed to obtain searchCategories", e);
+ }
+
+ if (notificationNode == null) {
+ return new ArrayList<>();
+ }
+
+ List notifications = new ArrayList<>();
+
+ NodeList childNodes = notificationNode.getDocument().getChildNodes();
+
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ Node node = childNodes.item(i);
+ if (isCommonsNotification(node)
+ && !getNotificationType(node).equals(UNKNOWN)) {
+ notifications.add(getNotificationFromApiResult(context, node));
+ }
+ }
+
+ return notifications;
+ }
+
+
@Override
public boolean existingFile(String fileSha1) throws IOException {
return api.action("query")
diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java
index 310c97a8a..70ab38297 100644
--- a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java
+++ b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java
@@ -5,7 +5,9 @@ import android.support.annotation.Nullable;
import java.io.IOException;
import java.io.InputStream;
+import java.util.List;
+import fr.free.nrw.commons.notification.Notification;
import io.reactivex.Observable;
import io.reactivex.Single;
@@ -43,6 +45,9 @@ public interface MediaWikiApi {
@NonNull
Observable allCategories(String filter, int searchCatsLimit);
+ @NonNull
+ List getNotifications() throws IOException;
+
@NonNull
Observable searchTitles(String title, int searchCatsLimit);
@@ -51,6 +56,8 @@ public interface MediaWikiApi {
boolean existingFile(String fileSha1) throws IOException;
+
+
@NonNull
LogEventResult logEvents(String user, String lastModified, String queryContinue, int limit) throws IOException;
diff --git a/app/src/main/java/fr/free/nrw/commons/notification/MarkReadResponse.java b/app/src/main/java/fr/free/nrw/commons/notification/MarkReadResponse.java
new file mode 100644
index 000000000..03cdd6f88
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/notification/MarkReadResponse.java
@@ -0,0 +1,16 @@
+package fr.free.nrw.commons.notification;
+
+import android.support.annotation.Nullable;
+
+public class MarkReadResponse {
+ @SuppressWarnings("unused") @Nullable
+ private String result;
+
+ public String result() {
+ return result;
+ }
+
+ public static class QueryMarkReadResponse {
+ @SuppressWarnings("unused") @Nullable private MarkReadResponse echomarkread;
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/notification/Notification.java b/app/src/main/java/fr/free/nrw/commons/notification/Notification.java
new file mode 100644
index 000000000..e4efbff6c
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/notification/Notification.java
@@ -0,0 +1,21 @@
+package fr.free.nrw.commons.notification;
+
+/**
+ * Created by root on 18.12.2017.
+ */
+
+public class Notification {
+ public NotificationType notificationType;
+ public String notificationText;
+ public String date;
+ public String description;
+ public String link;
+
+ public Notification(NotificationType notificationType, String notificationText, String date, String description, String link) {
+ this.notificationType = notificationType;
+ this.notificationText = notificationText;
+ this.date = date;
+ this.description = description;
+ this.link = link;
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java
new file mode 100644
index 000000000..c90e61318
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java
@@ -0,0 +1,88 @@
+package fr.free.nrw.commons.notification;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+
+import com.pedrogomez.renderers.RVRendererAdapter;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import fr.free.nrw.commons.R;
+import fr.free.nrw.commons.theme.NavigationBaseActivity;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+import timber.log.Timber;
+
+/**
+ * Created by root on 18.12.2017.
+ */
+
+public class NotificationActivity extends NavigationBaseActivity {
+ NotificationAdapterFactory notificationAdapterFactory;
+
+ @BindView(R.id.listView) RecyclerView recyclerView;
+
+ @Inject NotificationController controller;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_notification);
+ ButterKnife.bind(this);
+ initListView();
+ initDrawer();
+ }
+
+ private void initListView() {
+ recyclerView = findViewById(R.id.listView);
+ recyclerView.setLayoutManager(new LinearLayoutManager(this));
+ addNotifications();
+ }
+
+ @SuppressLint("CheckResult")
+ private void addNotifications() {
+ Timber.d("Add notifications");
+
+ Observable.fromCallable(() -> controller.getNotifications())
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(notificationList -> {
+ Timber.d("Number of notifications is %d", notificationList.size());
+ setAdapter(notificationList);
+ }, throwable -> {
+ Timber.e(throwable, "Error occurred while loading notifications");
+ });
+ }
+
+ private void handleUrl(String url) {
+ if (url == null || url.equals("")) {
+ return;
+ }
+ startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
+ }
+
+ private void setAdapter(List notificationList) {
+ notificationAdapterFactory = new NotificationAdapterFactory(notification -> {
+ Timber.d("Notification clicked %s", notification.link);
+ handleUrl(notification.link);
+ });
+ RVRendererAdapter adapter = notificationAdapterFactory.create(notificationList);
+ recyclerView.setAdapter(adapter);
+ }
+
+ public static void startYourself(Context context) {
+ Intent intent = new Intent(context, NotificationActivity.class);
+ context.startActivity(intent);
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationAdapterFactory.java b/app/src/main/java/fr/free/nrw/commons/notification/NotificationAdapterFactory.java
new file mode 100644
index 000000000..83a60dcfb
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationAdapterFactory.java
@@ -0,0 +1,30 @@
+package fr.free.nrw.commons.notification;
+
+import android.support.annotation.NonNull;
+
+import com.pedrogomez.renderers.ListAdapteeCollection;
+import com.pedrogomez.renderers.RVRendererAdapter;
+import com.pedrogomez.renderers.RendererBuilder;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Created by root on 19.12.2017.
+ */
+
+class NotificationAdapterFactory {
+ private NotificationRenderer.NotificationClicked listener;
+
+ NotificationAdapterFactory(@NonNull NotificationRenderer.NotificationClicked listener) {
+ this.listener = listener;
+ }
+
+ public RVRendererAdapter create(List notifications) {
+ RendererBuilder builder = new RendererBuilder()
+ .bind(Notification.class, new NotificationRenderer(listener));
+ ListAdapteeCollection collection = new ListAdapteeCollection<>(
+ notifications != null ? notifications : Collections.emptyList());
+ return new RVRendererAdapter<>(builder, collection);
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationController.java b/app/src/main/java/fr/free/nrw/commons/notification/NotificationController.java
new file mode 100644
index 000000000..b22bafbb5
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationController.java
@@ -0,0 +1,39 @@
+package fr.free.nrw.commons.notification;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import fr.free.nrw.commons.auth.SessionManager;
+import fr.free.nrw.commons.mwapi.MediaWikiApi;
+
+/**
+ * Created by root on 19.12.2017.
+ */
+@Singleton
+public class NotificationController {
+
+ private MediaWikiApi mediaWikiApi;
+ private SessionManager sessionManager;
+
+ @Inject
+ public NotificationController(MediaWikiApi mediaWikiApi, SessionManager sessionManager) {
+ this.mediaWikiApi = mediaWikiApi;
+ this.sessionManager = sessionManager;
+ }
+
+ public List getNotifications() throws IOException {
+ if (mediaWikiApi.validateLogin()) {
+ return mediaWikiApi.getNotifications();
+ } else {
+ Boolean authTokenValidated = sessionManager.revalidateAuthToken();
+ if (authTokenValidated != null && authTokenValidated) {
+ return mediaWikiApi.getNotifications();
+ }
+ }
+ return new ArrayList<>();
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationRenderer.java b/app/src/main/java/fr/free/nrw/commons/notification/NotificationRenderer.java
new file mode 100644
index 000000000..9bf3cec93
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationRenderer.java
@@ -0,0 +1,68 @@
+package fr.free.nrw.commons.notification;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.pedrogomez.renderers.Renderer;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import fr.free.nrw.commons.R;
+import fr.free.nrw.commons.utils.DateUtils;
+
+/**
+ * Created by root on 19.12.2017.
+ */
+
+public class NotificationRenderer extends Renderer {
+ @BindView(R.id.title) TextView title;
+ @BindView(R.id.description) TextView description;
+ @BindView(R.id.time) TextView time;
+ @BindView(R.id.icon) ImageView icon;
+ private NotificationClicked listener;
+
+
+ NotificationRenderer(NotificationClicked listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ protected void setUpView(View view) { }
+
+ @Override
+ protected void hookListeners(View rootView) {
+ rootView.setOnClickListener(v -> listener.notificationClicked(getContent()));
+ }
+
+ @Override
+ protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) {
+ View inflatedView = layoutInflater.inflate(R.layout.item_notification, viewGroup, false);
+ ButterKnife.bind(this, inflatedView);
+ return inflatedView;
+ }
+
+ @Override
+ public void render() {
+ Notification notification = getContent();
+ title.setText(notification.notificationText);
+ time.setText(notification.date);
+ description.setText(notification.description);
+ switch (notification.notificationType) {
+ case THANK_YOU_EDIT:
+ icon.setImageResource(R.drawable.ic_edit_black_24dp);
+ break;
+ default:
+ icon.setImageResource(R.drawable.round_icon_unknown);
+ }
+ }
+
+ public interface NotificationClicked{
+ void notificationClicked(Notification notification);
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationType.java b/app/src/main/java/fr/free/nrw/commons/notification/NotificationType.java
new file mode 100644
index 000000000..b83b23b2a
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationType.java
@@ -0,0 +1,27 @@
+package fr.free.nrw.commons.notification;
+
+public enum NotificationType {
+ THANK_YOU_EDIT("thank-you-edit"),
+ EDIT_USER_TALK("edit-user-talk"),
+ MENTION("mention"),
+ WELCOME("welcome"),
+ UNKNOWN("unknown");
+ private String type;
+
+ NotificationType(String type) {
+ this.type = type;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public static NotificationType handledValueOf(String name) {
+ for (NotificationType e : values()) {
+ if (e.getType().equals(name)) {
+ return e;
+ }
+ }
+ return UNKNOWN;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationUtils.java b/app/src/main/java/fr/free/nrw/commons/notification/NotificationUtils.java
new file mode 100644
index 000000000..7f32da126
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationUtils.java
@@ -0,0 +1,116 @@
+package fr.free.nrw.commons.notification;
+
+import android.content.Context;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.annotation.Nullable;
+
+import fr.free.nrw.commons.BuildConfig;
+import fr.free.nrw.commons.R;
+
+public class NotificationUtils {
+
+ private static final String COMMONS_WIKI = "commonswiki";
+
+ public static boolean isCommonsNotification(Node document) {
+ if (document == null || !document.hasAttributes()) {
+ return false;
+ }
+ Element element = (Element) document;
+ return COMMONS_WIKI.equals(element.getAttribute("wiki"));
+ }
+
+ public static NotificationType getNotificationType(Node document) {
+ Element element = (Element) document;
+ String type = element.getAttribute("type");
+ return NotificationType.handledValueOf(type);
+ }
+
+ public static Notification getNotificationFromApiResult(Context context, Node document) {
+ NotificationType type = getNotificationType(document);
+
+ String notificationText = "";
+ String link = getNotificationLink(document);
+ String description = getNotificationDescription(document);
+ switch (type) {
+ case THANK_YOU_EDIT:
+ notificationText = context.getString(R.string.notifications_thank_you_edit);
+ break;
+ case EDIT_USER_TALK:
+ notificationText = getUserTalkMessage(context, document);
+ break;
+ case MENTION:
+ notificationText = getMentionMessage(context, document);
+ break;
+ case WELCOME:
+ notificationText = getWelcomeMessage(context, document);
+ break;
+ }
+ return new Notification(type, notificationText, getTimestamp(document), description, link);
+ }
+
+ public static String getMentionMessage(Context context, Node document) {
+ String format = context.getString(R.string.notifications_mention);
+ return String.format(format, getAgent(document), getNotificationDescription(document));
+ }
+
+ public static String getUserTalkMessage(Context context, Node document) {
+ String format = context.getString(R.string.notifications_talk_page_message);
+ return String.format(format, getAgent(document));
+ }
+
+ public static String getWelcomeMessage(Context context, Node document) {
+ String welcomeMessageFormat = context.getString(R.string.notifications_welcome);
+ return String.format(welcomeMessageFormat, getAgent(document));
+ }
+
+ private static String getAgent(Node document) {
+ Element agentElement = (Element) getNode(document, "agent");
+ if (agentElement != null) {
+ return agentElement.getAttribute("name");
+ }
+ return "";
+ }
+
+ private static String getTimestamp(Node document) {
+ Element timestampElement = (Element) getNode(document, "timestamp");
+ if (timestampElement != null) {
+ return timestampElement.getAttribute("date");
+ }
+ return "";
+ }
+
+ private static String getNotificationLink(Node document) {
+ String format = "%s%s";
+ Element titleElement = (Element) getNode(document, "title");
+ if (titleElement != null) {
+ String fullName = titleElement.getAttribute("full");
+ return String.format(format, BuildConfig.HOME_URL, fullName);
+ }
+ return "";
+ }
+
+ private static String getNotificationDescription(Node document) {
+ Element titleElement = (Element) getNode(document, "title");
+ if (titleElement != null) {
+ return titleElement.getAttribute("text");
+ }
+ return "";
+ }
+
+ @Nullable
+ public static Node getNode(Node node, String nodeName) {
+ NodeList childNodes = node.getChildNodes();
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ Node nodeItem = childNodes.item(i);
+ Element item = (Element) nodeItem;
+ if (item.getTagName().equals(nodeName)) {
+ return nodeItem;
+ }
+ }
+ return null;
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java
index 60ea325e4..99c9f253b 100644
--- a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java
@@ -27,6 +27,7 @@ import fr.free.nrw.commons.auth.AccountUtil;
import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.contributions.ContributionsActivity;
import fr.free.nrw.commons.nearby.NearbyActivity;
+import fr.free.nrw.commons.notification.NotificationActivity;
import fr.free.nrw.commons.settings.SettingsActivity;
import timber.log.Timber;
@@ -143,6 +144,10 @@ public abstract class NavigationBaseActivity extends BaseActivity
.setNegativeButton(R.string.no, (dialog, which) -> dialog.cancel())
.show();
return true;
+ case R.id.action_notifications:
+ drawerLayout.closeDrawer(navigationView);
+ NotificationActivity.startYourself(this);
+ return true;
default:
Timber.e("Unknown option [%s] selected from the navigation menu", itemId);
return false;
diff --git a/app/src/main/java/fr/free/nrw/commons/utils/DateUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/DateUtils.java
new file mode 100644
index 000000000..6b3bf0377
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/utils/DateUtils.java
@@ -0,0 +1,36 @@
+package fr.free.nrw.commons.utils;
+
+import java.util.Calendar;
+import java.util.Date;
+
+public class DateUtils {
+ public static String getTimeAgo(Date currDate, Date itemDate) {
+ Calendar c = Calendar.getInstance();
+ c.setTime(currDate);
+ int yearNow = c.get(Calendar.YEAR);
+ int monthNow = c.get(Calendar.MONTH);
+ int dayNow = c.get(Calendar.DAY_OF_MONTH);
+ int hourNow = c.get(Calendar.HOUR_OF_DAY);
+ int minuteNow = c.get(Calendar.MINUTE);
+ c.setTime(itemDate);
+ int videoYear = c.get(Calendar.YEAR);
+ int videoMonth = c.get(Calendar.MONTH);
+ int videoDays = c.get(Calendar.DAY_OF_MONTH);
+ int videoHour = c.get(Calendar.HOUR_OF_DAY);
+ int videoMinute = c.get(Calendar.MINUTE);
+
+ if (yearNow != videoYear) {
+ return (String.valueOf(yearNow - videoYear) + "-" + "years");
+ } else if (monthNow != videoMonth) {
+ return (String.valueOf(monthNow - videoMonth) + "-" + "months");
+ } else if (dayNow != videoDays) {
+ return (String.valueOf(dayNow - videoDays) + "-" + "days");
+ } else if (hourNow != videoHour) {
+ return (String.valueOf(hourNow - videoHour) + "-" + "hours");
+ } else if (minuteNow != videoMinute) {
+ return (String.valueOf(minuteNow - videoMinute) + "-" + "minutes");
+ } else {
+ return "0-seconds";
+ }
+ }
+}
diff --git a/app/src/main/res/drawable/ic_chat_bubble_black_24px.xml b/app/src/main/res/drawable/ic_chat_bubble_black_24px.xml
new file mode 100644
index 000000000..8d40c6d63
--- /dev/null
+++ b/app/src/main/res/drawable/ic_chat_bubble_black_24px.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_edit_black_24dp.xml b/app/src/main/res/drawable/ic_edit_black_24dp.xml
new file mode 100644
index 000000000..2ab2fb753
--- /dev/null
+++ b/app/src/main/res/drawable/ic_edit_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_message_black_24dp.xml b/app/src/main/res/drawable/ic_message_black_24dp.xml
new file mode 100644
index 000000000..d2876bfad
--- /dev/null
+++ b/app/src/main/res/drawable/ic_message_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_notifications_black_24dp.xml b/app/src/main/res/drawable/ic_notifications_black_24dp.xml
new file mode 100644
index 000000000..7009a6763
--- /dev/null
+++ b/app/src/main/res/drawable/ic_notifications_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_notification.xml b/app/src/main/res/layout/activity_notification.xml
new file mode 100644
index 000000000..b2eb38475
--- /dev/null
+++ b/app/src/main/res/layout/activity_notification.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_notification.xml b/app/src/main/res/layout/item_notification.xml
new file mode 100644
index 000000000..d8f4dd8d4
--- /dev/null
+++ b/app/src/main/res/layout/item_notification.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/drawer.xml b/app/src/main/res/menu/drawer.xml
index f0a0a5e29..83c1bf0ad 100644
--- a/app/src/main/res/menu/drawer.xml
+++ b/app/src/main/res/menu/drawer.xml
@@ -35,4 +35,9 @@
android:icon="@drawable/ic_exit_to_app_black_24dp"
android:title="@string/navigation_item_logout"/>
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7fb975872..d95d2042a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -197,6 +197,7 @@
Feedback
Logout
Tutorial
+ Notifications
Nearby places cannot be displayed without location permissions
no description found
Commons file page
@@ -216,4 +217,9 @@
Permission required to display a list of nearby places
GET DIRECTIONS
READ ARTICLE
+
+ Welcome to Wikimedia Commons, %s! We\'re glad you\'re here.
+ %s left a message on your talk page
+ Thank you for making an edit
+ %s mentioned you on %s.
diff --git a/app/src/test/java/fr/free/nrw/commons/TestCommonsApplication.java b/app/src/test/java/fr/free/nrw/commons/TestCommonsApplication.java
index e59d75aca..aad8a22b8 100644
--- a/app/src/test/java/fr/free/nrw/commons/TestCommonsApplication.java
+++ b/app/src/test/java/fr/free/nrw/commons/TestCommonsApplication.java
@@ -89,7 +89,7 @@ public class TestCommonsApplication extends CommonsApplication {
}
@Override
- public MediaWikiApi provideMediaWikiApi() {
+ public MediaWikiApi provideMediaWikiApi(Context context) {
return mediaWikiApi;
}
diff --git a/app/src/test/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.java b/app/src/test/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.java
index 8e72b3252..e6b53d460 100644
--- a/app/src/test/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.java
+++ b/app/src/test/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.java
@@ -6,7 +6,9 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.io.IOException;
@@ -38,7 +40,7 @@ public class ApacheHttpClientMediaWikiApiTest {
@Before
public void setUp() throws Exception {
server = new MockWebServer();
- testObject = new ApacheHttpClientMediaWikiApi("http://" + server.getHostName() + ":" + server.getPort() + "/");
+ testObject = new ApacheHttpClientMediaWikiApi(RuntimeEnvironment.application, "http://" + server.getHostName() + ":" + server.getPort() + "/");
testObject.setWikiMediaToolforgeUrl("http://" + server.getHostName() + ":" + server.getPort() + "/");
}
diff --git a/gradle.properties b/gradle.properties
index 21c58394c..10685ea11 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -14,7 +14,6 @@ android.useDeprecatedNdk=true
BUTTERKNIFE_VERSION=8.6.0
DAGGER_VERSION=2.13
LEAK_CANARY=1.5.4
-
org.gradle.jvmargs=-Xmx1536M
#TODO: Temporary disabled. https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html#aapt2