diff --git a/app/build.gradle b/app/build.gradle index ac181a36c..06996d5a6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -137,6 +137,7 @@ android { buildConfigField "String", "WIKIMEDIA_FORGE_API_HOST", "\"https://tools.wmflabs.org/\"" buildConfigField "String", "IMAGE_URL_BASE", "\"https://upload.wikimedia.org/wikipedia/commons\"" buildConfigField "String", "HOME_URL", "\"https://commons.wikimedia.org/wiki/\"" + buildConfigField "String", "COMMONS_URL", "\"https://commons.wikimedia.org\"" buildConfigField "String", "MOBILE_HOME_URL", "\"https://commons.m.wikimedia.org/wiki/\"" buildConfigField "String", "EVENTLOG_URL", "\"https://www.wikimedia.org/beacon/event\"" buildConfigField "String", "EVENTLOG_WIKI", "\"commonswiki\"" @@ -152,6 +153,7 @@ android { buildConfigField "String", "WIKIMEDIA_FORGE_API_HOST", "\"https://tools.wmflabs.org/\"" buildConfigField "String", "IMAGE_URL_BASE", "\"https://upload.beta.wmflabs.org/wikipedia/commons\"" buildConfigField "String", "HOME_URL", "\"https://commons.wikimedia.beta.wmflabs.org/wiki/\"" + buildConfigField "String", "COMMONS_URL", "\"https://commons.wikimedia.beta.wmflabs.org\"" buildConfigField "String", "MOBILE_HOME_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/wiki/\"" buildConfigField "String", "EVENTLOG_URL", "\"https://commons.wikimedia.beta.wmflabs.org/beacon/event\"" buildConfigField "String", "EVENTLOG_WIKI", "\"commonswiki\"" diff --git a/app/src/main/java/fr/free/nrw/commons/Utils.java b/app/src/main/java/fr/free/nrw/commons/Utils.java index 05873782c..91c23ce26 100644 --- a/app/src/main/java/fr/free/nrw/commons/Utils.java +++ b/app/src/main/java/fr/free/nrw/commons/Utils.java @@ -177,7 +177,7 @@ public class Utils { } } - public static void handleWebUrl(Context context,Uri url){ + public static void handleWebUrl(Context context, Uri url) { Intent browserIntent = new Intent(Intent.ACTION_VIEW, url); if (browserIntent.resolveActivity(context.getPackageManager()) == null) { Toast toast = Toast.makeText(context, context.getString(R.string.no_web_browser), LENGTH_SHORT); 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 32ca96174..78051abd8 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 @@ -23,7 +23,6 @@ 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; @@ -41,16 +40,12 @@ 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 fr.free.nrw.commons.notification.NotificationUtils; 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 */ @@ -434,33 +429,25 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { .param("notprop", "list") .param("format", "xml") .param("meta", "notifications") - .param("notfilter", "!read") +// .param("meta", "notifications") + .param("notformat", "model") .get() .getNode("/api/query/notifications/list"); } catch (IOException e) { Timber.e("Failed to obtain searchCategories", e); } - if (notificationNode == null) { + if (notificationNode == null + || notificationNode.getDocument() == null + || notificationNode.getDocument().getChildNodes() == null + || notificationNode.getDocument().getChildNodes().getLength() == 0) { 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; + return NotificationUtils.getNotificationsFromList(context, childNodes); } - @Override public boolean existingFile(String fileSha1) throws IOException { return api.action("query") 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 index e4efbff6c..e6d759f66 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/Notification.java +++ b/app/src/main/java/fr/free/nrw/commons/notification/Notification.java @@ -10,12 +10,14 @@ public class Notification { public String date; public String description; public String link; + public String iconUrl; - public Notification(NotificationType notificationType, String notificationText, String date, String description, String link) { + public Notification(NotificationType notificationType, String notificationText, String date, String description, String link, String iconUrl) { this.notificationType = notificationType; this.notificationText = notificationText; this.date = date; this.description = description; this.link = link; + this.iconUrl = iconUrl; } } 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 index d6fcd2685..7c01a44b8 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java @@ -9,10 +9,13 @@ import android.os.Bundle; import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; -import android.widget.Toast; +import android.view.View; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; import com.pedrogomez.renderers.RVRendererAdapter; +import java.util.Collections; import java.util.List; import javax.inject.Inject; @@ -20,14 +23,14 @@ import javax.inject.Inject; import butterknife.BindView; import butterknife.ButterKnife; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.theme.NavigationBaseActivity; +import fr.free.nrw.commons.utils.ViewUtil; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; import timber.log.Timber; -import static android.widget.Toast.LENGTH_SHORT; - /** * Created by root on 18.12.2017. */ @@ -36,6 +39,8 @@ 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; @@ -65,14 +70,22 @@ public class NotificationActivity extends NavigationBaseActivity { Timber.d("Add notifications"); if(mNotificationWorkerFragment == null){ - Observable.fromCallable(() -> controller.getNotifications()) + Observable.fromCallable(() -> { + progressBar.setVisibility(View.VISIBLE); + return controller.getNotifications(); + }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(notificationList -> { + Collections.reverse(notificationList); Timber.d("Number of notifications is %d", notificationList.size()); - initializeAndSetNotificationList(notificationList); setAdapter(notificationList); - }, throwable -> Timber.e(throwable, "Error occurred while loading notifications")); + progressBar.setVisibility(View.GONE); + }, throwable -> { + Timber.e(throwable, "Error occurred while loading notifications"); + ViewUtil.showSnackbar(relativeLayout, R.string.error_notifications); + progressBar.setVisibility(View.GONE); + }); } else { setAdapter(mNotificationWorkerFragment.getNotificationList()); } @@ -82,17 +95,14 @@ public class NotificationActivity extends NavigationBaseActivity { if (url == null || url.equals("")) { return; } - Intent browser = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - //check if web browser available - if(browser.resolveActivity(this.getPackageManager()) != null){ - startActivity(browser); - } else { - Toast toast = Toast.makeText(this, getString(R.string.no_web_browser), LENGTH_SHORT); - toast.show(); - } + Utils.handleWebUrl(this, Uri.parse(url)); } private void setAdapter(List notificationList) { + if(notificationList == null || notificationList.isEmpty()) { + ViewUtil.showSnackbar(relativeLayout, R.string.no_notifications); + return; + } notificationAdapterFactory = new NotificationAdapterFactory(notification -> { Timber.d("Notification clicked %s", notification.link); handleUrl(notification.link); 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 index a02b9eff4..73dcaf7b5 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/NotificationRenderer.java +++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationRenderer.java @@ -20,7 +20,6 @@ import fr.free.nrw.commons.R; public class NotificationRenderer extends Renderer { @BindView(R.id.title) ReadMoreTextView title; - @BindView(R.id.description) ReadMoreTextView description; @BindView(R.id.time) TextView time; @BindView(R.id.icon) ImageView icon; private NotificationClicked listener; @@ -48,13 +47,10 @@ public class NotificationRenderer extends Renderer { @Override public void render() { Notification notification = getContent(); - StringBuilder str = new StringBuilder(notification.notificationText); - str.append(" " ); + StringBuilder str = new StringBuilder(notification.notificationText.trim()); + str.append(" "); title.setText(str); time.setText(notification.date); - StringBuilder desc = new StringBuilder(notification.description); - desc.append(" "); - description.setText(desc); switch (notification.notificationType) { case THANK_YOU_EDIT: icon.setImageResource(R.drawable.ic_edit_black_24dp); 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 index 7f32da126..68c3add1c 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/NotificationUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationUtils.java @@ -1,16 +1,24 @@ package fr.free.nrw.commons.notification; +import android.annotation.SuppressLint; import android.content.Context; +import android.support.annotation.NonNull; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import java.util.ArrayList; +import java.util.List; + import javax.annotation.Nullable; import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.R; +import static fr.free.nrw.commons.notification.NotificationType.THANK_YOU_EDIT; +import static fr.free.nrw.commons.notification.NotificationType.UNKNOWN; + public class NotificationUtils { private static final String COMMONS_WIKI = "commonswiki"; @@ -29,27 +37,124 @@ public class NotificationUtils { return NotificationType.handledValueOf(type); } + public static List getNotificationsFromBundle(Context context, Node document) { + Element bundledNotifications = getBundledNotifications(document); + NodeList childNodes = bundledNotifications.getChildNodes(); + + List notifications = new ArrayList<>(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node node = childNodes.item(i); + if (isUsefulNotification(node)) { + notifications.add(getNotificationFromApiResult(context, node)); + } + } + return notifications; + } + + @NonNull + public static List getNotificationsFromList(Context context, NodeList childNodes) { + List notifications = new ArrayList<>(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node node = childNodes.item(i); + if (isUsefulNotification(node)) { + if (isBundledNotification(node)) { + notifications.addAll(getNotificationsFromBundle(context, node)); + } else { + notifications.add(getNotificationFromApiResult(context, node)); + } + } + } + + return notifications; + } + + private static boolean isUsefulNotification(Node node) { + return isCommonsNotification(node) + && !getNotificationType(node).equals(UNKNOWN) + && !getNotificationType(node).equals(THANK_YOU_EDIT); + } + + public static boolean isBundledNotification(Node document) { + Element bundleElement = getBundledNotifications(document); + if (bundleElement == null) { + return false; + } + + return bundleElement.getChildNodes().getLength() > 0; + } + + private static Element getBundledNotifications(Node document) { + return (Element) getNode(document, "bundledNotifications"); + } + public static Notification getNotificationFromApiResult(Context context, Node document) { NotificationType type = getNotificationType(document); String notificationText = ""; - String link = getNotificationLink(document); + String link = getPrimaryLink(document); String description = getNotificationDescription(document); + String iconUrl = getNotificationIconUrl(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); + notificationText = getNotificationText(document); break; case MENTION: notificationText = getMentionMessage(context, document); + description = getMentionDescription(document); break; case WELCOME: notificationText = getWelcomeMessage(context, document); break; } - return new Notification(type, notificationText, getTimestamp(document), description, link); + return new Notification(type, notificationText, getTimestamp(document), description, link, iconUrl); + } + + private static String getNotificationText(Node document) { + String notificationBody = getNotificationBody(document); + if (notificationBody == null || notificationBody.trim().equals("")) { + return getNotificationHeader(document); + } + return notificationBody; + } + + private static String getNotificationHeader(Node document) { + Node body = getNode(getModel(document), "header"); + if (body != null) { + String textContent = body.getTextContent(); + return textContent.replace("", "").replace("", ""); + } else { + return ""; + } + } + + private static String getNotificationBody(Node document) { + Node body = getNode(getModel(document), "body"); + if (body != null) { + String textContent = body.getTextContent(); + return textContent.replace("", "").replace("", ""); + } else { + return ""; + } + } + + private static String getMentionDescription(Node document) { + Node body = getNode(getModel(document), "body"); + return body != null ? body.getTextContent() : ""; + } + + private static String getNotificationIconUrl(Node document) { + String format = "%s%s"; + Node iconUrl = getNode(getModel(document), "iconUrl"); + if(iconUrl == null) { + return null; + } else { + String url = iconUrl.getTextContent(); + return String.format(format, BuildConfig.COMMONS_URL, url); + } } public static String getMentionMessage(Context context, Node document) { @@ -57,16 +162,31 @@ public class NotificationUtils { return String.format(format, getAgent(document), getNotificationDescription(document)); } + @SuppressLint("StringFormatMatches") public static String getUserTalkMessage(Context context, Node document) { String format = context.getString(R.string.notifications_talk_page_message); return String.format(format, getAgent(document)); } + @SuppressLint("StringFormatInvalid") 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 getPrimaryLink(Node document) { + Node links = getNode(getModel(document), "links"); + Element primaryLink = (Element) getNode(links, "primary"); + if (primaryLink != null) { + return primaryLink.getAttribute("url"); + } + return ""; + } + + private static Node getModel(Node document) { + return getNode(document, "_.2A."); + } + private static String getAgent(Node document) { Element agentElement = (Element) getNode(document, "agent"); if (agentElement != null) { @@ -83,16 +203,6 @@ public class NotificationUtils { 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) { diff --git a/app/src/main/res/layout/activity_nearby.xml b/app/src/main/res/layout/activity_nearby.xml index 31eceff07..70f991c23 100644 --- a/app/src/main/res/layout/activity_nearby.xml +++ b/app/src/main/res/layout/activity_nearby.xml @@ -21,9 +21,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> - @@ -37,7 +38,7 @@ android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" /> - + - + @@ -15,6 +16,12 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> + - - - \ 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 ee0bb4726..61c0739fe 100644 --- a/app/src/main/res/menu/drawer.xml +++ b/app/src/main/res/menu/drawer.xml @@ -1,43 +1,50 @@ + + - + + + + - - + - + + + + - + - - - - + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a78a574b8..bae83799b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -253,9 +253,11 @@ Rate Us Frequently Asked Questions Skip Tutorial - + Internet unavailable Internet available + Error fetching notifications + No notifications found Translate Languages