diff --git a/.travis.yml b/.travis.yml
index 20c5bfaee..5e76e09d9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -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-.+'
diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md
index 9d7150008..37e104d14 100644
--- a/PULL_REQUEST_TEMPLATE.md
+++ b/PULL_REQUEST_TEMPLATE.md
@@ -1,3 +1,7 @@
+## Title (required)
+
+Fixes #{GitHub issue number and title (Please do not forget adding title) }
+
## Description (required)
Fixes #{GitHub issue number and title}
@@ -12,4 +16,4 @@ Tested on {API level & name of device/emulator}, with {build variant, e.g. ProdD
{Only for user interface changes, otherwise remove this section. See [how to take a screenshot](https://android.stackexchange.com/questions/1759/how-to-take-a-screenshot-with-an-android-device)}
-_Note: Please ensure that you have read CONTRIBUTING.md if this is your first pull request._
+_Note: Please ensure that you have read CONTRIBUTING.md if this is your first pull request._
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 72dbd7546..fedd7d703 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -11,7 +11,6 @@ dependencies {
implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar'
implementation 'in.yuvi:http.fluent:1.3'
implementation 'com.github.chrisbanes:PhotoView:2.0.0'
- implementation 'com.android.volley:volley:1.0.0'
implementation 'ch.acra:acra:4.9.2'
implementation 'org.mediawiki:api:1.3'
implementation 'commons-codec:commons-codec:1.10'
@@ -20,7 +19,7 @@ dependencies {
implementation 'com.jakewharton.timber:timber:4.5.1'
implementation 'info.debatty:java-string-similarity:0.24'
implementation 'com.borjabravo:readmoretextview:2.1.0'
- implementation 'com.android.support.constraint:constraint-layout:1.0.2'
+ implementation 'com.android.support.constraint:constraint-layout:1.1.0'
implementation ('com.mapbox.mapboxsdk:mapbox-android-sdk:5.4.1@aar'){
transitive=true
}
@@ -69,10 +68,14 @@ 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"
- androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2-alpha1'
+ androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
debugImplementation "com.squareup.leakcanary:leakcanary-android:$LEAK_CANARY"
releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$LEAK_CANARY"
@@ -117,7 +120,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"
diff --git a/app/proguard-glide.txt b/app/proguard-glide.txt
new file mode 100644
index 000000000..ef3437660
--- /dev/null
+++ b/app/proguard-glide.txt
@@ -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
\ No newline at end of file
diff --git a/app/proguard-rules.txt b/app/proguard-rules.txt
index bbf3a3f0d..39b618718 100644
--- a/app/proguard-rules.txt
+++ b/app/proguard-rules.txt
@@ -1,5 +1,4 @@
-dontobfuscate
-keep class org.apache.http.** { *; }
-dontwarn org.apache.http.**
--keep class fr.free.nrw.commons.upload.MwVolleyApi$Page {*;}
-keep class android.support.v7.widget.ShareActionProvider { *; }
\ No newline at end of file
diff --git a/app/quality.gradle b/app/quality.gradle
index 7ea20916a..1afdf0d68 100644
--- a/app/quality.gradle
+++ b/app/quality.gradle
@@ -18,7 +18,7 @@ task checkstyle(type: Checkstyle) {
reports {
html {
enabled true
- destination "${project.buildDir}/reports/checkstyle/checkstyle.html"
+ destination file("${project.buildDir}/reports/checkstyle/checkstyle.html")
}
}
}
@@ -36,10 +36,10 @@ task pmd(type: Pmd) {
xml.enabled = false
html.enabled = true
xml {
- destination "${project.buildDir}/reports/pmd/pmd.xml"
+ destination file("${project.buildDir}/reports/pmd/pmd.xml")
}
html {
- destination "${project.buildDir}/reports/pmd/pmd.html"
+ destination file("${project.buildDir}/reports/pmd/pmd.html")
}
}
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 17f6770d2..45a647227 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -15,6 +15,7 @@
+
diff --git a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java
index d0fb628e3..b19d70a4e 100644
--- a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java
@@ -271,11 +271,11 @@ public class LoginActivity extends AccountAuthenticatorActivity {
showMessageAndCancelDialog(R.string.login_failed_network);
} else if (result.toLowerCase(Locale.getDefault()).contains("nosuchuser".toLowerCase()) || result.toLowerCase().contains("noname".toLowerCase())) {
// Matches nosuchuser, nosuchusershort, noname
- showMessageAndCancelDialog(R.string.login_failed_username);
+ showMessageAndCancelDialog(R.string.login_failed_wrong_credentials);
emptySensitiveEditFields();
} else if (result.toLowerCase(Locale.getDefault()).contains("wrongpassword".toLowerCase())) {
// Matches wrongpassword, wrongpasswordempty
- showMessageAndCancelDialog(R.string.login_failed_password);
+ showMessageAndCancelDialog(R.string.login_failed_wrong_credentials);
emptySensitiveEditFields();
} else if (result.toLowerCase(Locale.getDefault()).contains("throttle".toLowerCase())) {
// Matches unknown throttle error codes
diff --git a/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java b/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java
index ff6ceece4..72de0db70 100644
--- a/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java
+++ b/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java
@@ -7,18 +7,25 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import fr.free.nrw.commons.upload.MwVolleyApi;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import fr.free.nrw.commons.upload.GpsCategoryModel;
import timber.log.Timber;
+@Singleton
public class CacheController {
+ private final GpsCategoryModel gpsCategoryModel;
+ private final QuadTree> quadTree;
private double x, y;
- private QuadTree> quadTree;
private double xMinus, xPlus, yMinus, yPlus;
private static final int EARTH_RADIUS = 6378137;
- public CacheController() {
+ @Inject
+ CacheController(GpsCategoryModel gpsCategoryModel) {
+ this.gpsCategoryModel = gpsCategoryModel;
quadTree = new QuadTree<>(-180, -90, +180, +90);
}
@@ -31,8 +38,8 @@ public class CacheController {
public void cacheCategory() {
List pointCatList = new ArrayList<>();
- if (MwVolleyApi.GpsCatExists.getGpsCatExists()) {
- pointCatList.addAll(MwVolleyApi.getGpsCat());
+ if (gpsCategoryModel.getGpsCatExists()) {
+ pointCatList.addAll(gpsCategoryModel.getCategoryList());
Timber.d("Categories being cached: %s", pointCatList);
} else {
Timber.d("No categories found, so no categories cached");
@@ -65,7 +72,7 @@ public class CacheController {
}
//Based on algorithm at http://gis.stackexchange.com/questions/2951/algorithm-for-offsetting-a-latitude-longitude-by-some-amount-of-meters
- public void convertCoordRange() {
+ private void convertCoordRange() {
//Position, decimal degrees
double lat = y;
double lon = x;
diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java
index e804189ab..93ddb60d5 100644
--- a/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java
@@ -39,7 +39,7 @@ import butterknife.ButterKnife;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
-import fr.free.nrw.commons.upload.MwVolleyApi;
+import fr.free.nrw.commons.upload.GpsCategoryModel;
import fr.free.nrw.commons.utils.StringSortingUtils;
import fr.free.nrw.commons.utils.ViewUtil;
import io.reactivex.Observable;
@@ -73,6 +73,7 @@ public class CategorizationFragment extends CommonsDaggerSupportFragment {
@Inject @Named("prefs") SharedPreferences prefsPrefs;
@Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs;
@Inject CategoryDao categoryDao;
+ @Inject GpsCategoryModel gpsCategoryModel;
private RVRendererAdapter categoriesAdapter;
private OnCategoriesSaveHandler onCategoriesSaveHandler;
@@ -253,7 +254,6 @@ public class CategorizationFragment extends CommonsDaggerSupportFragment {
}
private Observable defaultCategories() {
-
Observable directCat = directCategories();
if (hasDirectCategories) {
Timber.d("Image has direct Cat");
@@ -287,9 +287,7 @@ public class CategorizationFragment extends CommonsDaggerSupportFragment {
}
private Observable gpsCategories() {
- return Observable.fromIterable(
- MwVolleyApi.GpsCatExists.getGpsCatExists()
- ? MwVolleyApi.getGpsCat() : new ArrayList<>())
+ return Observable.fromIterable(gpsCategoryModel.getCategoryList())
.map(name -> new CategoryItem(name, false));
}
diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java
index 3b6734edd..a44e19a29 100644
--- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java
@@ -224,4 +224,14 @@ public class CategoryImagesListFragment extends DaggerFragment {
public ListAdapter getAdapter() {
return gridView.getAdapter();
}
+
+ /**
+ * This method will be called on back pressed of CategoryImagesActivity.
+ * It initializes the grid view by setting adapter.
+ */
+ @Override
+ public void onResume() {
+ gridView.setAdapter(gridAdapter);
+ super.onResume();
+ }
}
diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java
index 91f6d4ccb..5662bb885 100644
--- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java
+++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java
@@ -9,17 +9,16 @@ import dagger.android.support.AndroidSupportInjectionModule;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.MediaWikiImageView;
import fr.free.nrw.commons.auth.LoginActivity;
-import fr.free.nrw.commons.contributions.Contribution;
-import fr.free.nrw.commons.contributions.ContributionsActivity;
import fr.free.nrw.commons.contributions.ContributionsSyncAdapter;
import fr.free.nrw.commons.delete.DeleteTask;
import fr.free.nrw.commons.modifications.ModificationsSyncAdapter;
-import fr.free.nrw.commons.settings.SettingsFragment;
import fr.free.nrw.commons.nearby.PlaceRenderer;
+import fr.free.nrw.commons.settings.SettingsFragment;
@Singleton
@Component(modules = {
CommonsApplicationModule.class,
+ NetworkingModule.class,
AndroidInjectionModule.class,
AndroidSupportInjectionModule.class,
ActivityBuilderModule.class,
diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java
index 1c54366af..c5b9f0169 100644
--- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java
+++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java
@@ -13,14 +13,10 @@ import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
-import fr.free.nrw.commons.BuildConfig;
-import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.auth.AccountUtil;
import fr.free.nrw.commons.auth.SessionManager;
-import fr.free.nrw.commons.caching.CacheController;
import fr.free.nrw.commons.data.DBOpenHelper;
import fr.free.nrw.commons.location.LocationServiceManager;
-import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.nearby.NearbyPlaces;
import fr.free.nrw.commons.upload.UploadController;
@@ -35,7 +31,6 @@ import static fr.free.nrw.commons.modifications.ModificationsContentProvider.MOD
@SuppressWarnings({"WeakerAccess", "unused"})
public class CommonsApplicationModule {
public static final String CATEGORY_AUTHORITY = "fr.free.nrw.commons.categories.contentprovider";
- public static final long OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024;
private Context applicationContext;
@@ -144,12 +139,6 @@ public class CommonsApplicationModule {
return new Gson();
}
- @Provides
- @Singleton
- public CacheController provideCacheController() {
- return new CacheController();
- }
-
@Provides
@Singleton
public DBOpenHelper provideDBOpenHelper(Context context) {
diff --git a/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java
new file mode 100644
index 000000000..8c0b52316
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java
@@ -0,0 +1,59 @@
+package fr.free.nrw.commons.di;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.support.annotation.NonNull;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+import fr.free.nrw.commons.BuildConfig;
+import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi;
+import fr.free.nrw.commons.mwapi.MediaWikiApi;
+import okhttp3.HttpUrl;
+import okhttp3.OkHttpClient;
+
+@Module
+@SuppressWarnings({"WeakerAccess", "unused"})
+public class NetworkingModule {
+ public static final long OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024;
+
+ @Provides
+ @Singleton
+ public OkHttpClient provideOkHttpClient() {
+ return new OkHttpClient.Builder().build();
+ }
+
+ @Provides
+ @Singleton
+ public MediaWikiApi provideMediaWikiApi(Context context,
+ @Named("default_preferences") SharedPreferences defaultPreferences,
+ @Named("category_prefs") SharedPreferences categoryPrefs,
+ Gson gson) {
+ return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, defaultPreferences, categoryPrefs, gson);
+ }
+
+ @Provides
+ @Named("commons_mediawiki_url")
+ @NonNull
+ @SuppressWarnings("ConstantConditions")
+ public HttpUrl provideMwUrl() {
+ return HttpUrl.parse(BuildConfig.COMMONS_URL);
+ }
+
+ /**
+ * Gson objects are very heavy. The app should ideally be using just one instance of it instead of creating new instances everywhere.
+ * @return returns a singleton Gson instance
+ */
+ @Provides
+ @Singleton
+ public Gson provideGson() {
+ return new GsonBuilder().create();
+ }
+
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/glide/SvgDecoder.java b/app/src/main/java/fr/free/nrw/commons/glide/SvgDecoder.java
new file mode 100644
index 000000000..9087f9501
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/glide/SvgDecoder.java
@@ -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 {
+
+ @Override
+ public boolean handles(@NonNull InputStream source, @NonNull Options options) {
+ // TODO: Can we tell?
+ return true;
+ }
+
+ public Resource