Remove dependency on Glide, Picasso, SVG, and multidex. (#1859)

* Eliminate the use of Picasso.

This gets rid of the single use of the Picasso library (which was causing
the whole library to be imported and shipped) and replaces it with Glide.
TODO: replace this and the other instance(s) of Glide usage with Fresco,
or vice versa.

* Remove dependency on Glide.

This removes the dependency on Glide, as well as the SVG rendering
library, whose only purpose was to display a single SVG image in the
Notification activity. Unfortunately Android doesn't support SVG natively,
but Echo notifications have icons that are SVG formatted. Rather than
import a bunch of heavy libraries to support this single case of SVG
rendering, we can simply create a few local drawables that correspond to
the different types of notifications, and use them instead.

* Remove multidex!

Multidex is a killer of performance and should be avoided at all costs.

* Remove further unused bits.

* Remove final vestige of multidex.
This commit is contained in:
Dmitry Brant 2018-08-30 07:40:17 -04:00 committed by neslihanturan
parent a059a3c2ef
commit 2884bd934a
12 changed files with 50 additions and 211 deletions

View file

@ -39,11 +39,9 @@ We try to have an extensive documentation at [our wiki here at Github][5]:
* [Fresco][27] * [Fresco][27]
* [Stetho][28] * [Stetho][28]
* [Dagger][29] * [Dagger][29]
* [AndroidSVG][30] * [Java-HTTP-Fluent][30]
* [Java-HTTP-Fluent][31] * [CircleProgressBar][31]
* [CircleProgressBar][32] * [Leak Canary][32]
* [Glide][33]
* [Leak Canary][34]
## License ## ## License ##
@ -80,8 +78,6 @@ This software is open source, licensed under the [Apache License 2.0][4].
[27]: https://github.com/facebook/fresco [27]: https://github.com/facebook/fresco
[28]: https://github.com/facebook/stetho [28]: https://github.com/facebook/stetho
[29]: https://github.com/google/dagger [29]: https://github.com/google/dagger
[30]: https://github.com/BigBadaboom/androidsvg [30]: https://github.com/yuvipanda/java-http-fluent/blob/master/src/main/java/in/yuvi/http/fluent/Http.java
[31]: https://github.com/yuvipanda/java-http-fluent/blob/master/src/main/java/in/yuvi/http/fluent/Http.java [31]: https://github.com/dinuscxj/CircleProgressBar
[32]: https://github.com/dinuscxj/CircleProgressBar [32]: https://github.com/square/leakcanary
[33]: https://github.com/bumptech/glide
[34]: https://github.com/square/leakcanary

View file

@ -7,7 +7,6 @@ apply from: 'quality.gradle'
apply plugin: 'com.getkeepsafe.dexcount' apply plugin: 'com.getkeepsafe.dexcount'
dependencies { dependencies {
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'com.prof.rssparser:rssparser:1.1' implementation 'com.prof.rssparser:rssparser:1.1'
implementation 'com.github.nicolas-raoul:Quadtree:ac16ea8035bf07' implementation 'com.github.nicolas-raoul:Quadtree:ac16ea8035bf07'
implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar' implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar'
@ -40,7 +39,6 @@ dependencies {
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1' implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
// Because RxAndroid releases are few and far between, it is recommended you also // Because RxAndroid releases are few and far between, it is recommended you also
// explicitly depend on RxJava's latest version for bug fixes and new features. // explicitly depend on RxJava's latest version for bug fixes and new features.
implementation 'com.android.support:multidex:1.0.3'
implementation 'io.reactivex.rxjava2:rxjava:2.1.2' implementation 'io.reactivex.rxjava2:rxjava:2.1.2'
implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0' implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
implementation 'com.jakewharton.rxbinding2:rxbinding-support-v4:2.0.0' implementation 'com.jakewharton.rxbinding2:rxbinding-support-v4:2.0.0'
@ -53,7 +51,6 @@ dependencies {
implementation "com.google.dagger:dagger-android-support:$DAGGER_VERSION" implementation "com.google.dagger:dagger-android-support:$DAGGER_VERSION"
kapt "com.google.dagger:dagger-android-processor:$DAGGER_VERSION" kapt "com.google.dagger:dagger-android-processor:$DAGGER_VERSION"
kapt "com.google.dagger:dagger-compiler:$DAGGER_VERSION" kapt "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
testImplementation 'org.robolectric:multidex:3.4.2'
testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
@ -62,10 +59,6 @@ dependencies {
testImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1' testImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1'
implementation 'com.dinuscxj:circleprogressbar:1.1.1' implementation 'com.dinuscxj:circleprogressbar:1.1.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 "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1' androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1'
androidTestImplementation "com.android.support:support-annotations:$SUPPORT_LIB_VERSION" androidTestImplementation "com.android.support:support-annotations:$SUPPORT_LIB_VERSION"
@ -95,8 +88,6 @@ android {
targetSdkVersion project.targetSdkVersion targetSdkVersion project.targetSdkVersion
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
multiDexEnabled true
} }
testOptions { testOptions {
@ -117,7 +108,7 @@ android {
buildTypes { buildTypes {
release { 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. 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', 'proguard-glide.txt' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
} }
debug { debug {
testCoverageEnabled true testCoverageEnabled true

View file

@ -1,9 +0,0 @@
-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

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons; package fr.free.nrw.commons;
import android.app.Application;
import android.app.NotificationChannel; import android.app.NotificationChannel;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
@ -7,7 +8,6 @@ import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.os.Build; import android.os.Build;
import android.support.annotation.RequiresApi; import android.support.annotation.RequiresApi;
import android.support.multidex.MultiDexApplication;
import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.core.ImagePipelineConfig; import com.facebook.imagepipeline.core.ImagePipelineConfig;
@ -45,7 +45,7 @@ import timber.log.Timber;
resDialogCommentPrompt = R.string.crash_dialog_comment_prompt, resDialogCommentPrompt = R.string.crash_dialog_comment_prompt,
resDialogOkToast = R.string.crash_dialog_ok_toast resDialogOkToast = R.string.crash_dialog_ok_toast
) )
public class CommonsApplication extends MultiDexApplication { public class CommonsApplication extends Application {
@Inject SessionManager sessionManager; @Inject SessionManager sessionManager;
@Inject DBOpenHelper dbOpenHelper; @Inject DBOpenHelper dbOpenHelper;

View file

@ -1,36 +0,0 @@
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);
}
}
}

View file

@ -1,28 +0,0 @@
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);
}
}

View file

@ -1,51 +0,0 @@
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;
}
}

View file

@ -1,6 +1,5 @@
package fr.free.nrw.commons.notification; package fr.free.nrw.commons.notification;
import android.graphics.drawable.PictureDrawable;
import android.text.Html; import android.text.Html;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -9,23 +8,17 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import com.borjabravo.readmoretextview.ReadMoreTextView; import com.borjabravo.readmoretextview.ReadMoreTextView;
import com.bumptech.glide.RequestBuilder;
import com.pedrogomez.renderers.Renderer; import com.pedrogomez.renderers.Renderer;
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.glide.SvgSoftwareLayerSetter;
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
/** /**
* 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> {
private RequestBuilder<PictureDrawable> requestBuilder;
@BindView(R.id.title) ReadMoreTextView title; @BindView(R.id.title) ReadMoreTextView title;
@BindView(R.id.time) TextView time; @BindView(R.id.time) TextView time;
@BindView(R.id.icon) ImageView icon; @BindView(R.id.icon) ImageView icon;
@ -48,11 +41,6 @@ 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);
requestBuilder = GlideApp.with(inflatedView.getContext())
.as(PictureDrawable.class)
.error(R.drawable.round_icon_unknown)
.transition(withCrossFade())
.listener(new SvgSoftwareLayerSetter());
return inflatedView; return inflatedView;
} }
@ -61,7 +49,6 @@ public class NotificationRenderer extends Renderer<Notification> {
Notification notification = getContent(); Notification notification = getContent();
setTitle(notification.notificationText); setTitle(notification.notificationText);
time.setText(notification.date); time.setText(notification.date);
requestBuilder.load(notification.iconUrl).into(icon);
} }
/** /**

View file

@ -1,35 +0,0 @@
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;
}
}

View file

@ -3,18 +3,27 @@ package fr.free.nrw.commons.widget;
import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider; import android.appwidget.AppWidgetProvider;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.widget.RemoteViews; import android.widget.RemoteViews;
import com.facebook.common.executors.CallerThreadExecutor;
import com.facebook.common.references.CloseableReference;
import com.facebook.datasource.DataSource;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.core.ImagePipeline;
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.prof.rssparser.Article; import com.prof.rssparser.Article;
import com.prof.rssparser.Parser; import com.prof.rssparser.Parser;
import com.squareup.picasso.Picasso;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import java.util.ArrayList; import java.util.ArrayList;
@ -27,10 +36,7 @@ import fr.free.nrw.commons.R;
*/ */
public class PicOfDayAppWidget extends AppWidgetProvider { public class PicOfDayAppWidget extends AppWidgetProvider {
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
int appWidgetId) {
// Construct the RemoteViews object
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.pic_of_day_app_widget); RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.pic_of_day_app_widget);
String urlString = BuildConfig.WIKIMEDIA_API_POTD; String urlString = BuildConfig.WIKIMEDIA_API_POTD;
@ -45,19 +51,37 @@ public class PicOfDayAppWidget extends AppWidgetProvider {
Elements elements = document.select("img"); Elements elements = document.select("img");
String imageUrl = elements.get(0).attr("src"); String imageUrl = elements.get(0).attr("src");
if (imageUrl != null && imageUrl.length() > 0) { if (imageUrl != null && imageUrl.length() > 0) {
Picasso.get().load(imageUrl).into(views, R.id.appwidget_image, new int[]{appWidgetId});
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(imageUrl)).build();
ImagePipeline imagePipeline = Fresco.getImagePipeline();
DataSource<CloseableReference<CloseableImage>> dataSource
= imagePipeline.fetchDecodedImage(request, context);
dataSource.subscribe(new BaseBitmapDataSubscriber() {
@Override
protected void onNewResultImpl(@Nullable Bitmap tempBitmap) {
Bitmap bitmap = null;
if (tempBitmap != null) {
bitmap = Bitmap.createBitmap(tempBitmap.getWidth(), tempBitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(tempBitmap, 0f, 0f, new Paint());
} }
views.setImageViewBitmap(R.id.appwidget_image, bitmap);
appWidgetManager.updateAppWidget(appWidgetId, views);
} }
@Override
protected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {
// Ignore failure for now.
}
}, CallerThreadExecutor.getInstance());
}
}
} }
@Override @Override
public void onError() { public void onError() {
} }
}); });
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views);
} }
@Override @Override

View file

@ -6,7 +6,7 @@
android:foreground="?selectableItemBackground" android:foreground="?selectableItemBackground"
android:minHeight="72dp"> android:minHeight="72dp">
<ImageView <android.support.v7.widget.AppCompatImageView
android:id="@+id/icon" android:id="@+id/icon"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
@ -16,8 +16,8 @@
android:background="@android:color/white" android:background="@android:color/white"
android:contentDescription="@string/no_image_found" android:contentDescription="@string/no_image_found"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:src="@drawable/empty_photo" app:srcCompat="@drawable/ic_message_black_24dp"
android:tint="@color/primaryDarkColor" app:tint="@color/primaryDarkColor"
/> />
<TextView <TextView

Binary file not shown.