diff --git a/.gitignore b/.gitignore
index 962c96a8bd..029adc2a3b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,3 +38,4 @@ app/src/main/jniLibs
#Below removes all the HTML files related to OpenCV documentation. The documentation can be otherwise found at:
#https://docs.opencv.org/3.3.0/
/libraries/opencv/javadoc/
+captures/*
diff --git a/app/build.gradle b/app/build.gradle
index c2e618c177..b7821bc49f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -102,7 +102,14 @@ dependencies {
//swipe_layout
implementation 'com.daimajia.swipelayout:library:1.2.0@aar'
+
+ //Room
+ def room_version= '2.2.3'
+ implementation "androidx.room:room-runtime:$room_version"
+ kapt "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor
implementation 'com.squareup.retrofit2:retrofit:2.7.1'
+ implementation "androidx.room:room-rxjava2:$room_version"
+ testImplementation "androidx.arch.core:core-testing:2.1.0"
}
android {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 458db48be3..cf43ad1db1 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -78,7 +78,7 @@
android:name=".contributions.MainActivity"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
- android:configChanges="screenSize|keyboard" />
+ android:configChanges="screenSize|keyboard|orientation" />
@@ -150,18 +150,6 @@
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
-
-
-
-
-
-
-
-
categories; // as loaded at runtime?
- protected boolean requestedDeletion;
- private Map descriptions; // multilingual descriptions as loaded
- private HashMap tags = new HashMap<>();
- private @Nullable LatLng coordinates;
+ public Uri localUri;
+ public String thumbUrl;
+ public String imageUrl;
+ public String filename;
+ public String description; // monolingual description on input...
+ public String discussion;
+ long dataLength;
+ public Date dateCreated;
+ @Nullable public Date dateUploaded;
+ public int width;
+ public int height;
+ public String license;
+ public String licenseUrl;
+ public String creator;
+ public ArrayList categories; // as loaded at runtime?
+ public boolean requestedDeletion;
+ public HashMap descriptions; // multilingual descriptions as loaded
+ public HashMap tags = new HashMap<>();
+ @Nullable public LatLng coordinates;
/**
* Provides local constructor
@@ -118,7 +121,7 @@ public Media(Parcel in) {
dateCreated = (Date) in.readSerializable();
dateUploaded = (Date) in.readSerializable();
creator = in.readString();
- tags = (HashMap) in.readSerializable();
+ tags = (HashMap) in.readSerializable();
width = in.readInt();
height = in.readInt();
license = in.readString();
@@ -218,7 +221,7 @@ public Object getTag(String key) {
* @param key Media key
* @param value Media value
*/
- public void setTag(String key, Object value) {
+ public void setTag(String key, String value) {
tags.put(key, value);
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java
index 201f8f390f..6a97c938d1 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java
@@ -6,6 +6,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.StringDef;
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
import org.apache.commons.lang3.StringUtils;
@@ -21,6 +23,7 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
+@Entity(tableName = "contribution")
public class Contribution extends Media {
//{{According to Exif data|2009-01-09}}
@@ -54,17 +57,19 @@ public Contribution[] newArray(int i) {
public static final String SOURCE_CAMERA = "camera";
public static final String SOURCE_GALLERY = "gallery";
public static final String SOURCE_EXTERNAL = "external";
-
- private Uri contentUri;
- private String source;
- private String editSummary;
- private int state;
- private long transferred;
- private String decimalCoords;
- private boolean isMultiple;
- private String wikiDataEntityId;
- private Uri contentProviderUri;
- private String dateCreatedSource;
+ @PrimaryKey (autoGenerate = true)
+ @NonNull
+ public long _id;
+ public Uri contentUri;
+ public String source;
+ public String editSummary;
+ public int state;
+ public long transferred;
+ public String decimalCoords;
+ public boolean isMultiple;
+ public String wikiDataEntityId;
+ public Uri contentProviderUri;
+ public String dateCreatedSource;
public Contribution(Uri contentUri, String filename, Uri localUri, String imageUrl, Date dateCreated,
int state, long dataLength, Date dateUploaded, long transferred,
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java
index e1c134111e..b4d0a8659c 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java
@@ -1,331 +1,55 @@
package fr.free.nrw.commons.contributions;
-import android.content.ContentProviderClient;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.text.TextUtils;
+import androidx.lifecycle.LiveData;
+import androidx.room.Dao;
+import androidx.room.Delete;
+import androidx.room.Insert;
+import androidx.room.OnConflictStrategy;
+import androidx.room.Query;
+import androidx.room.Transaction;
-import androidx.annotation.Nullable;
+import java.util.List;
-import org.apache.commons.lang3.StringUtils;
+import io.reactivex.Completable;
+import io.reactivex.Single;
-import java.util.Date;
+@Dao
+public abstract class ContributionDao {
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Provider;
+ @Query("SELECT * FROM contribution order by dateUploaded DESC")
+ abstract LiveData> fetchContributions();
-import fr.free.nrw.commons.settings.Prefs;
-import timber.log.Timber;
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ public abstract Single save(Contribution contribution);
-import static fr.free.nrw.commons.contributions.ContributionDao.Table.ALL_FIELDS;
-import static fr.free.nrw.commons.contributions.ContributionDao.Table.COLUMN_WIKI_DATA_ENTITY_ID;
-import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
-import static fr.free.nrw.commons.contributions.ContributionsContentProvider.uriForId;
-
-public class ContributionDao {
- /*
- This sorts in the following order:
- Currently Uploading
- Failed (Sorted in ascending order of time added - FIFO)
- Queued to Upload (Sorted in ascending order of time added - FIFO)
- Completed (Sorted in descending order of time added)
-
- This is why Contribution.STATE_COMPLETED is -1.
- */
- static final String CONTRIBUTION_SORT = Table.COLUMN_STATE + " DESC, "
- + Table.COLUMN_UPLOADED + " DESC , ("
- + Table.COLUMN_TIMESTAMP + " * "
- + Table.COLUMN_STATE + ")";
-
- private final Provider clientProvider;
-
- @Inject
- public ContributionDao(@Named("contribution") Provider clientProvider) {
- this.clientProvider = clientProvider;
- }
-
- Cursor loadAllContributions() {
- ContentProviderClient db = clientProvider.get();
- try {
- return db.query(BASE_URI, ALL_FIELDS, "", null, CONTRIBUTION_SORT);
- } catch (RemoteException e) {
- return null;
- } finally {
- db.release();
- }
- }
-
- public void save(Contribution contribution) {
- ContentProviderClient db = clientProvider.get();
- try {
- if (contribution.getContentUri() == null) {
- contribution.setContentUri(db.insert(BASE_URI, toContentValues(contribution)));
- } else {
- db.update(contribution.getContentUri(), toContentValues(contribution), null, null);
- }
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- } finally {
- db.release();
- }
- }
-
- public void delete(Contribution contribution) {
- ContentProviderClient db = clientProvider.get();
- try {
- if (contribution.getContentUri() == null) {
- // noooo
- throw new RuntimeException("tried to delete item with no content URI");
- } else {
- db.delete(contribution.getContentUri(), null, null);
- }
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- } finally {
- db.release();
- }
- }
-
- ContentValues toContentValues(Contribution contribution) {
- ContentValues cv = new ContentValues();
- cv.put(Table.COLUMN_FILENAME, contribution.getFilename());
- if (contribution.getLocalUri() != null) {
- cv.put(Table.COLUMN_LOCAL_URI, contribution.getLocalUri().toString());
- }
- if (contribution.getImageUrl() != null) {
- cv.put(Table.COLUMN_IMAGE_URL, contribution.getImageUrl());
- }
- if (contribution.getDateUploaded() != null) {
- cv.put(Table.COLUMN_UPLOADED, contribution.getDateUploaded().getTime());
- }
- cv.put(Table.COLUMN_LENGTH, contribution.getDataLength());
- //This was always meant to store the date created..If somehow date created is not fetched while actually saving the contribution, lets saveValue today's date
- cv.put(Table.COLUMN_TIMESTAMP, contribution.getDateCreated()==null?System.currentTimeMillis():contribution.getDateCreated().getTime());
- cv.put(Table.COLUMN_STATE, contribution.getState());
- cv.put(Table.COLUMN_TRANSFERRED, contribution.getTransferred());
- cv.put(Table.COLUMN_SOURCE, contribution.getSource());
- cv.put(Table.COLUMN_DESCRIPTION, contribution.getDescription());
- cv.put(Table.COLUMN_CREATOR, contribution.getCreator());
- cv.put(Table.COLUMN_MULTIPLE, contribution.getMultiple() ? 1 : 0);
- cv.put(Table.COLUMN_WIDTH, contribution.getWidth());
- cv.put(Table.COLUMN_HEIGHT, contribution.getHeight());
- cv.put(Table.COLUMN_LICENSE, contribution.getLicense());
- cv.put(Table.COLUMN_WIKI_DATA_ENTITY_ID, contribution.getWikiDataEntityId());
- return cv;
- }
-
- public Contribution fromCursor(Cursor cursor) {
- // Hardcoding column positions!
- //Check that cursor has a value to avoid CursorIndexOutOfBoundsException
- if (cursor.getCount() > 0) {
- int index;
- if (cursor.getColumnIndex(Table.COLUMN_LICENSE) == -1){
- index = 15;
- } else {
- index = cursor.getColumnIndex(Table.COLUMN_LICENSE);
- }
- Contribution contribution = new Contribution(
- uriForId(cursor.getInt(cursor.getColumnIndex(Table.COLUMN_ID))),
- cursor.getString(cursor.getColumnIndex(Table.COLUMN_FILENAME)),
- parseUri(cursor.getString(cursor.getColumnIndex(Table.COLUMN_LOCAL_URI))),
- cursor.getString(cursor.getColumnIndex(Table.COLUMN_IMAGE_URL)),
- parseTimestamp(cursor.getLong(cursor.getColumnIndex(Table.COLUMN_TIMESTAMP))),
- cursor.getInt(cursor.getColumnIndex(Table.COLUMN_STATE)),
- cursor.getLong(cursor.getColumnIndex(Table.COLUMN_LENGTH)),
- parseTimestamp(cursor.getLong(cursor.getColumnIndex(Table.COLUMN_UPLOADED))),
- cursor.getLong(cursor.getColumnIndex(Table.COLUMN_TRANSFERRED)),
- cursor.getString(cursor.getColumnIndex(Table.COLUMN_SOURCE)),
- cursor.getString(cursor.getColumnIndex(Table.COLUMN_DESCRIPTION)),
- cursor.getString(cursor.getColumnIndex(Table.COLUMN_CREATOR)),
- cursor.getInt(cursor.getColumnIndex(Table.COLUMN_MULTIPLE)) == 1,
- cursor.getInt(cursor.getColumnIndex(Table.COLUMN_WIDTH)),
- cursor.getInt(cursor.getColumnIndex(Table.COLUMN_HEIGHT)),
- cursor.getString(index)
- );
-
- String wikidataEntityId = cursor.getString(cursor.getColumnIndex(COLUMN_WIKI_DATA_ENTITY_ID));
- if (!StringUtils.isBlank(wikidataEntityId)) {
- contribution.setWikiDataEntityId(wikidataEntityId);
- }
-
- return contribution;
- }
-
- return null;
- }
-
- @Nullable
- private static Date parseTimestamp(long timestamp) {
- return timestamp == 0 ? null : new Date(timestamp);
+ public Completable deleteAllAndSave(List contributions){
+ return Completable.fromAction(() -> deleteAllAndSaveTransaction(contributions));
}
- @Nullable
- private static Uri parseUri(String uriString) {
- return TextUtils.isEmpty(uriString) ? null : Uri.parse(uriString);
+ @Transaction
+ public void deleteAllAndSaveTransaction(List contributions){
+ deleteAll(Contribution.STATE_COMPLETED);
+ save(contributions);
}
- public static class Table {
- public static final String TABLE_NAME = "contributions";
-
- public static final String COLUMN_ID = "_id";
- public static final String COLUMN_FILENAME = "filename";
- public static final String COLUMN_LOCAL_URI = "local_uri";
- public static final String COLUMN_IMAGE_URL = "image_url";
- public static final String COLUMN_TIMESTAMP = "timestamp";
- public static final String COLUMN_STATE = "state";
- public static final String COLUMN_LENGTH = "length";
- public static final String COLUMN_UPLOADED = "uploaded";
- public static final String COLUMN_TRANSFERRED = "transferred"; // Currently transferred number of bytes
- public static final String COLUMN_SOURCE = "source";
- public static final String COLUMN_DESCRIPTION = "description";
- public static final String COLUMN_CREATOR = "creator"; // Initial uploader
- public static final String COLUMN_MULTIPLE = "multiple";
- public static final String COLUMN_WIDTH = "width";
- public static final String COLUMN_HEIGHT = "height";
- public static final String COLUMN_LICENSE = "license";
- public static final String COLUMN_WIKI_DATA_ENTITY_ID = "wikidataEntityID";
-
- // NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
- public static final String[] ALL_FIELDS = {
- COLUMN_ID,
- COLUMN_FILENAME,
- COLUMN_LOCAL_URI,
- COLUMN_IMAGE_URL,
- COLUMN_TIMESTAMP,
- COLUMN_STATE,
- COLUMN_LENGTH,
- COLUMN_UPLOADED,
- COLUMN_TRANSFERRED,
- COLUMN_SOURCE,
- COLUMN_DESCRIPTION,
- COLUMN_CREATOR,
- COLUMN_MULTIPLE,
- COLUMN_WIDTH,
- COLUMN_HEIGHT,
- COLUMN_LICENSE,
- COLUMN_WIKI_DATA_ENTITY_ID
- };
-
- public static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME;
-
- public static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
- + "_id INTEGER PRIMARY KEY,"
- + "filename STRING,"
- + "local_uri STRING,"
- + "image_url STRING,"
- + "uploaded INTEGER,"
- + "timestamp INTEGER,"
- + "state INTEGER,"
- + "length INTEGER,"
- + "transferred INTEGER,"
- + "source STRING,"
- + "description STRING,"
- + "creator STRING,"
- + "multiple INTEGER,"
- + "width INTEGER,"
- + "height INTEGER,"
- + "LICENSE STRING,"
- + "wikidataEntityID STRING"
- + ");";
+ @Insert
+ public abstract void save(List contribution);
- // Upgrade from version 1 ->
- static final String ADD_CREATOR_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN creator STRING;";
- static final String ADD_DESCRIPTION_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN description STRING;";
+ @Delete
+ public abstract Single delete(Contribution contribution);
- // Upgrade from version 2 ->
- static final String ADD_MULTIPLE_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN multiple INTEGER;";
- static final String SET_DEFAULT_MULTIPLE = "UPDATE " + TABLE_NAME + " SET multiple = 0";
+ @Query("SELECT * from contribution WHERE contentProviderUri=:uri")
+ public abstract List getContributionWithUri(String uri);
- // Upgrade from version 5 ->
- static final String ADD_WIDTH_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN width INTEGER;";
- static final String SET_DEFAULT_WIDTH = "UPDATE " + TABLE_NAME + " SET width = 0";
- static final String ADD_HEIGHT_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN height INTEGER;";
- static final String SET_DEFAULT_HEIGHT = "UPDATE " + TABLE_NAME + " SET height = 0";
- static final String ADD_LICENSE_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN license STRING;";
- static final String SET_DEFAULT_LICENSE = "UPDATE " + TABLE_NAME + " SET license='" + Prefs.Licenses.CC_BY_SA_3 + "';";
+ @Query("SELECT * from contribution WHERE filename=:fileName")
+ public abstract List getContributionWithTitle(String fileName);
- // Upgrade from version 8 ->
- static final String ADD_WIKI_DATA_ENTITY_ID_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN wikidataEntityID STRING;";
+ @Query("UPDATE contribution SET state=:state WHERE state in (:toUpdateStates)")
+ public abstract Single updateStates(int state, int[] toUpdateStates);
+ @Query("Delete FROM contribution")
+ public abstract void deleteAll();
- public static void onCreate(SQLiteDatabase db) {
- db.execSQL(CREATE_TABLE_STATEMENT);
- }
-
- public static void onDelete(SQLiteDatabase db) {
- db.execSQL(DROP_TABLE_STATEMENT);
- onCreate(db);
- }
-
- public static void onUpdate(SQLiteDatabase db, int from, int to) {
- if (from == to) {
- return;
- }
-
- //Considering the crashes we have been facing recently, lets blindly add this column to any table which has ever existed
- runQuery(db,ADD_WIKI_DATA_ENTITY_ID_FIELD);
-
- if (from == 1) {
- runQuery(db,ADD_DESCRIPTION_FIELD);
- runQuery(db,ADD_CREATOR_FIELD);
- from++;
- onUpdate(db, from, to);
- return;
- }
- if (from == 2) {
- runQuery(db, ADD_MULTIPLE_FIELD);
- runQuery(db, SET_DEFAULT_MULTIPLE);
- from++;
- onUpdate(db, from, to);
- return;
- }
- if (from == 3) {
- // Do nothing
- from++;
- onUpdate(db, from, to);
- return;
- }
- if (from == 4) {
- // Do nothing -- added Category
- from++;
- onUpdate(db, from, to);
- return;
- }
- if (from == 5) {
- // Added width and height fields
- runQuery(db, ADD_WIDTH_FIELD);
- runQuery(db, SET_DEFAULT_WIDTH);
- runQuery(db, ADD_HEIGHT_FIELD);
- runQuery(db, SET_DEFAULT_HEIGHT);
- runQuery(db, ADD_LICENSE_FIELD);
- runQuery(db, SET_DEFAULT_LICENSE);
- from++;
- onUpdate(db, from, to);
- return;
- }
- if (from > 5) {
- // Added place field
- from=to;
- onUpdate(db, from, to);
- return;
- }
- }
-
- /**
- * perform the db.execSQl with handled exceptions
- */
- private static void runQuery(SQLiteDatabase db, String query) {
- try {
- db.execSQL(query);
- } catch (SQLiteException e) {
- Timber.e("Exception performing query: " + query + " message: " + e.getMessage());
- }
- }
-
- }
+ @Query("Delete FROM contribution WHERE state = :state")
+ public abstract void deleteAll(int state);
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContentProvider.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContentProvider.java
deleted file mode 100644
index 4a504f1228..0000000000
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContentProvider.java
+++ /dev/null
@@ -1,186 +0,0 @@
-package fr.free.nrw.commons.contributions;
-
-import android.content.ContentValues;
-import android.content.UriMatcher;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQueryBuilder;
-import android.net.Uri;
-import android.text.TextUtils;
-
-import androidx.annotation.NonNull;
-
-import javax.inject.Inject;
-
-import fr.free.nrw.commons.BuildConfig;
-import fr.free.nrw.commons.data.DBOpenHelper;
-import fr.free.nrw.commons.di.CommonsDaggerContentProvider;
-import timber.log.Timber;
-
-import static android.content.UriMatcher.NO_MATCH;
-import static fr.free.nrw.commons.contributions.ContributionDao.Table.ALL_FIELDS;
-import static fr.free.nrw.commons.contributions.ContributionDao.Table.TABLE_NAME;
-
-public class ContributionsContentProvider extends CommonsDaggerContentProvider {
-
- private static final int CONTRIBUTIONS = 1;
- private static final int CONTRIBUTIONS_ID = 2;
- private static final String BASE_PATH = "contributions";
- private static final UriMatcher uriMatcher = new UriMatcher(NO_MATCH);
-
- public static final Uri BASE_URI = Uri.parse("content://" + BuildConfig.CONTRIBUTION_AUTHORITY + "/" + BASE_PATH);
-
- static {
- uriMatcher.addURI(BuildConfig.CONTRIBUTION_AUTHORITY, BASE_PATH, CONTRIBUTIONS);
- uriMatcher.addURI(BuildConfig.CONTRIBUTION_AUTHORITY, BASE_PATH + "/#", CONTRIBUTIONS_ID);
- }
-
- public static Uri uriForId(int id) {
- return Uri.parse(BASE_URI.toString() + "/" + id);
- }
-
- @Inject DBOpenHelper dbOpenHelper;
-
- @SuppressWarnings("ConstantConditions")
- @Override
- public Cursor query(@NonNull Uri uri, String[] projection, String selection,
- String[] selectionArgs, String sortOrder) {
- SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
- queryBuilder.setTables(TABLE_NAME);
-
- int uriType = uriMatcher.match(uri);
-
- SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
- Cursor cursor;
-
- switch (uriType) {
- case CONTRIBUTIONS:
- cursor = queryBuilder.query(db, projection, selection, selectionArgs,
- null, null, sortOrder);
- break;
- case CONTRIBUTIONS_ID:
- cursor = queryBuilder.query(db,
- ALL_FIELDS,
- "_id = ?",
- new String[]{uri.getLastPathSegment()},
- null,
- null,
- sortOrder
- );
- break;
- default:
- throw new IllegalArgumentException("Unknown URI" + uri);
- }
-
- cursor.setNotificationUri(getContext().getContentResolver(), uri);
-
- return cursor;
- }
-
- @Override
- public String getType(@NonNull Uri uri) {
- return null;
- }
-
- @SuppressWarnings("ConstantConditions")
- @Override
- public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
- int uriType = uriMatcher.match(uri);
- SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
- long id;
- switch (uriType) {
- case CONTRIBUTIONS:
- id = sqlDB.insert(TABLE_NAME, null, contentValues);
- break;
- default:
- throw new IllegalArgumentException("Unknown URI: " + uri);
- }
- getContext().getContentResolver().notifyChange(uri, null);
- return Uri.parse(BASE_URI + "/" + id);
- }
-
- @SuppressWarnings("ConstantConditions")
- @Override
- public int delete(@NonNull Uri uri, String s, String[] strings) {
- int rows;
- int uriType = uriMatcher.match(uri);
-
- SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
-
- switch (uriType) {
- case CONTRIBUTIONS_ID:
- Timber.d("Deleting contribution id %s", uri.getLastPathSegment());
- rows = db.delete(TABLE_NAME,
- "_id = ?",
- new String[]{uri.getLastPathSegment()}
- );
- break;
- default:
- throw new IllegalArgumentException("Unknown URI" + uri);
- }
- getContext().getContentResolver().notifyChange(uri, null);
- return rows;
- }
-
- @SuppressWarnings("ConstantConditions")
- @Override
- public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
- Timber.d("Hello, bulk insert! (ContributionsContentProvider)");
- int uriType = uriMatcher.match(uri);
- SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
- sqlDB.beginTransaction();
- switch (uriType) {
- case CONTRIBUTIONS:
- for (ContentValues value : values) {
- Timber.d("Inserting! %s", value);
- sqlDB.insert(TABLE_NAME, null, value);
- }
- break;
- default:
- throw new IllegalArgumentException("Unknown URI: " + uri);
- }
- sqlDB.setTransactionSuccessful();
- sqlDB.endTransaction();
- getContext().getContentResolver().notifyChange(uri, null);
- return values.length;
- }
-
- @SuppressWarnings("ConstantConditions")
- @Override
- public int update(@NonNull Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
- /*
- SQL Injection warnings: First, note that we're not exposing this to the outside world (exported="false")
- Even then, we should make sure to sanitize all user input appropriately.
- Input that passes through ContentValues should be fine. So only issues are those that pass
- in via concatenating.
-
- In here, the only concat created argument is for id. It is cast to an int, and will
- error out otherwise.
- */
- int uriType = uriMatcher.match(uri);
- SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
- int rowsUpdated;
- switch (uriType) {
- case CONTRIBUTIONS:
- rowsUpdated = sqlDB.update(TABLE_NAME, contentValues, selection, selectionArgs);
- break;
- case CONTRIBUTIONS_ID:
- int id = Integer.valueOf(uri.getLastPathSegment());
-
- if (TextUtils.isEmpty(selection)) {
- rowsUpdated = sqlDB.update(TABLE_NAME,
- contentValues,
- ContributionDao.Table.COLUMN_ID + " = ?",
- new String[]{String.valueOf(id)});
- } else {
- throw new IllegalArgumentException(
- "Parameter `selection` should be empty when updating an ID");
- }
- break;
- default:
- throw new IllegalArgumentException("Unknown URI: " + uri + " with type " + uriType);
- }
- getContext().getContentResolver().notifyChange(uri, null);
- return rowsUpdated;
- }
-}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.java
index d62e4358a1..90120d3791 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.java
@@ -1,8 +1,6 @@
package fr.free.nrw.commons.contributions;
-import android.database.Cursor;
-
-import androidx.loader.app.LoaderManager;
+import java.util.List;
import fr.free.nrw.commons.BasePresenter;
import fr.free.nrw.commons.Media;
@@ -22,13 +20,13 @@ public interface View {
void setUploadCount(int count);
- void onDataSetChanged();
- }
+ void showContributions(List contributionList);
- public interface UserActionListener extends BasePresenter,
- LoaderManager.LoaderCallbacks {
+ void showMessage(String localizedMessage);
+ }
- Contribution getContributionsFromCursor(Cursor cursor);
+ public interface UserActionListener extends BasePresenter {
+ Contribution getContributionsWithTitle(String uri);
void deleteUpload(Contribution contribution);
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java
index 32ddc4400b..25e20cc7ac 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java
@@ -20,6 +20,8 @@
import androidx.fragment.app.FragmentManager.OnBackStackChangedListener;
import androidx.fragment.app.FragmentTransaction;
+import java.util.List;
+
import javax.inject.Inject;
import javax.inject.Named;
@@ -71,7 +73,6 @@ public class ContributionsFragment
LocationUpdateListener,
ICampaignsView, ContributionsContract.View {
@Inject @Named("default_preferences") JsonKvStore store;
- @Inject ContributionDao contributionDao;
@Inject NearbyController nearbyController;
@Inject OkHttpJsonApiClient okHttpJsonApiClient;
@Inject CampaignsPresenter presenter;
@@ -118,11 +119,11 @@ public void onServiceDisconnected(ComponentName componentName) {
};
private boolean shouldShowMediaDetailsFragment;
private int numberOfContributions;
+ private boolean isAuthCookieAcquired;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setRetainInstance(true);
}
@Nullable
@@ -132,6 +133,7 @@ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
ButterKnife.bind(this, view);
presenter.onAttachView(this);
contributionsPresenter.onAttachView(this);
+ contributionsPresenter.setLifeCycleOwner(this.getViewLifecycleOwner());
campaignView.setVisibility(View.GONE);
checkBoxView = View.inflate(getActivity(), R.layout.nearby_permission_dialog, null);
checkBox = (CheckBox) checkBoxView.findViewById(R.id.never_ask_again);
@@ -210,20 +212,10 @@ public void openMediaDetail(int position) {
showDetail(position);
}
- @Override
- public int getNumberOfContributions() {
- return numberOfContributions;
- }
-
@Override
public Contribution getContributionForPosition(int position) {
return (Contribution) contributionsPresenter.getItemAtPosition(position);
}
-
- @Override
- public int findItemPositionWithId(String id) {
- return contributionsPresenter.getChildPositionWithId(id);
- }
});
if(null==mediaDetailPagerFragment){
@@ -306,11 +298,10 @@ public void onBackStackChanged() {
*/
void onAuthCookieAcquired() {
// Since we call onAuthCookieAcquired method from onAttach, isAdded is still false. So don't use it
-
+ isAuthCookieAcquired=true;
if (getActivity() != null) { // If fragment is attached to parent activity
getActivity().bindService(getUploadServiceIntent(), uploadServiceConnection, Context.BIND_AUTO_CREATE);
isUploadServiceConnected = true;
- getActivity().getSupportLoaderManager().initLoader(0, null, contributionsPresenter);
}
}
@@ -336,7 +327,7 @@ private void showDetail(int i) {
@Override
public void refreshSource() {
- getActivity().getSupportLoaderManager().restartLoader(0, null, contributionsPresenter);
+ contributionsPresenter.fetchContributions();
}
@Override
@@ -411,6 +402,10 @@ public void onResume() {
}
fetchCampaigns();
+ if(isAuthCookieAcquired){
+ contributionsPresenter.fetchContributions();
+ }
+
}
private void checkPermissionsAndShowNearbyCardView() {
@@ -578,9 +573,8 @@ public void setUploadCount(int count) {
}
@Override
- public void onDataSetChanged() {
- contributionsListFragment.onDataSetChanged();
- mediaDetailPagerFragment.onDataSetChanged();
+ public void showContributions(List contributionList) {
+ contributionsListFragment.setContributions(contributionList);
}
/**
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.java
index 274c8a5858..234222c927 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.java
@@ -6,6 +6,9 @@
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
+import java.util.ArrayList;
+import java.util.List;
+
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.contributions.model.DisplayableContribution;
@@ -15,9 +18,11 @@
public class ContributionsListAdapter extends RecyclerView.Adapter {
private Callback callback;
+ private List contributions;
public ContributionsListAdapter(Callback callback) {
this.callback = callback;
+ contributions=new ArrayList<>();
}
/**
@@ -35,7 +40,7 @@ public ContributionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int
@Override
public void onBindViewHolder(@NonNull ContributionViewHolder holder, int position) {
- final Contribution contribution = callback.getContributionForPosition(position);
+ final Contribution contribution = contributions.get(position);
DisplayableContribution displayableContribution = new DisplayableContribution(contribution,
position);
holder.init(position, displayableContribution);
@@ -43,7 +48,15 @@ public void onBindViewHolder(@NonNull ContributionViewHolder holder, int positio
@Override
public int getItemCount() {
- return callback.getNumberOfContributions();
+ return contributions.size();
+ }
+
+ public void setContributions(List contributionList) {
+ if(null!=contributionList) {
+ this.contributions.clear();
+ this.contributions.addAll(contributionList);
+ notifyDataSetChanged();
+ }
}
public interface Callback {
@@ -54,10 +67,6 @@ public interface Callback {
void openMediaDetail(int contribution);
- int getNumberOfContributions();
-
Contribution getContributionForPosition(int position);
-
- int findItemPositionWithId(String lastVisibleItemID);
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java
index de564196f6..89a8ac2cb9 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java
@@ -20,6 +20,9 @@
import com.google.android.material.floatingactionbutton.FloatingActionButton;
+import java.util.ArrayList;
+import java.util.List;
+
import javax.inject.Inject;
import javax.inject.Named;
@@ -72,6 +75,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment {
private String lastVisibleItemID;
private int SPAN_COUNT=3;
+ private List contributions=new ArrayList<>();
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_contributions_list, container, false);
@@ -104,6 +108,7 @@ private void initRecyclerView() {
}
rvContributionsList.setAdapter(adapter);
+ adapter.setContributions(contributions);
}
@Override
@@ -178,16 +183,10 @@ public void showNoContributionsUI(boolean shouldShow) {
noContributionsYet.setVisibility(shouldShow ? VISIBLE : GONE);
}
- public void onDataSetChanged() {
- if (null != adapter) {
- adapter.notifyDataSetChanged();
- //Restoring last visible item position in cases of orientation change
- if (null != lastVisibleItemID) {
- int itemPositionWithId = callback.findItemPositionWithId(lastVisibleItemID);
- rvContributionsList.scrollToPosition(itemPositionWithId);
- lastVisibleItemID = null;//Reset the lastVisibleItemID once we have used it
- }
- }
+ public void setContributions(List contributionList) {
+ this.contributions.clear();
+ this.contributions.addAll(contributionList);
+ adapter.setContributions(contributions);
}
public interface SourceRefresher {
@@ -228,7 +227,7 @@ public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
private String findIdOfItemWithPosition(int position) {
Contribution contributionForPosition = callback.getContributionForPosition(position);
if (null != contributionForPosition) {
- return contributionForPosition.getContentUri().getLastPathSegment();
+ return contributionForPosition.getFilename();
}
return null;
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java
index 672d8ba6b2..a8b04cbd0e 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java
@@ -1,18 +1,22 @@
package fr.free.nrw.commons.contributions;
-import android.database.Cursor;
+import androidx.lifecycle.LiveData;
+
+import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import fr.free.nrw.commons.kvstore.JsonKvStore;
+import io.reactivex.Completable;
+import io.reactivex.Single;
/**
* The LocalDataSource class for Contributions
*/
class ContributionsLocalDataSource {
- private final ContributionDao contributionsDao;
+ private final ContributionDao contributionDao;
private final JsonKvStore defaultKVStore;
@Inject
@@ -20,30 +24,54 @@ public ContributionsLocalDataSource(
@Named("default_preferences") JsonKvStore defaultKVStore,
ContributionDao contributionDao) {
this.defaultKVStore = defaultKVStore;
- this.contributionsDao = contributionDao;
+ this.contributionDao = contributionDao;
}
/**
* Fetch default number of contributions to be show, based on user preferences
*/
- public int get(String key) {
- return defaultKVStore.getInt(key);
+ public String getString(String key) {
+ return defaultKVStore.getString(key);
+ }
+
+ /**
+ * Fetch default number of contributions to be show, based on user preferences
+ */
+ public long getLong(String key) {
+ return defaultKVStore.getLong(key);
}
/**
* Get contribution object from cursor
- * @param cursor
+ * @param uri
* @return
*/
- public Contribution getContributionFromCursor(Cursor cursor) {
- return contributionsDao.fromCursor(cursor);
+ public Contribution getContributionWithFileName(String uri) {
+ List contributionWithUri = contributionDao.getContributionWithTitle(uri);
+ if(!contributionWithUri.isEmpty()){
+ return contributionWithUri.get(0);
+ }
+ return null;
}
/**
* Remove a contribution from the contributions table
* @param contribution
+ * @return
*/
- public void deleteContribution(Contribution contribution) {
- contributionsDao.delete(contribution);
+ public Single deleteContribution(Contribution contribution) {
+ return contributionDao.delete(contribution);
+ }
+
+ public LiveData> getContributions() {
+ return contributionDao.fetchContributions();
+ }
+
+ public Completable saveContributions(List contributions) {
+ return contributionDao.deleteAllAndSave(contributions);
+ }
+
+ public void set(String key, long value) {
+ defaultKVStore.putLong(key,value);
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java
index cfb848c0fd..2845eec153 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java
@@ -3,104 +3,156 @@
import android.content.Context;
import android.database.Cursor;
import android.database.DataSetObserver;
-import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.loader.content.CursorLoader;
-import androidx.loader.content.Loader;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.Observer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import javax.inject.Inject;
+import javax.inject.Named;
+import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.Media;
+import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.contributions.ContributionsContract.UserActionListener;
+import fr.free.nrw.commons.db.AppDatabase;
+import fr.free.nrw.commons.di.CommonsApplicationModule;
+import fr.free.nrw.commons.mwapi.UserClient;
+import fr.free.nrw.commons.utils.ExecutorUtils;
+import fr.free.nrw.commons.utils.NetworkUtils;
+import io.reactivex.Observable;
+import io.reactivex.Scheduler;
+import io.reactivex.SingleObserver;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.CompositeDisposable;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;
-import static fr.free.nrw.commons.contributions.ContributionDao.Table.ALL_FIELDS;
-import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
-import static fr.free.nrw.commons.settings.Prefs.UPLOADS_SHOWING;
+import static fr.free.nrw.commons.contributions.Contribution.STATE_COMPLETED;
/**
* The presenter class for Contributions
*/
-public class ContributionsPresenter extends DataSetObserver implements UserActionListener {
+public class ContributionsPresenter implements UserActionListener {
private final ContributionsRepository repository;
+ private final Scheduler mainThreadScheduler;
+ private final Scheduler ioThreadScheduler;
+ private CompositeDisposable compositeDisposable;
private ContributionsContract.View view;
- private Cursor cursor;
+ private List contributionList=new ArrayList<>();
@Inject
Context context;
@Inject
- ContributionsPresenter(ContributionsRepository repository) {
+ UserClient userClient;
+
+ @Inject
+ AppDatabase appDatabase;
+
+ @Inject
+ SessionManager sessionManager;
+ private LifecycleOwner lifeCycleOwner;
+
+ @Inject
+ ContributionsPresenter(ContributionsRepository repository, @Named(CommonsApplicationModule.MAIN_THREAD) Scheduler mainThreadScheduler,@Named(CommonsApplicationModule.IO_THREAD) Scheduler ioThreadScheduler) {
this.repository = repository;
+ this.mainThreadScheduler=mainThreadScheduler;
+ this.ioThreadScheduler=ioThreadScheduler;
}
+ private String user;
+
@Override
public void onAttachView(ContributionsContract.View view) {
this.view = view;
- if (null != cursor) {
- try {
- cursor.registerDataSetObserver(this);
- } catch (IllegalStateException e) {//Cursor might be already registered
- Timber.d(e);
- }
- }
+ compositeDisposable=new CompositeDisposable();
}
- @Override
- public void onDetachView() {
- this.view = null;
- if (null != cursor) {
- try {
- cursor.unregisterDataSetObserver(this);
- } catch (Exception e) {//Cursor might not be already registered
- Timber.d(e);
- }
- }
+ public void setLifeCycleOwner(LifecycleOwner lifeCycleOwner){
+ this.lifeCycleOwner=lifeCycleOwner;
}
- @NonNull
- @Override
- public Loader onCreateLoader(int id, @Nullable Bundle args) {
- int preferredNumberOfUploads = repository.get(UPLOADS_SHOWING);
- return new CursorLoader(context, BASE_URI,
- ALL_FIELDS, "", null,
- ContributionDao.CONTRIBUTION_SORT + "LIMIT "
- + (preferredNumberOfUploads>0?preferredNumberOfUploads:100));
+ public void fetchContributions() {
+ Timber.d("fetch Contributions");
+ LiveData> liveDataContributions = repository.fetchContributions();
+ if(null!=lifeCycleOwner) {
+ liveDataContributions.observe(lifeCycleOwner, this::showContributions);
+ }
+
+ if (NetworkUtils.isInternetConnectionEstablished(CommonsApplication.getInstance()) && shouldFetchContributions()) {
+ Timber.d("fetching contributions: ");
+ view.showProgress(true);
+ this.user = sessionManager.getUserName();
+ view.showContributions(Collections.emptyList());
+ compositeDisposable.add(userClient.logEvents(user)
+ .subscribeOn(ioThreadScheduler)
+ .observeOn(mainThreadScheduler)
+ .doOnNext(mwQueryLogEvent -> Timber.d("Received image %s", mwQueryLogEvent.title()))
+ .filter(mwQueryLogEvent -> !mwQueryLogEvent.isDeleted()).doOnNext(mwQueryLogEvent -> Timber.d("Image %s passed filters", mwQueryLogEvent.title()))
+ .map(image -> {
+ Contribution contribution = new Contribution(null, null, image.title(),
+ "", -1, image.date(), image.date(), user,
+ "", "", STATE_COMPLETED);
+ return contribution;
+ })
+ .toList()
+ .subscribe(this::saveContributionsToDB, error -> {
+ Timber.e("Failed to fetch contributions: %s", error.getMessage());
+ }));
+ }
}
- @Override
- public void onLoadFinished(@NonNull Loader loader, Cursor cursor) {
+ private void showContributions(@NonNull List contributions) {
view.showProgress(false);
- if (null != cursor && cursor.getCount() > 0) {
- view.showWelcomeTip(false);
- view.showNoContributionsUI(false);
- view.setUploadCount(cursor.getCount());
- } else {
+ if (contributions.isEmpty()) {
view.showWelcomeTip(true);
view.showNoContributionsUI(true);
+ } else {
+ view.showWelcomeTip(false);
+ view.showNoContributionsUI(false);
+ view.setUploadCount(contributions.size());
+ view.showContributions(contributions);
+ this.contributionList.clear();
+ this.contributionList.addAll(contributions);
+ }
+ }
+
+ private void saveContributionsToDB(List contributions) {
+ Timber.e("Fetched: "+contributions.size()+" contributions "+" saving to db");
+ repository.save(contributions).subscribeOn(ioThreadScheduler).subscribe();
+ repository.set("last_fetch_timestamp",System.currentTimeMillis());
+ }
+
+ private boolean shouldFetchContributions() {
+ long lastFetchTimestamp = repository.getLong("last_fetch_timestamp");
+ Timber.d("last fetch timestamp: %s", lastFetchTimestamp);
+ if(lastFetchTimestamp!=0){
+ return System.currentTimeMillis()-lastFetchTimestamp>15*60*100;
}
- swapCursor(cursor);
+ Timber.d("should fetch contributions: %s", true);
+ return true;
}
@Override
- public void onLoaderReset(@NonNull Loader loader) {
- this.cursor = null;
- //On LoadFinished is not guaranteed to be called
- view.showProgress(false);
- view.showWelcomeTip(true);
- view.showNoContributionsUI(true);
- swapCursor(null);
+ public void onDetachView() {
+ this.view = null;
+ compositeDisposable.clear();
}
- /**
- * Get contribution from the repository
- */
@Override
- public Contribution getContributionsFromCursor(Cursor cursor) {
- return repository.getContributionFromCursor(cursor);
+ public Contribution getContributionsWithTitle(String title) {
+ return repository.getContributionWithFileName(title);
}
/**
@@ -109,75 +161,23 @@ public Contribution getContributionsFromCursor(Cursor cursor) {
*/
@Override
public void deleteUpload(Contribution contribution) {
- repository.deleteContributionFromDB(contribution);
+ compositeDisposable.add(repository.deleteContributionFromDB(contribution)
+ .subscribeOn(ioThreadScheduler)
+ .subscribe());
}
/**
* Returns a contribution at the specified cursor position
+ *
* @param i
* @return
*/
@Nullable
@Override
public Media getItemAtPosition(int i) {
- if (null != cursor && cursor.moveToPosition(i)) {
- return getContributionsFromCursor(cursor);
- }
- return null;
- }
-
- /**
- * Get contribution position with id
- */
- public int getChildPositionWithId(String id) {
- int position = 0;
- cursor.moveToFirst();
- while (null != cursor && cursor.moveToNext()) {
- if (getContributionsFromCursor(cursor).getContentUri().getLastPathSegment()
- .equals(id)) {
- position = cursor.getPosition();
- break;
- }
- }
- return position;
- }
-
- @Override
- public void onChanged() {
- super.onChanged();
- view.onDataSetChanged();
- }
-
- @Override
- public void onInvalidated() {
- super.onInvalidated();
- //Not letting the view know of this as of now, TODO discuss how to handle this and maybe show a proper ui for this
- }
-
- /**
- * Swap in a new Cursor, returning the old Cursor. The returned old Cursor is not
- * closed.
- *
- * @param newCursor The new cursor to be used.
- * @return Returns the previously set Cursor, or null if there was not one. If the given new
- * Cursor is the same instance is the previously set Cursor, null is also returned.
- */
- private void swapCursor(Cursor newCursor) {
- try {
- if (newCursor == cursor) {
- return;
- }
- Cursor oldCursor = cursor;
- if (oldCursor != null) {
- oldCursor.unregisterDataSetObserver(this);
- }
- cursor = newCursor;
- if (newCursor != null) {
- newCursor.registerDataSetObserver(this);
- }
- view.onDataSetChanged();
- } catch (IllegalStateException e) {//Cursor might [not] be already registered/unregistered
- Timber.e(e);
+ if (i == -1 || contributionList.size() < i+1) {
+ return null;
}
+ return contributionList.get(i);
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java
index 4c479e3000..10e0878ec9 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java
@@ -1,9 +1,14 @@
package fr.free.nrw.commons.contributions;
-import android.database.Cursor;
+import androidx.lifecycle.LiveData;
+
+import java.util.List;
import javax.inject.Inject;
+import io.reactivex.Completable;
+import io.reactivex.Single;
+
/**
* The repository class for contributions
*/
@@ -19,25 +24,41 @@ public ContributionsRepository(ContributionsLocalDataSource localDataSource) {
/**
* Fetch default number of contributions to be show, based on user preferences
*/
- public int get(String uploadsShowing) {
- return localDataSource.get(uploadsShowing);
+ public String getString(String key) {
+ return localDataSource.getString(key);
}
-
/**
- * Get contribution object from cursor from LocalDataSource
- * @param cursor
+ * Deletes a failed upload from DB
+ * @param contribution
* @return
*/
- public Contribution getContributionFromCursor(Cursor cursor) {
- return localDataSource.getContributionFromCursor(cursor);
+ public Single deleteContributionFromDB(Contribution contribution) {
+ return localDataSource.deleteContribution(contribution);
}
/**
- * Deletes a failed upload from DB
- * @param contribution
+ * Get contribution object with title
+ * @param fileName
+ * @return
*/
- public void deleteContributionFromDB(Contribution contribution) {
- localDataSource.deleteContribution(contribution);
+ public Contribution getContributionWithFileName(String fileName) {
+ return localDataSource.getContributionWithFileName(fileName);
+ }
+
+ public LiveData> fetchContributions() {
+ return localDataSource.getContributions();
+ }
+
+ public Completable save(List contributions) {
+ return localDataSource.saveContributions(contributions);
+ }
+
+ public void set(String key, long value) {
+ localDataSource.set(key,value);
+ }
+
+ public long getLong(String key) {
+ return localDataSource.getLong(key);
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java
deleted file mode 100644
index 4ce126f37a..0000000000
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package fr.free.nrw.commons.contributions;
-
-import android.accounts.Account;
-import android.annotation.SuppressLint;
-import android.content.AbstractThreadedSyncAdapter;
-import android.content.ContentProviderClient;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.SyncResult;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.os.RemoteException;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import fr.free.nrw.commons.di.ApplicationlessInjection;
-import fr.free.nrw.commons.kvstore.JsonKvStore;
-import fr.free.nrw.commons.mwapi.UserClient;
-import timber.log.Timber;
-
-import static fr.free.nrw.commons.contributions.Contribution.STATE_COMPLETED;
-import static fr.free.nrw.commons.contributions.ContributionDao.Table.COLUMN_FILENAME;
-import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
-
-@SuppressWarnings("WeakerAccess")
-public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
-
- private static final String[] existsQuery = {COLUMN_FILENAME};
- private static final String existsSelection = COLUMN_FILENAME + " = ?";
- private static final ContentValues[] EMPTY = {};
-
- @Inject
- UserClient userClient;
- @Inject
- @Named("default_preferences")
- JsonKvStore defaultKvStore;
-
- public ContributionsSyncAdapter(Context context, boolean autoInitialize) {
- super(context, autoInitialize);
- }
-
- private boolean fileExists(ContentProviderClient client, String filename) {
- if (filename == null) {
- return false;
- }
- try (Cursor cursor = client.query(BASE_URI,
- existsQuery,
- existsSelection,
- new String[]{filename},
- ""
- )) {
- return cursor != null && cursor.getCount() != 0;
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- @SuppressLint("CheckResult")
- @Override
- public void onPerformSync(Account account, Bundle bundle, String authority,
- ContentProviderClient contentProviderClient, SyncResult syncResult) {
- ApplicationlessInjection
- .getInstance(getContext()
- .getApplicationContext())
- .getCommonsApplicationComponent()
- .inject(this);
- // This code is(was?) fraught with possibilities of race conditions, but lalalalala I can't hear you!
- String user = account.name;
- ContributionDao contributionDao = new ContributionDao(() -> contentProviderClient);
- userClient.logEvents(user)
- .doOnNext(mwQueryLogEvent->Timber.d("Received image %s", mwQueryLogEvent.title() ))
- .filter(mwQueryLogEvent -> !mwQueryLogEvent.isDeleted())
- .filter(mwQueryLogEvent -> !fileExists(contentProviderClient, mwQueryLogEvent.title()))
- .doOnNext(mwQueryLogEvent->Timber.d("Image %s passed filters", mwQueryLogEvent.title() ))
- .map(image -> new Contribution(null, null, image.title(),
- "", -1, image.date(), image.date(), user,
- "", "", STATE_COMPLETED))
- .map(contributionDao::toContentValues)
- .buffer(10)
- .subscribe(imageValues->contentProviderClient.bulkInsert(BASE_URI, imageValues.toArray(EMPTY)));
- Timber.d("Oh hai, everyone! Look, a kitty!");
- }
-}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncService.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncService.java
deleted file mode 100644
index 946da69158..0000000000
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncService.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package fr.free.nrw.commons.contributions;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-public class ContributionsSyncService extends Service {
-
- private static final Object sSyncAdapterLock = new Object();
-
- private static ContributionsSyncAdapter sSyncAdapter = null;
-
- @Override
- public void onCreate() {
- super.onCreate();
- synchronized (sSyncAdapterLock) {
- if (sSyncAdapter == null) {
- sSyncAdapter = new ContributionsSyncAdapter(this, true);
- }
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return sSyncAdapter.getSyncAdapterBinder();
- }
-}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java
index 1d1e560d38..648e21ef60 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java
@@ -113,8 +113,6 @@ protected void onSaveInstanceState(Bundle outState) {
private void initMain() {
//Do not remove this, this triggers the sync service
- ContentResolver.setSyncAutomatically(sessionManager.getCurrentAccount(),BuildConfig.CONTRIBUTION_AUTHORITY,true);
- requestSync(sessionManager.getCurrentAccount(), BuildConfig.CONTRIBUTION_AUTHORITY, new Bundle());
Intent uploadServiceIntent = new Intent(this, UploadService.class);
uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE);
startService(uploadServiceIntent);
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/model/DisplayableContribution.java b/app/src/main/java/fr/free/nrw/commons/contributions/model/DisplayableContribution.java
index 28f0beca10..064cdabdf3 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/model/DisplayableContribution.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/model/DisplayableContribution.java
@@ -22,6 +22,7 @@ public DisplayableContribution(Contribution contribution,
contribution.getWidth(),
contribution.getHeight(),
contribution.getLicense());
+ this._id=contribution._id;
this.position = position;
}
diff --git a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java
index aaade6277f..b678d04918 100644
--- a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java
+++ b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java
@@ -2,18 +2,20 @@
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao;
import fr.free.nrw.commons.category.CategoryDao;
-import fr.free.nrw.commons.contributions.ContributionDao;
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao;
public class DBOpenHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "commons.db";
- private static final int DATABASE_VERSION = 11;
+ private static final int DATABASE_VERSION = 12;
+ public static final String CONTRIBUTIONS_TABLE = "contributions";
+ private final String DROP_TABLE_STATEMENT="DROP TABLE IF EXISTS %s";
/**
* Do not use directly - @Inject an instance where it's needed and let
@@ -25,7 +27,6 @@ public DBOpenHelper(Context context) {
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
- ContributionDao.Table.onCreate(sqLiteDatabase);
CategoryDao.Table.onCreate(sqLiteDatabase);
BookmarkPicturesDao.Table.onCreate(sqLiteDatabase);
BookmarkLocationsDao.Table.onCreate(sqLiteDatabase);
@@ -34,10 +35,23 @@ public void onCreate(SQLiteDatabase sqLiteDatabase) {
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int from, int to) {
- ContributionDao.Table.onUpdate(sqLiteDatabase, from, to);
CategoryDao.Table.onUpdate(sqLiteDatabase, from, to);
BookmarkPicturesDao.Table.onUpdate(sqLiteDatabase, from, to);
BookmarkLocationsDao.Table.onUpdate(sqLiteDatabase, from, to);
RecentSearchesDao.Table.onUpdate(sqLiteDatabase, from, to);
+ deleteTable(sqLiteDatabase,CONTRIBUTIONS_TABLE);
+ }
+
+ /**
+ * Delete table in the given db
+ * @param db
+ * @param tableName
+ */
+ public void deleteTable(SQLiteDatabase db, String tableName) {
+ try {
+ db.execSQL(String.format(DROP_TABLE_STATEMENT, tableName));
+ } catch (SQLiteException e) {
+ e.printStackTrace();
+ }
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.java b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.java
new file mode 100644
index 0000000000..61097fa6f0
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.java
@@ -0,0 +1,14 @@
+package fr.free.nrw.commons.db;
+
+import androidx.room.Database;
+import androidx.room.RoomDatabase;
+import androidx.room.TypeConverters;
+
+import fr.free.nrw.commons.contributions.Contribution;
+import fr.free.nrw.commons.contributions.ContributionDao;
+
+@Database(entities = {Contribution.class}, version = 1, exportSchema = false)
+@TypeConverters({Converters.class})
+abstract public class AppDatabase extends RoomDatabase {
+ public abstract ContributionDao getContributionDao();
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/db/Converters.java b/app/src/main/java/fr/free/nrw/commons/db/Converters.java
new file mode 100644
index 0000000000..bea9736128
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/db/Converters.java
@@ -0,0 +1,76 @@
+package fr.free.nrw.commons.db;
+
+import android.net.Uri;
+
+import androidx.room.TypeConverter;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+import org.wikipedia.json.GsonUtil;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+
+import fr.free.nrw.commons.CommonsApplication;
+import fr.free.nrw.commons.di.ApplicationlessInjection;
+import fr.free.nrw.commons.location.LatLng;
+
+public class Converters {
+
+ public static Gson getGson() {
+ return ApplicationlessInjection.getInstance(CommonsApplication.getInstance()).getCommonsApplicationComponent().gson();
+ }
+
+ @TypeConverter
+ public static Date fromTimestamp(Long value) {
+ return value == null ? null : new Date(value);
+ }
+
+ @TypeConverter
+ public static Long dateToTimestamp(Date date) {
+ return date == null ? null : date.getTime();
+ }
+
+ @TypeConverter
+ public static Uri fromString(String value) {
+ return value == null ? null : Uri.parse(value);
+ }
+
+ @TypeConverter
+ public static String uriToString(Uri uri) {
+ return uri == null ? null : uri.toString();
+ }
+
+ @TypeConverter
+ public static String listObjectToString(ArrayList objectList) {
+ return objectList == null ? null : getGson().toJson(objectList);
+ }
+
+ @TypeConverter
+ public static ArrayList stringToArrayListObject(String objectList) {
+ return objectList == null ? null : getGson().fromJson(objectList,new TypeToken>(){}.getType());
+ }
+
+ @TypeConverter
+ public static String mapObjectToString(HashMap objectList) {
+ return objectList == null ? null : getGson().toJson(objectList);
+ }
+
+ @TypeConverter
+ public static HashMap stringToMap(String objectList) {
+ return objectList == null ? null : getGson().fromJson(objectList,new TypeToken>(){}.getType());
+ }
+
+ @TypeConverter
+ public static String latlngObjectToString(LatLng latlng) {
+ return latlng == null ? null : getGson().toJson(latlng);
+ }
+
+ @TypeConverter
+ public static LatLng stringToLatLng(String objectList) {
+ return objectList == null ? null : getGson().fromJson(objectList,LatLng.class);
+ }
+
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.java b/app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.java
index 5cce0b84dd..ee673cdc18 100644
--- a/app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.java
+++ b/app/src/main/java/fr/free/nrw/commons/delete/DeleteHelper.java
@@ -98,7 +98,7 @@ private Observable delete(Media media, String reason) {
String userPageString = "\n{{subst:idw|" + media.getFilename() +
"}} ~~~~";
- return pageEditClient.prependEdit(media.getFilename(), fileDeleteString + "\n", summary)
+ return pageEditClient.prependEdit(media.filename, fileDeleteString + "\n", summary)
.flatMap(result -> {
if (result) {
return pageEditClient.edit("Commons:Deletion_requests/" + media.getFilename(), subpageString + "\n", summary);
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 114b0e326b..d8998b2c04 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
@@ -1,5 +1,7 @@
package fr.free.nrw.commons.di;
+import com.google.gson.Gson;
+
import javax.inject.Singleton;
import dagger.Component;
@@ -10,7 +12,6 @@
import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.contributions.ContributionViewHolder;
import fr.free.nrw.commons.contributions.ContributionsModule;
-import fr.free.nrw.commons.contributions.ContributionsSyncAdapter;
import fr.free.nrw.commons.nearby.PlaceRenderer;
import fr.free.nrw.commons.review.ReviewController;
import fr.free.nrw.commons.settings.SettingsFragment;
@@ -37,8 +38,6 @@
public interface CommonsApplicationComponent extends AndroidInjector {
void inject(CommonsApplication application);
- void inject(ContributionsSyncAdapter syncAdapter);
-
void inject(LoginActivity activity);
void inject(SettingsFragment fragment);
@@ -56,9 +55,12 @@ public interface CommonsApplicationComponent extends AndroidInjector(-180, -90, +180, +90);
}
+
+ @Provides
+ @Singleton
+ public AppDatabase provideAppDataBase() {
+ appDatabase=Room.databaseBuilder(applicationContext, AppDatabase.class, "commons_room.db").build();
+ return appDatabase;
+ }
+
+ @Provides
+ public ContributionDao providesContributionsDao() {
+ return appDatabase.getContributionDao();
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/di/ContentProviderBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/ContentProviderBuilderModule.java
index bf21b4e97f..687ef36a0d 100644
--- a/app/src/main/java/fr/free/nrw/commons/di/ContentProviderBuilderModule.java
+++ b/app/src/main/java/fr/free/nrw/commons/di/ContentProviderBuilderModule.java
@@ -5,7 +5,6 @@
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsContentProvider;
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider;
import fr.free.nrw.commons.category.CategoryContentProvider;
-import fr.free.nrw.commons.contributions.ContributionsContentProvider;
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider;
/**
@@ -17,9 +16,6 @@
@SuppressWarnings({"WeakerAccess", "unused"})
public abstract class ContentProviderBuilderModule {
- @ContributesAndroidInjector
- abstract ContributionsContentProvider bindContributionsContentProvider();
-
@ContributesAndroidInjector
abstract CategoryContentProvider bindCategoryContentProvider();
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java
index e9b0b06291..9630d83e99 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java
+++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java
@@ -205,6 +205,7 @@ public Observable buildContributions() {
contribution.setTag("mimeType", item.mimeType);
contribution.setSource(item.source);
contribution.setContentProviderUri(item.mediaUri);
+ contribution.setDateUploaded(new Date());
Timber.d("Created timestamp while building contribution is %s, %s",
item.getCreatedTimestamp(),
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java
index 306a01c980..b37dfd0111 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java
+++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java
@@ -3,7 +3,6 @@
import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.ContentResolver;
-import android.content.ContentValues;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.net.Uri;
@@ -20,6 +19,7 @@
import java.util.regex.Pattern;
import javax.inject.Inject;
+import javax.inject.Named;
import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.CommonsApplication;
@@ -28,12 +28,16 @@
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.contributions.ContributionDao;
-import fr.free.nrw.commons.contributions.ContributionsContentProvider;
import fr.free.nrw.commons.contributions.MainActivity;
+import fr.free.nrw.commons.di.CommonsApplicationModule;
import fr.free.nrw.commons.media.MediaClient;
import fr.free.nrw.commons.utils.CommonsDateUtil;
import fr.free.nrw.commons.wikidata.WikidataEditService;
import io.reactivex.Observable;
+import io.reactivex.Scheduler;
+import io.reactivex.SingleObserver;
+import io.reactivex.disposables.CompositeDisposable;
+import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;
@@ -46,16 +50,22 @@ public class UploadService extends HandlerService {
public static final String ACTION_START_SERVICE = EXTRA_PREFIX + ".upload";
public static final String EXTRA_SOURCE = EXTRA_PREFIX + ".source";
public static final String EXTRA_FILES = EXTRA_PREFIX + ".files";
-
@Inject WikidataEditService wikidataEditService;
@Inject SessionManager sessionManager;
- @Inject ContributionDao contributionDao;
+ @Inject
+ ContributionDao contributionDao;
@Inject UploadClient uploadClient;
@Inject MediaClient mediaClient;
+ @Inject
+ @Named(CommonsApplicationModule.MAIN_THREAD)
+ Scheduler mainThreadScheduler;
+ @Inject
+ @Named(CommonsApplicationModule.IO_THREAD) Scheduler ioThreadScheduler;
private NotificationManagerCompat notificationManager;
private NotificationCompat.Builder curNotification;
private int toUpload;
+ private CompositeDisposable compositeDisposable;
/**
* The filePath names of unfinished uploads, used to prevent overwriting
@@ -105,7 +115,10 @@ public void onProgress(long transferred, long total) {
notificationManager.notify(notificationTag, NOTIFICATION_UPLOAD_IN_PROGRESS, curNotification.build());
contribution.setTransferred(transferred);
- contributionDao.save(contribution);
+ compositeDisposable.add(contributionDao.
+ save(contribution).subscribeOn(ioThreadScheduler)
+ .observeOn(mainThreadScheduler)
+ .subscribe());
}
}
@@ -113,6 +126,7 @@ public void onProgress(long transferred, long total) {
@Override
public void onDestroy() {
super.onDestroy();
+ compositeDisposable.dispose();
Timber.d("UploadService.onDestroy; %s are yet to be uploaded", unfinishedUploads);
}
@@ -120,6 +134,7 @@ public void onDestroy() {
public void onCreate() {
super.onCreate();
CommonsApplication.createNotificationChannel(getApplicationContext());
+ compositeDisposable = new CompositeDisposable();
notificationManager = NotificationManagerCompat.from(this);
curNotification = getNotificationBuilder(CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL);
}
@@ -143,15 +158,20 @@ public void queue(int what, Contribution contribution) {
contribution.setState(Contribution.STATE_QUEUED);
contribution.setTransferred(0);
- contributionDao.save(contribution);
toUpload++;
if (curNotification != null && toUpload != 1) {
curNotification.setContentText(getResources().getQuantityString(R.plurals.uploads_pending_notification_indicator, toUpload, toUpload));
Timber.d("%d uploads left", toUpload);
notificationManager.notify(contribution.getLocalUri().toString(), NOTIFICATION_UPLOAD_IN_PROGRESS, curNotification.build());
}
-
- super.queue(what, contribution);
+ compositeDisposable.add(contributionDao
+ .save(contribution)
+ .subscribeOn(ioThreadScheduler)
+ .observeOn(mainThreadScheduler)
+ .subscribe(aLong->{
+ contribution._id = aLong;
+ UploadService.super.queue(what, contribution);
+ }, Throwable::printStackTrace));
break;
default:
throw new IllegalArgumentException("Unknown value for what");
@@ -163,16 +183,10 @@ public void queue(int what, Contribution contribution) {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (ACTION_START_SERVICE.equals(intent.getAction()) && freshStart) {
- ContentValues failedValues = new ContentValues();
- failedValues.put(ContributionDao.Table.COLUMN_STATE, Contribution.STATE_FAILED);
-
- int updated = getContentResolver().update(ContributionsContentProvider.BASE_URI,
- failedValues,
- ContributionDao.Table.COLUMN_STATE + " = ? OR " + ContributionDao.Table.COLUMN_STATE + " = ?",
- new String[]{ String.valueOf(Contribution.STATE_QUEUED), String.valueOf(Contribution.STATE_IN_PROGRESS) }
- );
- Timber.d("Set %d uploads to failed", updated);
- Timber.d("Flags is %d id is %d", flags, startId);
+ compositeDisposable.add(contributionDao.updateStates(Contribution.STATE_FAILED, new int[]{Contribution.STATE_QUEUED, Contribution.STATE_IN_PROGRESS})
+ .observeOn(mainThreadScheduler)
+ .subscribeOn(ioThreadScheduler)
+ .subscribe());
freshStart = false;
}
return START_REDELIVER_INTENT;
@@ -272,7 +286,11 @@ private void uploadContribution(Contribution contribution) {
contribution.setState(Contribution.STATE_COMPLETED);
contribution.setDateUploaded(CommonsDateUtil.getIso8601DateFormatShort()
.parse(uploadResult.getImageinfo().getTimestamp()));
- contributionDao.save(contribution);
+ compositeDisposable.add(contributionDao
+ .save(contribution)
+ .subscribeOn(ioThreadScheduler)
+ .observeOn(mainThreadScheduler)
+ .subscribe());
}
}, throwable -> {
Timber.w(throwable, "Exception during upload");
@@ -291,7 +309,10 @@ private void showFailedNotification(Contribution contribution) {
notificationManager.notify(contribution.getLocalUri().toString(), NOTIFICATION_UPLOAD_FAILED, curNotification.build());
contribution.setState(Contribution.STATE_FAILED);
- contributionDao.save(contribution);
+ compositeDisposable.add(contributionDao.save(contribution)
+ .subscribeOn(ioThreadScheduler)
+ .observeOn(mainThreadScheduler)
+ .subscribe());
}
private String findUniqueFilename(String fileName) throws IOException {
diff --git a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt
index a59748f1d8..de3081e764 100644
--- a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt
+++ b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt
@@ -25,6 +25,14 @@ class TestCommonsApplication : Application() {
.build()
}
super.onCreate()
+ context=applicationContext
+ }
+
+ companion object{
+ private var context: Context?=null
+ fun getContext(): Context? {
+ return context
+ }
}
}
diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionDaoTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionDaoTest.kt
deleted file mode 100644
index b388937ee6..0000000000
--- a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionDaoTest.kt
+++ /dev/null
@@ -1,348 +0,0 @@
-package fr.free.nrw.commons.contributions
-
-import android.content.ContentProviderClient
-import android.content.ContentValues
-import android.database.MatrixCursor
-import android.database.sqlite.SQLiteDatabase
-import android.net.Uri
-import android.os.RemoteException
-import com.nhaarman.mockitokotlin2.*
-import fr.free.nrw.commons.BuildConfig
-import fr.free.nrw.commons.TestCommonsApplication
-import fr.free.nrw.commons.Utils
-import fr.free.nrw.commons.contributions.Contribution.*
-import fr.free.nrw.commons.contributions.ContributionDao.Table
-import fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI
-import fr.free.nrw.commons.contributions.ContributionsContentProvider.uriForId
-import org.junit.Assert.*
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.robolectric.RobolectricTestRunner
-import org.robolectric.annotation.Config
-import java.util.*
-
-@RunWith(RobolectricTestRunner::class)
-@Config(sdk = [21], application = TestCommonsApplication::class)
-class ContributionDaoTest {
- private val localUri = "http://example.com/"
- private val client: ContentProviderClient = mock()
- private val database: SQLiteDatabase = mock()
- private val captor = argumentCaptor()
-
- private lateinit var contentUri: Uri
- private lateinit var testObject: ContributionDao
-
- @Before
- fun setUp() {
- contentUri = uriForId(111)
- testObject = ContributionDao { client }
- }
-
- @Test
- fun createTable() {
- Table.onCreate(database)
- verify(database).execSQL(Table.CREATE_TABLE_STATEMENT)
- }
-
- @Test
- fun deleteTable() {
- Table.onDelete(database)
-
- inOrder(database) {
- verify(database).execSQL(Table.DROP_TABLE_STATEMENT)
- verify(database).execSQL(Table.CREATE_TABLE_STATEMENT)
- }
- }
-
- @Test
- fun upgradeDatabase_v1_to_v2() {
- Table.onUpdate(database, 1, 2)
-
- inOrder(database) {
- verify(database).execSQL(Table.ADD_DESCRIPTION_FIELD)
- verify(database).execSQL(Table.ADD_CREATOR_FIELD)
- }
- }
-
- @Test
- fun upgradeDatabase_v2_to_v3() {
- Table.onUpdate(database, 2, 3)
-
- inOrder(database) {
- verify(database).execSQL(Table.ADD_MULTIPLE_FIELD)
- verify(database).execSQL(Table.SET_DEFAULT_MULTIPLE)
- }
- }
-
- @Test
- fun upgradeDatabase_v3_to_v4() {
- Table.onUpdate(database, 3, 4)
- }
-
- @Test
- fun upgradeDatabase_v4_to_v5() {
- Table.onUpdate(database, 4, 5)
- }
-
- @Test
- fun upgradeDatabase_v5_to_v6() {
- Table.onUpdate(database, 5, 6)
-
- inOrder(database) {
- verify(database).execSQL(Table.ADD_WIDTH_FIELD)
- verify(database).execSQL(Table.SET_DEFAULT_WIDTH)
- verify(database).execSQL(Table.ADD_HEIGHT_FIELD)
- verify(database).execSQL(Table.SET_DEFAULT_HEIGHT)
- verify(database).execSQL(Table.ADD_LICENSE_FIELD)
- verify(database).execSQL(Table.SET_DEFAULT_LICENSE)
- }
- }
-
- @Test
- fun migrateTableVersionFrom_v6_to_v7() {
- Table.onUpdate(database, 6, 7)
- // Table has changed in version 7
- inOrder(database) {
- verify(database).execSQL(Table.ADD_WIKI_DATA_ENTITY_ID_FIELD)
- }
- }
-
- @Test
- fun migrateTableVersionFrom_v7_to_v8() {
- Table.onUpdate(database, 7, 8)
- // Table has changed in version 8
- inOrder(database) {
- verify(database).execSQL(Table.ADD_WIKI_DATA_ENTITY_ID_FIELD)
- }
- }
-
- @Test
- fun migrateTableVersionFrom_v8_to_v9() {
- Table.onUpdate(database, 8, 9)
- // Table changed in version 9
- inOrder(database) {
- verify(database).execSQL(Table.ADD_WIKI_DATA_ENTITY_ID_FIELD)
- }
- }
-
- @Test
- fun migrateTableVersionFrom_v9_to_v10() {
- Table.onUpdate(database, 8, 9)
- // Table changed in version 9
- inOrder(database) {
- verify(database).execSQL(Table.ADD_WIKI_DATA_ENTITY_ID_FIELD)
- }
- }
-
- @Test
- fun saveNewContribution_nonNullFields() {
- whenever(client.insert(isA(), isA())).thenReturn(contentUri)
- val contribution = createContribution(true, null, null, null, null)
-
- testObject.save(contribution)
-
- assertEquals(contentUri, contribution.contentUri)
- verify(client).insert(eq(BASE_URI), captor.capture())
- captor.firstValue.let {
- // Long fields
- assertEquals(222L, it.getAsLong(Table.COLUMN_LENGTH))
- assertEquals(321L, it.getAsLong(Table.COLUMN_TIMESTAMP))
- assertEquals(333L, it.getAsLong(Table.COLUMN_TRANSFERRED))
-
- // Integer fields
- assertEquals(STATE_COMPLETED, it.getAsInteger(Table.COLUMN_STATE))
- assertEquals(640, it.getAsInteger(Table.COLUMN_WIDTH))
- assertEquals(480, it.getAsInteger(Table.COLUMN_HEIGHT))
-
- // String fields
- assertEquals(SOURCE_CAMERA, it.getAsString(Table.COLUMN_SOURCE))
- assertEquals("desc", it.getAsString(Table.COLUMN_DESCRIPTION))
- assertEquals("create", it.getAsString(Table.COLUMN_CREATOR))
- assertEquals("007", it.getAsString(Table.COLUMN_LICENSE))
- }
- }
-
- @Test
- fun saveNewContribution_nullableFieldsAreNull() {
- whenever(client.insert(isA(), isA())).thenReturn(contentUri)
- val contribution = createContribution(true, null, null, null, null)
-
- testObject.save(contribution)
-
- assertEquals(contentUri, contribution.contentUri)
- verify(client).insert(eq(BASE_URI), captor.capture())
- captor.firstValue.let {
- // Nullable fields are absent if null
- assertFalse(it.containsKey(Table.COLUMN_LOCAL_URI))
- assertFalse(it.containsKey(Table.COLUMN_IMAGE_URL))
- assertFalse(it.containsKey(Table.COLUMN_UPLOADED))
- }
- }
-
- @Test
- fun saveNewContribution_nullableFieldsAreNonNull() {
- whenever(client.insert(isA(), isA())).thenReturn(contentUri)
- val contribution = createContribution(true, Uri.parse(localUri),
- "image", Date(456L), null)
-
- testObject.save(contribution)
-
- assertEquals(contentUri, contribution.contentUri)
- verify(client).insert(eq(BASE_URI), captor.capture())
- captor.firstValue.let {
- assertEquals(localUri, it.getAsString(Table.COLUMN_LOCAL_URI))
- assertEquals("image", it.getAsString(Table.COLUMN_IMAGE_URL))
- assertEquals(456L, it.getAsLong(Table.COLUMN_UPLOADED))
- }
- }
-
- @Test
- fun saveNewContribution_booleanEncodesTrue() {
- whenever(client.insert(isA(), isA())).thenReturn(contentUri)
- val contribution = createContribution(true, null, null, null, null)
-
- testObject.save(contribution)
-
- assertEquals(contentUri, contribution.contentUri)
- verify(client).insert(eq(BASE_URI), captor.capture())
-
- // Boolean true --> 1 for ths encoding scheme
- assertEquals("Boolean true should be encoded as 1", 1,
- captor.firstValue.getAsInteger(Table.COLUMN_MULTIPLE))
- }
-
- @Test
- fun saveNewContribution_booleanEncodesFalse() {
- whenever(client.insert(isA(), isA())).thenReturn(contentUri)
- val contribution = createContribution(false, null, null, null, null)
-
- testObject.save(contribution)
-
- assertEquals(contentUri, contribution.contentUri)
- verify(client).insert(eq(BASE_URI), captor.capture())
-
- // Boolean true --> 1 for ths encoding scheme
- assertEquals("Boolean false should be encoded as 0", 0,
- captor.firstValue.getAsInteger(Table.COLUMN_MULTIPLE))
- }
-
- @Test
- fun saveExistingContribution() {
- val contribution = createContribution(false, null, null, null, null)
- contribution.contentUri = contentUri
-
- testObject.save(contribution)
-
- verify(client).update(eq(contentUri), isA(), isNull(), isNull())
- }
-
- @Test(expected = RuntimeException::class)
- fun saveTranslatesExceptions() {
- whenever(client.insert(isA(), isA())).thenThrow(RemoteException(""))
-
- testObject.save(createContribution(false, null, null, null, null))
- }
-
- @Test(expected = RuntimeException::class)
- fun deleteTranslatesExceptions() {
- whenever(client.delete(anyOrNull(), anyOrNull(), anyOrNull())).thenThrow(RemoteException(""))
-
- val contribution = createContribution(false, null, null, null, null)
- contribution.contentUri = contentUri
- testObject.delete(contribution)
- }
-
- @Test(expected = RuntimeException::class)
- fun exceptionThrownWhenAttemptingToDeleteUnsavedContribution() {
- testObject.delete(createContribution(false, null, null, null, null))
- }
-
- @Test
- fun deleteExistingContribution() {
- val contribution = createContribution(false, null, null, null, null)
- contribution.contentUri = contentUri
-
- testObject.delete(contribution)
-
- verify(client).delete(eq(contentUri), isNull(), isNull())
- }
-
- @Test
- fun createFromCursor() {
- val created = 321L
- val uploaded = 456L
- createCursor(created, uploaded, false, localUri).let { mc ->
- testObject.fromCursor(mc).let {
- assertEquals(uriForId(111), it.contentUri)
- assertEquals("filePath", it.filename)
- assertEquals(localUri, it.localUri.toString())
- assertEquals("image", it.imageUrl)
- assertEquals(created, it.dateCreated.time)
- assertEquals(STATE_QUEUED, it.state)
- assertEquals(222L, it.dataLength)
- assertEquals(uploaded, it.dateUploaded?.time)
- assertEquals(88L, it.transferred)
- assertEquals(SOURCE_GALLERY, it.source)
- assertEquals("desc", it.description)
- assertEquals("create", it.creator)
- assertEquals(640, it.width)
- assertEquals(480, it.height)
- assertEquals("007", it.license)
- }
- }
- }
-
- @Test
- fun createFromCursor_nullableTimestamps() {
- createCursor(0L, 0L, false, localUri).let { mc ->
- testObject.fromCursor(mc).let {
- assertNull(it.dateCreated)
- assertNull(it.dateUploaded)
- }
- }
- }
-
- @Test
- fun createFromCursor_nullableLocalUri() {
- createCursor(0L, 0L, false, "").let { mc ->
- testObject.fromCursor(mc).let {
- assertNull(it.localUri)
- assertNull(it.dateCreated)
- assertNull(it.dateUploaded)
- }
- }
- }
-
- @Test
- fun createFromCursor_booleanEncoding() {
- val mcFalse = createCursor(0L, 0L, false, localUri)
- assertFalse(testObject.fromCursor(mcFalse).multiple)
-
- val mcHammer = createCursor(0L, 0L, true, localUri)
- assertTrue(testObject.fromCursor(mcHammer).multiple)
- }
-
- private fun createCursor(created: Long, uploaded: Long, multiple: Boolean, localUri: String) =
- MatrixCursor(Table.ALL_FIELDS, 1).apply {
- addRow(listOf("111", "filePath", localUri, "image",
- created, STATE_QUEUED, 222L, uploaded, 88L, SOURCE_GALLERY, "desc",
- "create", if (multiple) 1 else 0, 640, 480, "007", "Q1"))
- moveToFirst()
- }
-
- private fun createContribution(isMultiple: Boolean, localUri: Uri?, imageUrl: String?, dateUploaded: Date?, filename: String?): Contribution {
- val contribution = Contribution(localUri, imageUrl, filename, "desc", 222L, Date(321L), dateUploaded,
- "create", "edit", "coords").apply {
- state = STATE_COMPLETED
- transferred = 333L
- source = SOURCE_CAMERA
- license = "007"
- multiple = isMultiple
- width = 640
- height = 480 // VGA should be enough for anyone, right?
- }
- contribution.wikiDataEntityId = "Q1"
- return contribution
- }
-}
diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsPresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsPresenterTest.kt
index 2dee682c81..e318b2ea78 100644
--- a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsPresenterTest.kt
+++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsPresenterTest.kt
@@ -1,11 +1,22 @@
package fr.free.nrw.commons.contributions
import android.database.Cursor
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
import androidx.loader.content.CursorLoader
import androidx.loader.content.Loader
+import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
+import com.nhaarman.mockitokotlin2.whenever
+import io.reactivex.Scheduler
+import io.reactivex.Single
+import io.reactivex.schedulers.TestScheduler
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
+import org.mockito.ArgumentMatchers
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
@@ -15,11 +26,11 @@ import org.mockito.MockitoAnnotations
*/
class ContributionsPresenterTest {
@Mock
- internal var repository: ContributionsRepository? = null
+ internal lateinit var repository: ContributionsRepository
@Mock
- internal var view: ContributionsContract.View? = null
+ internal lateinit var view: ContributionsContract.View
- private var contributionsPresenter: ContributionsPresenter? = null
+ private lateinit var contributionsPresenter: ContributionsPresenter
private lateinit var cursor: Cursor
@@ -27,6 +38,12 @@ class ContributionsPresenterTest {
lateinit var loader: Loader
+ lateinit var liveData: LiveData>
+
+ @Rule @JvmField var instantTaskExecutorRule = InstantTaskExecutorRule()
+
+ lateinit var scheduler : Scheduler
+
/**
* initial setup
*/
@@ -34,21 +51,24 @@ class ContributionsPresenterTest {
@Throws(Exception::class)
fun setUp() {
MockitoAnnotations.initMocks(this)
+ scheduler=TestScheduler()
cursor = Mockito.mock(Cursor::class.java)
contribution = Mockito.mock(Contribution::class.java)
- contributionsPresenter = ContributionsPresenter(repository)
+ contributionsPresenter = ContributionsPresenter(repository,scheduler,scheduler)
loader = Mockito.mock(CursorLoader::class.java)
- contributionsPresenter?.onAttachView(view)
+ contributionsPresenter.onAttachView(view)
+ liveData=MutableLiveData()
}
-
/**
- * Test presenter actions onGetContributionFromCursor
+ * Test fetch contributions
*/
@Test
- fun testGetContributionFromCursor() {
- contributionsPresenter?.getContributionsFromCursor(cursor)
- verify(repository)?.getContributionFromCursor(cursor)
+ fun testFetchContributions(){
+ whenever(repository.getString(ArgumentMatchers.anyString())).thenReturn("10")
+ whenever(repository.fetchContributions()).thenReturn(liveData)
+ contributionsPresenter.fetchContributions()
+ verify(repository).fetchContributions()
}
/**
@@ -56,55 +76,20 @@ class ContributionsPresenterTest {
*/
@Test
fun testDeleteContribution() {
- contributionsPresenter?.deleteUpload(contribution)
- verify(repository)?.deleteContributionFromDB(contribution)
- }
-
- /**
- * Test presenter actions on loaderFinished and has non zero media objects
- */
- @Test
- fun testOnLoaderFinishedNonZeroContributions() {
- Mockito.`when`(cursor.count).thenReturn(1)
- contributionsPresenter?.onLoadFinished(loader, cursor)
- verify(view)?.showProgress(false)
- verify(view)?.showWelcomeTip(false)
- verify(view)?.showNoContributionsUI(false)
- verify(view)?.setUploadCount(cursor.count)
+ whenever(repository.deleteContributionFromDB(ArgumentMatchers.any(Contribution::class.java))).thenReturn(Single.just(1))
+ contributionsPresenter.deleteUpload(contribution)
+ verify(repository).deleteContributionFromDB(contribution)
}
/**
- * Test presenter actions on loaderFinished and has Zero media objects
+ * Test fetch contribution with filename
*/
@Test
- fun testOnLoaderFinishedZeroContributions() {
- Mockito.`when`(cursor.count).thenReturn(0)
- contributionsPresenter?.onLoadFinished(loader, cursor)
- verify(view)?.showProgress(false)
- verify(view)?.showWelcomeTip(true)
- verify(view)?.showNoContributionsUI(true)
+ fun testGetContributionWithFileName(){
+ contributionsPresenter.getContributionsWithTitle("ashish")
+ verify(repository).getContributionWithFileName("ashish")
}
- /**
- * Test presenter actions on loader reset
- */
- @Test
- fun testOnLoaderReset() {
- contributionsPresenter?.onLoaderReset(loader)
- verify(view)?.showProgress(false)
- verify(view)?.showWelcomeTip(true)
- verify(view)?.showNoContributionsUI(true)
- }
-
- /**
- * Test presenter actions on loader change
- */
- @Test
- fun testOnChanged() {
- contributionsPresenter?.onChanged()
- verify(view)?.onDataSetChanged()
- }
-
}
\ No newline at end of file
diff --git a/app/src/test/kotlin/fr/free/nrw/commons/delete/DeleteHelperTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/delete/DeleteHelperTest.kt
index 8df1971a1d..d0faddc8f6 100644
--- a/app/src/test/kotlin/fr/free/nrw/commons/delete/DeleteHelperTest.kt
+++ b/app/src/test/kotlin/fr/free/nrw/commons/delete/DeleteHelperTest.kt
@@ -63,7 +63,7 @@ class DeleteHelperTest {
.thenReturn(Observable.just(true))
`when`(media?.displayTitle).thenReturn("Test file")
- `when`(media?.filename).thenReturn("Test file.jpg")
+ media?.filename="Test file.jpg"
val makeDeletion = deleteHelper?.makeDeletion(context, media, "Test reason")?.blockingGet()
assertNotNull(makeDeletion)
diff --git a/app/src/test/kotlin/fr/free/nrw/commons/delete/ReasonBuilderTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/delete/ReasonBuilderTest.kt
index c7dc588e31..6b21d15b68 100644
--- a/app/src/test/kotlin/fr/free/nrw/commons/delete/ReasonBuilderTest.kt
+++ b/app/src/test/kotlin/fr/free/nrw/commons/delete/ReasonBuilderTest.kt
@@ -55,8 +55,8 @@ class ReasonBuilderTest {
`when`(okHttpJsonApiClient!!.getAchievements(anyString()))
.thenReturn(Single.just(mock(FeedbackResponse::class.java)))
- val media = mock(Media::class.java)
- `when`(media!!.dateUploaded).thenReturn(Date())
+ val media = Media("test_file")
+ media.dateUploaded=Date()
reasonBuilder!!.getReason(media, "test")
verify(sessionManager, times(0))!!.forceLogin(any(Context::class.java))
diff --git a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewHelperTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewHelperTest.kt
index fbad4a46ff..95c857c6d8 100644
--- a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewHelperTest.kt
+++ b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewHelperTest.kt
@@ -61,7 +61,7 @@ class ReviewHelperTest {
.thenReturn(Observable.just(mockResponse))
val media = mock(Media::class.java)
- `when`(media.filename).thenReturn("File:Test.jpg")
+ media.filename="File:Test.jpg"
`when`(mediaClient?.getMedia(ArgumentMatchers.anyString()))
.thenReturn(Single.just(media))
}
@@ -74,10 +74,10 @@ class ReviewHelperTest {
`when`(mediaClient?.checkPageExistsUsingTitle(ArgumentMatchers.anyString()))
.thenReturn(Single.just(false))
- val randomMedia = reviewHelper?.randomMedia?.blockingGet()
+ `when`(mediaClient?.checkPageExistsUsingTitle(ArgumentMatchers.anyString()))
+ .thenReturn(Single.just(false))
- assertNotNull(randomMedia)
- assertTrue(randomMedia is Media)
+ reviewHelper?.randomMedia
verify(reviewInterface, times(1))!!.getRecentChanges(ArgumentMatchers.anyString())
}
@@ -105,10 +105,7 @@ class ReviewHelperTest {
`when`(mediaClient?.checkPageExistsUsingTitle("Commons:Deletion_requests/File:Test3.jpg"))
.thenReturn(Single.just(true))
- val media = reviewHelper?.randomMedia?.blockingGet()
-
- assertNotNull(media)
- assertTrue(media is Media)
+ reviewHelper?.randomMedia
verify(reviewInterface, times(1))!!.getRecentChanges(ArgumentMatchers.anyString())
}
diff --git a/captures/fr.free.nrw.commons_2019.04.15_22.10.li b/captures/fr.free.nrw.commons_2019.04.15_22.10.li
deleted file mode 100644
index 9612bf7556..0000000000
Binary files a/captures/fr.free.nrw.commons_2019.04.15_22.10.li and /dev/null differ