mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
Enable crosswiki notifications and minor UI fixes in displaying notif… (#1540)
* Enable crosswiki notifications and minor UI fixes in displaying notifications * Added java docs
This commit is contained in:
parent
41acb76bd8
commit
2a0b9d8a0b
11 changed files with 247 additions and 23 deletions
|
|
@ -19,12 +19,13 @@ android:
|
|||
components:
|
||||
- tools
|
||||
- platform-tools
|
||||
- build-tools-26.0.2
|
||||
- build-tools-27.0.0
|
||||
- extra-google-m2repository
|
||||
- extra-android-m2repository
|
||||
- ${ANDROID_TARGET}
|
||||
- android-25
|
||||
- android-26
|
||||
- android-27
|
||||
- sys-img-${ANDROID_ABI}-${ANDROID_TARGET}
|
||||
licenses:
|
||||
- 'android-sdk-license-.+'
|
||||
|
|
|
|||
|
|
@ -69,6 +69,10 @@ dependencies {
|
|||
testImplementation 'com.nhaarman:mockito-kotlin:1.5.0'
|
||||
testImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1'
|
||||
|
||||
implementation 'com.caverock:androidsvg:1.2.1'
|
||||
implementation 'com.github.bumptech.glide:glide:4.7.1'
|
||||
kapt 'com.github.bumptech.glide:compiler:4.7.1'
|
||||
|
||||
androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
|
||||
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1'
|
||||
androidTestImplementation "com.android.support:support-annotations:$SUPPORT_LIB_VERSION"
|
||||
|
|
@ -117,7 +121,7 @@ android {
|
|||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false // See https://stackoverflow.com/questions/40232404/google-play-apk-and-android-studio-apk-usb-debug-behaving-differently - proguard.cfg modification alone insufficient.
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-glide.txt'
|
||||
}
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
|
|
|
|||
9
app/proguard-glide.txt
Normal file
9
app/proguard-glide.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
-keep public class * implements com.bumptech.glide.module.GlideModule
|
||||
-keep public class * extends com.bumptech.glide.module.AppGlideModule
|
||||
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
|
||||
**[] $VALUES;
|
||||
public *;
|
||||
}
|
||||
|
||||
# for DexGuard only
|
||||
-keepresourcexmlelements manifest/application/meta-data@value=GlideModule
|
||||
36
app/src/main/java/fr/free/nrw/commons/glide/SvgDecoder.java
Normal file
36
app/src/main/java/fr/free/nrw/commons/glide/SvgDecoder.java
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package fr.free.nrw.commons.glide;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.bumptech.glide.load.Options;
|
||||
import com.bumptech.glide.load.ResourceDecoder;
|
||||
import com.bumptech.glide.load.engine.Resource;
|
||||
import com.bumptech.glide.load.resource.SimpleResource;
|
||||
import com.caverock.androidsvg.SVG;
|
||||
import com.caverock.androidsvg.SVGParseException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Decodes an SVG internal representation from an {@link InputStream}.
|
||||
*/
|
||||
public class SvgDecoder implements ResourceDecoder<InputStream, SVG> {
|
||||
|
||||
@Override
|
||||
public boolean handles(@NonNull InputStream source, @NonNull Options options) {
|
||||
// TODO: Can we tell?
|
||||
return true;
|
||||
}
|
||||
|
||||
public Resource<SVG> decode(@NonNull InputStream source, int width, int height,
|
||||
@NonNull Options options)
|
||||
throws IOException {
|
||||
try {
|
||||
SVG svg = SVG.getFromInputStream(source);
|
||||
return new SimpleResource<>(svg);
|
||||
} catch (SVGParseException ex) {
|
||||
throw new IOException("Cannot load SVG from stream", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package fr.free.nrw.commons.glide;
|
||||
|
||||
import android.graphics.Picture;
|
||||
import android.graphics.drawable.PictureDrawable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.bumptech.glide.load.Options;
|
||||
import com.bumptech.glide.load.engine.Resource;
|
||||
import com.bumptech.glide.load.resource.SimpleResource;
|
||||
import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
|
||||
import com.caverock.androidsvg.SVG;
|
||||
|
||||
/**
|
||||
* Convert the {@link SVG}'s internal representation to an Android-compatible one
|
||||
* ({@link Picture}).
|
||||
*/
|
||||
public class SvgDrawableTranscoder implements ResourceTranscoder<SVG, PictureDrawable> {
|
||||
@Nullable
|
||||
@Override
|
||||
public Resource<PictureDrawable> transcode(@NonNull Resource<SVG> toTranscode,
|
||||
@NonNull Options options) {
|
||||
SVG svg = toTranscode.get();
|
||||
Picture picture = svg.renderToPicture();
|
||||
PictureDrawable drawable = new PictureDrawable(picture);
|
||||
return new SimpleResource<>(drawable);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package fr.free.nrw.commons.glide;
|
||||
|
||||
import android.graphics.drawable.PictureDrawable;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.bumptech.glide.load.DataSource;
|
||||
import com.bumptech.glide.load.engine.GlideException;
|
||||
import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.target.ImageViewTarget;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
|
||||
/**
|
||||
* Listener which updates the {@link ImageView} to be software rendered, because
|
||||
* {@link com.caverock.androidsvg.SVG SVG}/{@link android.graphics.Picture Picture} can't render on
|
||||
* a hardware backed {@link android.graphics.Canvas Canvas}.
|
||||
*/
|
||||
public class SvgSoftwareLayerSetter implements RequestListener<PictureDrawable> {
|
||||
|
||||
/**
|
||||
* Sets the layer type to none if the load fails
|
||||
* @param e
|
||||
* @param model
|
||||
* @param target
|
||||
* @param isFirstResource
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean onLoadFailed(GlideException e, Object model, Target<PictureDrawable> target,
|
||||
boolean isFirstResource) {
|
||||
ImageView view = ((ImageViewTarget<?>) target).getView();
|
||||
view.setLayerType(ImageView.LAYER_TYPE_NONE, null);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the layer type to software when the resource is ready
|
||||
* @param resource
|
||||
* @param model
|
||||
* @param target
|
||||
* @param dataSource
|
||||
* @param isFirstResource
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean onResourceReady(PictureDrawable resource, Object model,
|
||||
Target<PictureDrawable> target, DataSource dataSource, boolean isFirstResource) {
|
||||
ImageView view = ((ImageViewTarget<?>) target).getView();
|
||||
view.setLayerType(ImageView.LAYER_TYPE_SOFTWARE, null);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -444,8 +444,8 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
.param("notprop", "list")
|
||||
.param("format", "xml")
|
||||
.param("meta", "notifications")
|
||||
// .param("meta", "notifications")
|
||||
.param("notformat", "model")
|
||||
.param("notwikis", "wikidatawiki|commonswiki|enwiki")
|
||||
.get()
|
||||
.getNode("/api/query/notifications/list");
|
||||
} catch (IOException e) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package fr.free.nrw.commons.notification;
|
||||
|
||||
import android.util.Log;
|
||||
import android.graphics.drawable.PictureDrawable;
|
||||
import android.text.Html;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
|
@ -8,17 +9,23 @@ import android.widget.ImageView;
|
|||
import android.widget.TextView;
|
||||
|
||||
import com.borjabravo.readmoretextview.ReadMoreTextView;
|
||||
import com.bumptech.glide.RequestBuilder;
|
||||
import com.pedrogomez.renderers.Renderer;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.glide.SvgSoftwareLayerSetter;
|
||||
|
||||
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
||||
|
||||
/**
|
||||
* Created by root on 19.12.2017.
|
||||
*/
|
||||
|
||||
public class NotificationRenderer extends Renderer<Notification> {
|
||||
private RequestBuilder<PictureDrawable> requestBuilder;
|
||||
|
||||
@BindView(R.id.title) ReadMoreTextView title;
|
||||
@BindView(R.id.time) TextView time;
|
||||
@BindView(R.id.icon) ImageView icon;
|
||||
|
|
@ -41,23 +48,32 @@ public class NotificationRenderer extends Renderer<Notification> {
|
|||
protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) {
|
||||
View inflatedView = layoutInflater.inflate(R.layout.item_notification, viewGroup, false);
|
||||
ButterKnife.bind(this, inflatedView);
|
||||
requestBuilder = GlideApp.with(inflatedView.getContext())
|
||||
.as(PictureDrawable.class)
|
||||
.error(R.drawable.round_icon_unknown)
|
||||
.transition(withCrossFade())
|
||||
.listener(new SvgSoftwareLayerSetter());
|
||||
return inflatedView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
Notification notification = getContent();
|
||||
String str = notification.notificationText.trim();
|
||||
str = str.concat(" ");
|
||||
title.setText(str);
|
||||
setTitle(notification.notificationText);
|
||||
time.setText(notification.date);
|
||||
switch (notification.notificationType) {
|
||||
case THANK_YOU_EDIT:
|
||||
icon.setImageResource(R.drawable.ic_edit_black_24dp);
|
||||
break;
|
||||
default:
|
||||
icon.setImageResource(R.drawable.round_icon_unknown);
|
||||
requestBuilder.load(notification.iconUrl).into(icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the notification text and sets it as the title
|
||||
* Clean up is required to fix escaped HTML string and extra white spaces at the beginning of the notification
|
||||
* @param notificationText
|
||||
*/
|
||||
private void setTitle(String notificationText) {
|
||||
notificationText = notificationText.trim().replaceAll("(^\\h*)|(\\h*$)", "");
|
||||
notificationText = Html.fromHtml(notificationText).toString();
|
||||
notificationText = notificationText.concat(" ");
|
||||
title.setText(notificationText);
|
||||
}
|
||||
|
||||
public interface NotificationClicked{
|
||||
|
|
|
|||
|
|
@ -16,12 +16,13 @@ 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";
|
||||
private static final String WIKIDATA_WIKI = "wikidatawiki";
|
||||
private static final String WIKIPEDIA_WIKI = "enwiki";
|
||||
|
||||
public static boolean isCommonsNotification(Node document) {
|
||||
if (document == null || !document.hasAttributes()) {
|
||||
|
|
@ -31,6 +32,32 @@ public class NotificationUtils {
|
|||
return COMMONS_WIKI.equals(element.getAttribute("wiki"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the wiki attribute corresponds to wikidatawiki
|
||||
* @param document
|
||||
* @return
|
||||
*/
|
||||
public static boolean isWikidataNotification(Node document) {
|
||||
if (document == null || !document.hasAttributes()) {
|
||||
return false;
|
||||
}
|
||||
Element element = (Element) document;
|
||||
return WIKIDATA_WIKI.equals(element.getAttribute("wiki"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the wiki attribute corresponds to enwiki
|
||||
* @param document
|
||||
* @return
|
||||
*/
|
||||
public static boolean isWikipediaNotification(Node document) {
|
||||
if (document == null || !document.hasAttributes()) {
|
||||
return false;
|
||||
}
|
||||
Element element = (Element) document;
|
||||
return WIKIPEDIA_WIKI.equals(element.getAttribute("wiki"));
|
||||
}
|
||||
|
||||
public static NotificationType getNotificationType(Node document) {
|
||||
Element element = (Element) document;
|
||||
String type = element.getAttribute("type");
|
||||
|
|
@ -68,10 +95,17 @@ public class NotificationUtils {
|
|||
return notifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* Currently the app is interested in showing notifications just from the following three wikis: commons, wikidata, wikipedia
|
||||
* This function returns true only if the notification belongs to any of the above wikis and is of a known notification type
|
||||
* @param node
|
||||
* @return
|
||||
*/
|
||||
private static boolean isUsefulNotification(Node node) {
|
||||
return isCommonsNotification(node)
|
||||
&& !getNotificationType(node).equals(UNKNOWN)
|
||||
&& !getNotificationType(node).equals(THANK_YOU_EDIT);
|
||||
return (isCommonsNotification(node)
|
||||
|| isWikidataNotification(node)
|
||||
|| isWikipediaNotification(node))
|
||||
&& !getNotificationType(node).equals(UNKNOWN);
|
||||
}
|
||||
|
||||
public static boolean isBundledNotification(Node document) {
|
||||
|
|
@ -97,7 +131,7 @@ public class NotificationUtils {
|
|||
|
||||
switch (type) {
|
||||
case THANK_YOU_EDIT:
|
||||
notificationText = context.getString(R.string.notifications_thank_you_edit);
|
||||
notificationText = getThankYouEditDescription(document);
|
||||
break;
|
||||
case EDIT_USER_TALK:
|
||||
notificationText = getNotificationText(document);
|
||||
|
|
@ -146,6 +180,16 @@ public class NotificationUtils {
|
|||
return body != null ? body.getTextContent() : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the header node returned in the XML document to form the description for thank you edits
|
||||
* @param document
|
||||
* @return
|
||||
*/
|
||||
private static String getThankYouEditDescription(Node document) {
|
||||
Node body = getNode(getModel(document), "header");
|
||||
return body != null ? body.getTextContent() : "";
|
||||
}
|
||||
|
||||
private static String getNotificationIconUrl(Node document) {
|
||||
String format = "%s%s";
|
||||
Node iconUrl = getNode(getModel(document), "iconUrl");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
package fr.free.nrw.commons.notification;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.PictureDrawable;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.Registry;
|
||||
import com.bumptech.glide.annotation.GlideModule;
|
||||
import com.bumptech.glide.module.AppGlideModule;
|
||||
import com.caverock.androidsvg.SVG;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import fr.free.nrw.commons.glide.SvgDecoder;
|
||||
import fr.free.nrw.commons.glide.SvgDrawableTranscoder;
|
||||
|
||||
/**
|
||||
* Module for the SVG sample app.
|
||||
*/
|
||||
@GlideModule
|
||||
public class SvgModule extends AppGlideModule {
|
||||
@Override
|
||||
public void registerComponents(@NonNull Context context, @NonNull Glide glide,
|
||||
@NonNull Registry registry) {
|
||||
registry.register(SVG.class, PictureDrawable.class, new SvgDrawableTranscoder())
|
||||
.append(InputStream.class, SVG.class, new SvgDecoder());
|
||||
}
|
||||
|
||||
// Disable manifest parsing to avoid adding similar modules twice.
|
||||
@Override
|
||||
public boolean isManifestParsingEnabled() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -14,17 +14,17 @@
|
|||
# org.gradle.parallel=true
|
||||
#Thu Mar 01 15:28:48 IST 2018
|
||||
systemProp.http.proxyPort=0
|
||||
compileSdkVersion=android-26
|
||||
compileSdkVersion=android-27
|
||||
android.useDeprecatedNdk=true
|
||||
BUTTERKNIFE_VERSION=8.6.0
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
buildToolsVersion=26.0.2
|
||||
targetSdkVersion=25
|
||||
buildToolsVersion=27.0.0
|
||||
targetSdkVersion=27
|
||||
|
||||
#TODO: Temporary disabled. https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html#aapt2
|
||||
#Refer to PR: https://github.com/commons-app/apps-android-commons/pull/932
|
||||
android.enableAapt2=false
|
||||
SUPPORT_LIB_VERSION=26.0.2
|
||||
SUPPORT_LIB_VERSION=27.1.1
|
||||
minSdkVersion=15
|
||||
systemProp.http.proxyHost=
|
||||
LEAK_CANARY=1.5.4
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue