mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 04:43:54 +01:00
* #3482 Use Room in Structured Data branch - remove unused code * #3482 Use Room in Structured Data branch - fix unit test compilation * #3482 Use Room in Structured Data branch - add kdoc
This commit is contained in:
parent
942cef5d5e
commit
66e195d88b
20 changed files with 131 additions and 754 deletions
|
|
@ -107,6 +107,7 @@ dependencies {
|
||||||
//Room
|
//Room
|
||||||
def room_version= '2.2.3'
|
def room_version= '2.2.3'
|
||||||
implementation "androidx.room:room-runtime:$room_version"
|
implementation "androidx.room:room-runtime:$room_version"
|
||||||
|
implementation "androidx.room:room-ktx:$room_version"
|
||||||
kapt "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor
|
kapt "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor
|
||||||
implementation 'com.squareup.retrofit2:retrofit:2.7.1'
|
implementation 'com.squareup.retrofit2:retrofit:2.7.1'
|
||||||
implementation "androidx.room:room-rxjava2:$room_version"
|
implementation "androidx.room:room-rxjava2:$room_version"
|
||||||
|
|
|
||||||
|
|
@ -183,14 +183,7 @@
|
||||||
android:authorities="${applicationId}.categories.contentprovider"
|
android:authorities="${applicationId}.categories.contentprovider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:label="@string/provider_categories"
|
android:label="@string/provider_categories"
|
||||||
android:syncable="false" />
|
android:syncable="false" />
|
||||||
|
|
||||||
<provider
|
|
||||||
android:authorities="${applicationId}.depicts.contentprovider"
|
|
||||||
android:name=".upload.structure.depictions.DepictsContentProvider"
|
|
||||||
android:exported="false"
|
|
||||||
android:label="@string/provider_depictions"
|
|
||||||
android:syncable="false"/>
|
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name=".explore.recentsearches.RecentSearchesContentProvider"
|
android:name=".explore.recentsearches.RecentSearchesContentProvider"
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteException;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
@ -16,10 +15,7 @@ import androidx.annotation.NonNull;
|
||||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||||
import com.facebook.imagepipeline.core.ImagePipeline;
|
import com.facebook.imagepipeline.core.ImagePipeline;
|
||||||
import com.facebook.imagepipeline.core.ImagePipelineConfig;
|
import com.facebook.imagepipeline.core.ImagePipelineConfig;
|
||||||
import com.facebook.imagepipeline.producers.Consumer;
|
|
||||||
import com.facebook.imagepipeline.producers.FetchState;
|
|
||||||
import com.facebook.imagepipeline.producers.NetworkFetcher;
|
import com.facebook.imagepipeline.producers.NetworkFetcher;
|
||||||
import com.facebook.imagepipeline.producers.ProducerContext;
|
|
||||||
import com.mapbox.mapboxsdk.Mapbox;
|
import com.mapbox.mapboxsdk.Mapbox;
|
||||||
import com.squareup.leakcanary.LeakCanary;
|
import com.squareup.leakcanary.LeakCanary;
|
||||||
import com.squareup.leakcanary.RefWatcher;
|
import com.squareup.leakcanary.RefWatcher;
|
||||||
|
|
@ -45,15 +41,14 @@ import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao;
|
||||||
import fr.free.nrw.commons.category.CategoryDao;
|
import fr.free.nrw.commons.category.CategoryDao;
|
||||||
import fr.free.nrw.commons.concurrency.BackgroundPoolExceptionHandler;
|
import fr.free.nrw.commons.concurrency.BackgroundPoolExceptionHandler;
|
||||||
import fr.free.nrw.commons.concurrency.ThreadPoolService;
|
import fr.free.nrw.commons.concurrency.ThreadPoolService;
|
||||||
|
import fr.free.nrw.commons.contributions.ContributionDao;
|
||||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||||
import fr.free.nrw.commons.db.AppDatabase;
|
|
||||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||||
import fr.free.nrw.commons.logging.FileLoggingTree;
|
import fr.free.nrw.commons.logging.FileLoggingTree;
|
||||||
import fr.free.nrw.commons.logging.LogUtils;
|
import fr.free.nrw.commons.logging.LogUtils;
|
||||||
import fr.free.nrw.commons.settings.Prefs;
|
import fr.free.nrw.commons.settings.Prefs;
|
||||||
import fr.free.nrw.commons.upload.FileUtils;
|
import fr.free.nrw.commons.upload.FileUtils;
|
||||||
import fr.free.nrw.commons.upload.structure.depictions.DepictionDao;
|
|
||||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.internal.functions.Functions;
|
import io.reactivex.internal.functions.Functions;
|
||||||
|
|
@ -129,8 +124,7 @@ public class CommonsApplication extends Application {
|
||||||
return languageLookUpTable;
|
return languageLookUpTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject ContributionDao contributionDao;
|
||||||
AppDatabase appDatabase;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to declare and initialize various components and dependencies
|
* Used to declare and initialize various components and dependencies
|
||||||
|
|
@ -312,9 +306,8 @@ public class CommonsApplication extends Application {
|
||||||
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
|
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
|
||||||
|
|
||||||
CategoryDao.Table.onDelete(db);
|
CategoryDao.Table.onDelete(db);
|
||||||
DepictionDao.Table.onDelete(db);
|
|
||||||
dbOpenHelper.deleteTable(db,CONTRIBUTIONS_TABLE);//Delete the contributions table in the existing db on older versions
|
dbOpenHelper.deleteTable(db,CONTRIBUTIONS_TABLE);//Delete the contributions table in the existing db on older versions
|
||||||
appDatabase.getContributionDao().deleteAll();
|
contributionDao.deleteAll();
|
||||||
BookmarkPicturesDao.Table.onDelete(db);
|
BookmarkPicturesDao.Table.onDelete(db);
|
||||||
BookmarkLocationsDao.Table.onDelete(db);
|
BookmarkLocationsDao.Table.onDelete(db);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
|
||||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao;
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao;
|
||||||
import fr.free.nrw.commons.category.CategoryDao;
|
import fr.free.nrw.commons.category.CategoryDao;
|
||||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao;
|
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao;
|
||||||
import fr.free.nrw.commons.upload.structure.depictions.DepictionDao;
|
|
||||||
|
|
||||||
public class DBOpenHelper extends SQLiteOpenHelper {
|
public class DBOpenHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
|
|
@ -29,7 +28,6 @@ public class DBOpenHelper extends SQLiteOpenHelper {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(SQLiteDatabase sqLiteDatabase) {
|
public void onCreate(SQLiteDatabase sqLiteDatabase) {
|
||||||
CategoryDao.Table.onCreate(sqLiteDatabase);
|
CategoryDao.Table.onCreate(sqLiteDatabase);
|
||||||
DepictionDao.Table.onCreate(sqLiteDatabase);
|
|
||||||
BookmarkPicturesDao.Table.onCreate(sqLiteDatabase);
|
BookmarkPicturesDao.Table.onCreate(sqLiteDatabase);
|
||||||
BookmarkLocationsDao.Table.onCreate(sqLiteDatabase);
|
BookmarkLocationsDao.Table.onCreate(sqLiteDatabase);
|
||||||
RecentSearchesDao.Table.onCreate(sqLiteDatabase);
|
RecentSearchesDao.Table.onCreate(sqLiteDatabase);
|
||||||
|
|
@ -38,7 +36,6 @@ public class DBOpenHelper extends SQLiteOpenHelper {
|
||||||
@Override
|
@Override
|
||||||
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int from, int to) {
|
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int from, int to) {
|
||||||
CategoryDao.Table.onUpdate(sqLiteDatabase, from, to);
|
CategoryDao.Table.onUpdate(sqLiteDatabase, from, to);
|
||||||
DepictionDao.Table.onUpdate(sqLiteDatabase, from, to);
|
|
||||||
BookmarkPicturesDao.Table.onUpdate(sqLiteDatabase, from, to);
|
BookmarkPicturesDao.Table.onUpdate(sqLiteDatabase, from, to);
|
||||||
BookmarkLocationsDao.Table.onUpdate(sqLiteDatabase, from, to);
|
BookmarkLocationsDao.Table.onUpdate(sqLiteDatabase, from, to);
|
||||||
RecentSearchesDao.Table.onUpdate(sqLiteDatabase, from, to);
|
RecentSearchesDao.Table.onUpdate(sqLiteDatabase, from, to);
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
||||||
17
app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt
Normal file
17
app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The database for accessing the respective DAOs
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Database(entities = [Contribution::class], version = 1, exportSchema = false)
|
||||||
|
@TypeConverters(Converters::class)
|
||||||
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
abstract fun contributionDao(): ContributionDao
|
||||||
|
}
|
||||||
|
|
@ -55,7 +55,6 @@ public class CommonsApplicationModule {
|
||||||
private Context applicationContext;
|
private Context applicationContext;
|
||||||
public static final String IO_THREAD="io_thread";
|
public static final String IO_THREAD="io_thread";
|
||||||
public static final String MAIN_THREAD="main_thread";
|
public static final String MAIN_THREAD="main_thread";
|
||||||
private AppDatabase appDatabase;
|
|
||||||
|
|
||||||
public CommonsApplicationModule(Context applicationContext) {
|
public CommonsApplicationModule(Context applicationContext) {
|
||||||
this.applicationContext = applicationContext;
|
this.applicationContext = applicationContext;
|
||||||
|
|
@ -247,12 +246,11 @@ public class CommonsApplicationModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
public AppDatabase provideAppDataBase() {
|
public AppDatabase provideAppDataBase() {
|
||||||
appDatabase=Room.databaseBuilder(applicationContext, AppDatabase.class, "commons_room.db").build();
|
return Room.databaseBuilder(applicationContext, AppDatabase.class, "commons_room.db").build();
|
||||||
return appDatabase;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
public ContributionDao providesContributionsDao() {
|
public ContributionDao providesContributionsDao(AppDatabase appDatabase) {
|
||||||
return appDatabase.getContributionDao();
|
return appDatabase.contributionDao();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,30 +6,25 @@ import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsContentProvider;
|
||||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider;
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider;
|
||||||
import fr.free.nrw.commons.category.CategoryContentProvider;
|
import fr.free.nrw.commons.category.CategoryContentProvider;
|
||||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider;
|
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider;
|
||||||
import fr.free.nrw.commons.upload.structure.depictions.DepictsContentProvider;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This Class Represents the Module for dependency injection (using dagger)
|
* This Class Represents the Module for dependency injection (using dagger)
|
||||||
* so, if a developer needs to add a new ContentProvider to the commons app
|
* so, if a developer needs to add a new ContentProvider to the commons app
|
||||||
* then that must be mentioned here to inject the dependencies
|
* then that must be mentioned here to inject the dependencies
|
||||||
*/
|
*/
|
||||||
@Module
|
@Module
|
||||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
@SuppressWarnings({ "WeakerAccess", "unused" })
|
||||||
public abstract class ContentProviderBuilderModule {
|
public abstract class ContentProviderBuilderModule {
|
||||||
|
|
||||||
@ContributesAndroidInjector
|
@ContributesAndroidInjector
|
||||||
abstract CategoryContentProvider bindCategoryContentProvider();
|
abstract CategoryContentProvider bindCategoryContentProvider();
|
||||||
|
|
||||||
@ContributesAndroidInjector
|
@ContributesAndroidInjector
|
||||||
abstract DepictsContentProvider bindDepictsContentProvider();
|
abstract RecentSearchesContentProvider bindRecentSearchesContentProvider();
|
||||||
|
|
||||||
@ContributesAndroidInjector
|
@ContributesAndroidInjector
|
||||||
abstract RecentSearchesContentProvider bindRecentSearchesContentProvider();
|
abstract BookmarkPicturesContentProvider bindBookmarkContentProvider();
|
||||||
|
|
||||||
@ContributesAndroidInjector
|
|
||||||
abstract BookmarkPicturesContentProvider bindBookmarkContentProvider();
|
|
||||||
|
|
||||||
@ContributesAndroidInjector
|
|
||||||
abstract BookmarkLocationsContentProvider bindBookmarkLocationContentProvider();
|
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract BookmarkLocationsContentProvider bindBookmarkLocationContentProvider();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ public class DepictsClient {
|
||||||
String.valueOf(offset)
|
String.valueOf(offset)
|
||||||
)
|
)
|
||||||
.flatMap(depictSearchResponse ->Observable.fromIterable(depictSearchResponse.getSearch()))
|
.flatMap(depictSearchResponse ->Observable.fromIterable(depictSearchResponse.getSearch()))
|
||||||
.map(depictSearchItem -> new DepictedItem(depictSearchItem.getLabel(), depictSearchItem.getDescription(), "", false, depictSearchItem.getId()));
|
.map(DepictedItem::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import fr.free.nrw.commons.upload.UploadModel;
|
||||||
import fr.free.nrw.commons.upload.UploadModel.UploadItem;
|
import fr.free.nrw.commons.upload.UploadModel.UploadItem;
|
||||||
import fr.free.nrw.commons.upload.structure.depictions.DepictModel;
|
import fr.free.nrw.commons.upload.structure.depictions.DepictModel;
|
||||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
|
|
||||||
|
|
@ -230,8 +229,8 @@ public class UploadRemoteDataSource {
|
||||||
* get all depictions
|
* get all depictions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public Observable<DepictedItem> searchAllEntities(String query, List<String> imageTitleList) {
|
public Observable<DepictedItem> searchAllEntities(String query) {
|
||||||
return depictModel.searchAllEntities(query, imageTitleList);
|
return depictModel.searchAllEntities(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectedDepictions(List<String> selectedDepictions) {
|
public void setSelectedDepictions(List<String> selectedDepictions) {
|
||||||
|
|
|
||||||
|
|
@ -287,12 +287,11 @@ public class UploadRepository {
|
||||||
* Search all depictions from
|
* Search all depictions from
|
||||||
*
|
*
|
||||||
* @param query
|
* @param query
|
||||||
* @param imageTitleList
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public Observable<DepictedItem> searchAllEntities(String query, List<String> imageTitleList) {
|
public Observable<DepictedItem> searchAllEntities(String query) {
|
||||||
return remoteDataSource.searchAllEntities(query, imageTitleList);
|
return remoteDataSource.searchAllEntities(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getDepictionsEntityIdList() {
|
public List<String> getDepictionsEntityIdList() {
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@ package fr.free.nrw.commons.upload.depicts;
|
||||||
import static fr.free.nrw.commons.di.CommonsApplicationModule.IO_THREAD;
|
import static fr.free.nrw.commons.di.CommonsApplicationModule.IO_THREAD;
|
||||||
import static fr.free.nrw.commons.di.CommonsApplicationModule.MAIN_THREAD;
|
import static fr.free.nrw.commons.di.CommonsApplicationModule.MAIN_THREAD;
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import fr.free.nrw.commons.explore.depictions.DepictsClient;
|
import fr.free.nrw.commons.explore.depictions.DepictsClient;
|
||||||
import fr.free.nrw.commons.repository.UploadRepository;
|
import fr.free.nrw.commons.repository.UploadRepository;
|
||||||
import fr.free.nrw.commons.upload.UploadModel;
|
import fr.free.nrw.commons.upload.UploadModel;
|
||||||
|
|
@ -85,7 +83,6 @@ public class DepictsPresenter implements DepictsContract.UserActionListener {
|
||||||
@Override
|
@Override
|
||||||
public void searchForDepictions(String query) {
|
public void searchForDepictions(String query) {
|
||||||
List<DepictedItem> depictedItemList = new ArrayList<>();
|
List<DepictedItem> depictedItemList = new ArrayList<>();
|
||||||
List<String> imageTitleList = getImageTitleList();
|
|
||||||
Observable<DepictedItem> distinctDepictsObservable = Observable
|
Observable<DepictedItem> distinctDepictsObservable = Observable
|
||||||
.fromIterable(repository.getSelectedDepictions())
|
.fromIterable(repository.getSelectedDepictions())
|
||||||
.subscribeOn(ioScheduler)
|
.subscribeOn(ioScheduler)
|
||||||
|
|
@ -97,27 +94,22 @@ public class DepictsPresenter implements DepictsContract.UserActionListener {
|
||||||
})
|
})
|
||||||
.observeOn(ioScheduler)
|
.observeOn(ioScheduler)
|
||||||
.concatWith(
|
.concatWith(
|
||||||
repository.searchAllEntities(query, imageTitleList)
|
repository.searchAllEntities(query)
|
||||||
)
|
)
|
||||||
.distinct();
|
.distinct();
|
||||||
|
|
||||||
Disposable searchDepictsDisposable = distinctDepictsObservable
|
Disposable searchDepictsDisposable = distinctDepictsObservable
|
||||||
.observeOn(mainThreadScheduler)
|
.observeOn(mainThreadScheduler)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
s -> depictedItemList.add(s),
|
depictedItemList::add,
|
||||||
Timber::e,
|
Timber::e,
|
||||||
() -> {
|
() -> {
|
||||||
view.showProgress(false);
|
view.showProgress(false);
|
||||||
|
|
||||||
if (null == depictedItemList || depictedItemList.isEmpty()) {
|
if (depictedItemList.isEmpty()) {
|
||||||
view.showError(true);
|
view.showError(true);
|
||||||
} else {
|
} else {
|
||||||
view.showError(false);
|
view.showError(false);
|
||||||
//Understand this is shitty, but yes, doing it the other way is even worse and adapter positions can not be trusted
|
|
||||||
for (int position = 0; position < depictedItemList.size();
|
|
||||||
position++) {
|
|
||||||
//depictedItemList.get(position).setPosition(position);
|
|
||||||
}
|
|
||||||
view.setDepictsList(depictedItemList);
|
view.setDepictsList(depictedItemList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -156,18 +148,4 @@ public class DepictsPresenter implements DepictsContract.UserActionListener {
|
||||||
view.onImageUrlFetched(response,position);
|
view.onImageUrlFetched(response,position);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns image title list from UploadItem
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private List<String> getImageTitleList() {
|
|
||||||
List<String> titleList = new ArrayList<>();
|
|
||||||
for (UploadModel.UploadItem item : repository.getUploads()) {
|
|
||||||
if (item.getTitle().isSet()) {
|
|
||||||
titleList.add(item.getTitle().toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return titleList;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,141 +0,0 @@
|
||||||
package fr.free.nrw.commons.upload.structure.depictions;
|
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
|
|
||||||
import fr.free.nrw.commons.explore.depictions.DepictsClient;
|
|
||||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
|
||||||
import fr.free.nrw.commons.upload.depicts.DepictsInterface;
|
|
||||||
import fr.free.nrw.commons.utils.StringSortingUtils;
|
|
||||||
import fr.free.nrw.commons.wikidata.model.DepictSearchItem;
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The model class for depictions in upload
|
|
||||||
*/
|
|
||||||
public class DepictModel {
|
|
||||||
private static final int SEARCH_DEPICTS_LIMIT = 25;
|
|
||||||
private final DepictionDao depictDao;
|
|
||||||
private final DepictsInterface depictsInterface;
|
|
||||||
private final JsonKvStore directKvStore;
|
|
||||||
@Inject
|
|
||||||
DepictsClient depictsClient;
|
|
||||||
|
|
||||||
private List<DepictedItem> selectedDepictedItems;
|
|
||||||
private HashMap<String, ArrayList<String>> depictsCache;
|
|
||||||
private CompositeDisposable compositeDisposable = new CompositeDisposable();
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public DepictModel(DepictionDao depictDao, @Named("default_preferences") JsonKvStore directKvStore, DepictsInterface depictsInterface) {
|
|
||||||
this.depictDao = depictDao;
|
|
||||||
this.directKvStore = directKvStore;
|
|
||||||
this.depictsInterface = depictsInterface;
|
|
||||||
this.depictsCache = new HashMap<>();
|
|
||||||
this.selectedDepictedItems = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Comparator<DepictedItem> sortBySimilarity(final String filter) {
|
|
||||||
Comparator<String> stringSimilarityComparator = StringSortingUtils.sortBySimilarity(filter);
|
|
||||||
return (firstItem, secondItem) -> stringSimilarityComparator
|
|
||||||
.compare(firstItem.getDepictsLabel(), secondItem.getDescription());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cacheAll(HashMap<String, ArrayList<String>> depictsCache) {
|
|
||||||
depictsCache.putAll(depictsCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashMap<String, ArrayList<String>> getDepictsCache() {
|
|
||||||
return depictsCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean cacheContainsKey(String term) {
|
|
||||||
return depictsCache.containsKey(term);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onDepictItemClicked(DepictedItem depictedItem) {
|
|
||||||
if (depictedItem.isSelected()) {
|
|
||||||
selectDepictItem(depictedItem);
|
|
||||||
// updateDepictCount(depictedItem);
|
|
||||||
} else {
|
|
||||||
unselectDepiction(depictedItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void unselectDepiction(DepictedItem depictedItem) {
|
|
||||||
selectedDepictedItems.remove(depictedItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateDepictCount(DepictedItem depictedItem) {
|
|
||||||
Depiction depiction = depictDao.find(depictedItem.getDepictsLabel());
|
|
||||||
|
|
||||||
if (depictedItem == null) {
|
|
||||||
depiction = new Depiction(null, depictedItem.getDepictsLabel(), new Date(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
depiction.incTimesUsed();
|
|
||||||
depictDao.save(depiction);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void selectDepictItem(DepictedItem depictedItem) {
|
|
||||||
selectedDepictedItems.add(depictedItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Observable<DepictedItem> titleDepicts(List<String> titleList) {
|
|
||||||
return Observable.fromIterable(titleList)
|
|
||||||
.concatMap(this::getTitleDepicts);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Observable<DepictedItem> getTitleDepicts(String title) {
|
|
||||||
return depictsInterface.searchForDepicts(title, String.valueOf(SEARCH_DEPICTS_LIMIT), Locale.getDefault().getLanguage(), Locale.getDefault().getLanguage(),"0")
|
|
||||||
.map(depictSearchResponse -> {
|
|
||||||
DepictSearchItem depictedItem = depictSearchResponse.getSearch().get(0);
|
|
||||||
return new DepictedItem(depictedItem.getLabel(), depictedItem.getDescription(), "", false, depictedItem.getId());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private Observable<DepictedItem> recentDepicts() {
|
|
||||||
return Observable.fromIterable(depictDao.recentDepicts(SEARCH_DEPICTS_LIMIT))
|
|
||||||
.map(s -> new DepictedItem(s, "", "", false, ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get selected Depictions
|
|
||||||
* @return selected depictions
|
|
||||||
*/
|
|
||||||
public List<DepictedItem> getSelectedDepictions() {
|
|
||||||
return selectedDepictedItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search for depictions
|
|
||||||
* @param query
|
|
||||||
* @param imageTitleList
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public Observable<DepictedItem> searchAllEntities(String query, List<String> imageTitleList) {
|
|
||||||
return depictsInterface.searchForDepicts(query, String.valueOf(SEARCH_DEPICTS_LIMIT), Locale.getDefault().getLanguage(), Locale.getDefault().getLanguage(), "0")
|
|
||||||
.flatMap(depictSearchResponse -> Observable.fromIterable(depictSearchResponse.getSearch()))
|
|
||||||
.map(depictSearchItem -> new DepictedItem(depictSearchItem.getLabel(), depictSearchItem.getDescription(), "", false, depictSearchItem.getId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> depictionsEntityIdList() {
|
|
||||||
List<String> output = new ArrayList<>();
|
|
||||||
for (DepictedItem d : selectedDepictedItems) {
|
|
||||||
output.add(d.getEntityId());
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
package fr.free.nrw.commons.upload.structure.depictions
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.upload.depicts.DepictsInterface
|
||||||
|
import io.reactivex.Observable
|
||||||
|
import java.util.Locale
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model class for depictions in upload
|
||||||
|
*/
|
||||||
|
class DepictModel @Inject constructor(private val depictsInterface: DepictsInterface) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val SEARCH_DEPICTS_LIMIT = 25
|
||||||
|
}
|
||||||
|
|
||||||
|
val selectedDepictions = mutableListOf<DepictedItem>()
|
||||||
|
|
||||||
|
fun onDepictItemClicked(depictedItem: DepictedItem) {
|
||||||
|
if (depictedItem.isSelected) {
|
||||||
|
selectDepictItem(depictedItem)
|
||||||
|
} else {
|
||||||
|
unselectDepiction(depictedItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unselectDepiction(depictedItem: DepictedItem) {
|
||||||
|
selectedDepictions.remove(depictedItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun selectDepictItem(depictedItem: DepictedItem) {
|
||||||
|
selectedDepictions.add(depictedItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for depictions
|
||||||
|
*/
|
||||||
|
fun searchAllEntities(query: String?): Observable<DepictedItem> {
|
||||||
|
return depictsInterface.searchForDepicts(
|
||||||
|
query, "$SEARCH_DEPICTS_LIMIT", Locale.getDefault().language,
|
||||||
|
Locale.getDefault().language, "0"
|
||||||
|
)
|
||||||
|
.flatMap { Observable.fromIterable(it.search) }
|
||||||
|
.map(::DepictedItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun depictionsEntityIdList() = selectedDepictions.map { it.entityId }
|
||||||
|
}
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
package fr.free.nrw.commons.upload.structure.depictions;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Model class for Depicted Item in Upload and Explore
|
|
||||||
*/
|
|
||||||
public class DepictedItem {
|
|
||||||
private final String depictsLabel;
|
|
||||||
private final String description;
|
|
||||||
private String imageUrl;
|
|
||||||
private boolean selected;
|
|
||||||
private String entityId;
|
|
||||||
private int position;
|
|
||||||
|
|
||||||
public DepictedItem(String depictsLabel, String description, String imageUrl, boolean selected, String entityId) {
|
|
||||||
this.depictsLabel = depictsLabel;
|
|
||||||
this.selected = selected;
|
|
||||||
this.description = description;
|
|
||||||
this.imageUrl = imageUrl;
|
|
||||||
this.entityId = entityId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEntityId() {
|
|
||||||
return entityId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDescription() {
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getImageUrl() {
|
|
||||||
return imageUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setImageUrl(String imageUrl) {
|
|
||||||
this.imageUrl = imageUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDepictsLabel() {
|
|
||||||
return depictsLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSelected() {
|
|
||||||
return selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSelected(boolean selected) {
|
|
||||||
this.selected = selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return super.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return super.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPosition(int position) {
|
|
||||||
this.position = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPosition() {
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (o == null || getClass() != o.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DepictedItem that = (DepictedItem) o;
|
|
||||||
|
|
||||||
return depictsLabel.equals(that.depictsLabel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package fr.free.nrw.commons.upload.structure.depictions
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.wikidata.model.DepictSearchItem
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model class for Depicted Item in Upload and Explore
|
||||||
|
*/
|
||||||
|
data class DepictedItem constructor(
|
||||||
|
val depictsLabel: String,
|
||||||
|
val description: String,
|
||||||
|
var imageUrl: String,
|
||||||
|
var isSelected: Boolean,
|
||||||
|
val entityId: String
|
||||||
|
) {
|
||||||
|
constructor(depictSearchItem: DepictSearchItem) : this(
|
||||||
|
depictSearchItem.label,
|
||||||
|
depictSearchItem.description,
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
depictSearchItem.id
|
||||||
|
)
|
||||||
|
|
||||||
|
var position = 0
|
||||||
|
|
||||||
|
override fun equals(o: Any?) = when {
|
||||||
|
this === o -> true
|
||||||
|
o is DepictedItem -> depictsLabel == o.depictsLabel
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,106 +0,0 @@
|
||||||
package fr.free.nrw.commons.upload.structure.depictions;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the fact that a given Commons picture depicts a given Wikidata item.
|
|
||||||
* Example: https://commons.wikimedia.org/wiki/File:Sorting_quicksort_anim.gif depicts https://www.wikidata.org/wiki/Q486598
|
|
||||||
*/
|
|
||||||
public class Depiction {
|
|
||||||
private Uri contentUri;
|
|
||||||
private String name;
|
|
||||||
private Date lastUsed;
|
|
||||||
private int timesUsed;
|
|
||||||
|
|
||||||
public Depiction() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Depiction(Uri contentUri, String name, Date lastUsed, int timesUsed) {
|
|
||||||
this.contentUri = contentUri;
|
|
||||||
this.name = name;
|
|
||||||
this.lastUsed = lastUsed;
|
|
||||||
this.timesUsed = timesUsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the content URI for this category
|
|
||||||
*
|
|
||||||
* @return content URI
|
|
||||||
*/
|
|
||||||
public Uri getContentUri() {
|
|
||||||
return contentUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Modifies the content URI - marking this depiction as already saved in the database
|
|
||||||
*
|
|
||||||
* @param contentUri the content URI
|
|
||||||
*/
|
|
||||||
public void setContentUri(Uri contentUri) {
|
|
||||||
this.contentUri = contentUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets name
|
|
||||||
*
|
|
||||||
* @return name
|
|
||||||
*/
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Modifies name
|
|
||||||
*
|
|
||||||
* @param name Depicts name
|
|
||||||
*/
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets last used date
|
|
||||||
*
|
|
||||||
* @return Last used date
|
|
||||||
*/
|
|
||||||
public Date getLastUsed() {
|
|
||||||
return lastUsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set last used date
|
|
||||||
*
|
|
||||||
* @param lastUsed last used date of depiction
|
|
||||||
*/
|
|
||||||
|
|
||||||
public void setLastUsed(Date lastUsed) {
|
|
||||||
this.lastUsed = lastUsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets no. of times the depiction is used
|
|
||||||
*
|
|
||||||
* @return no. of times used
|
|
||||||
*/
|
|
||||||
public int getTimesUsed() {
|
|
||||||
return timesUsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increments timesUsed by 1 and sets last used date as now.
|
|
||||||
*/
|
|
||||||
public void incTimesUsed() {
|
|
||||||
timesUsed++;
|
|
||||||
touch();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates new last used date
|
|
||||||
*/
|
|
||||||
private void touch() {
|
|
||||||
lastUsed = new Date();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,176 +0,0 @@
|
||||||
package fr.free.nrw.commons.upload.structure.depictions;
|
|
||||||
|
|
||||||
import android.content.ContentProviderClient;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Provider;
|
|
||||||
|
|
||||||
public class DepictionDao {
|
|
||||||
|
|
||||||
private final Provider<ContentProviderClient> clientProvider;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public DepictionDao(@Named("depictions") Provider<ContentProviderClient> clientProvider) {
|
|
||||||
this.clientProvider = clientProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save(Depiction depiction) {
|
|
||||||
ContentProviderClient db = clientProvider.get();
|
|
||||||
try {
|
|
||||||
if (depiction.getContentUri() == null) {
|
|
||||||
depiction.setContentUri(db.insert(DepictsContentProvider.BASE_URI, toContentValues(depiction)));
|
|
||||||
} else {
|
|
||||||
db.update(depiction.getContentUri(), toContentValues(depiction), null, null);
|
|
||||||
}
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
db.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find persisted depicts in database, based on its name.
|
|
||||||
*
|
|
||||||
* @param name Depiction name
|
|
||||||
* @return depiction from database, or null if not found
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
Depiction find(String name) {
|
|
||||||
Cursor cursor = null;
|
|
||||||
ContentProviderClient db = clientProvider.get();
|
|
||||||
try {
|
|
||||||
cursor = db.query(
|
|
||||||
DepictsContentProvider.BASE_URI,
|
|
||||||
Table.ALL_FIELDS,
|
|
||||||
Table.COLUMN_NAME + "=?",
|
|
||||||
new String[]{name},
|
|
||||||
null);
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
|
||||||
return fromCursor(cursor);
|
|
||||||
}
|
|
||||||
db.release();
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
// This feels lazy, but to hell with checked exceptions. :)
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
if (cursor != null) {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve recently-used depictions, ordered by descending date.
|
|
||||||
*
|
|
||||||
* @return a list containing recent depicts
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
List<String> recentDepicts(int limit) {
|
|
||||||
List<String> items = new ArrayList<>();
|
|
||||||
Cursor cursor = null;
|
|
||||||
ContentProviderClient db = clientProvider.get();
|
|
||||||
try {
|
|
||||||
cursor = db.query(
|
|
||||||
DepictsContentProvider.BASE_URI,
|
|
||||||
Table.ALL_FIELDS,
|
|
||||||
null,
|
|
||||||
new String[]{},
|
|
||||||
Table.COLUMN_LAST_USED + "DESC");
|
|
||||||
while (cursor != null && cursor.moveToNext()
|
|
||||||
&& cursor.getPosition() < limit) {
|
|
||||||
items.add(fromCursor(cursor).getName());
|
|
||||||
}
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
if (cursor != null) {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
db.release();
|
|
||||||
}
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
Depiction fromCursor(Cursor cursor) {
|
|
||||||
// Hardcoding column positions!
|
|
||||||
return new Depiction(
|
|
||||||
DepictsContentProvider.uriForId(cursor.getInt(cursor.getColumnIndex(DepictionDao.Table.COLUMN_ID))),
|
|
||||||
cursor.getString(cursor.getColumnIndex(DepictionDao.Table.COLUMN_NAME)),
|
|
||||||
new Date(cursor.getLong(cursor.getColumnIndex(DepictionDao.Table.COLUMN_LAST_USED))),
|
|
||||||
cursor.getInt(cursor.getColumnIndex(DepictionDao.Table.COLUMN_TIMES_USED))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ContentValues toContentValues(Depiction depiction) {
|
|
||||||
ContentValues cv = new ContentValues();
|
|
||||||
cv.put(DepictionDao.Table.COLUMN_NAME, depiction.getName());
|
|
||||||
cv.put(DepictionDao.Table.COLUMN_LAST_USED, depiction.getLastUsed().getTime());
|
|
||||||
cv.put(DepictionDao.Table.COLUMN_TIMES_USED, depiction.getTimesUsed());
|
|
||||||
return cv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example Table: TABLE_NAME: depictions
|
|
||||||
* COLUMN_ID: unique id for the column
|
|
||||||
* COLUMN_NAME: depiction name
|
|
||||||
* COLUMN_LAST_USED: Time stamp for the previous usage of the depiction
|
|
||||||
* COLUMN_TIMES_USED: Number of times the depiction was used previously
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static class Table {
|
|
||||||
public static final String TABLE_NAME = "depictions";
|
|
||||||
public static final String COLUMN_ID = "_id";
|
|
||||||
static final String COLUMN_NAME = "name";
|
|
||||||
static final String COLUMN_LAST_USED = "last_used";
|
|
||||||
static final String COLUMN_TIMES_USED = "times_used";
|
|
||||||
|
|
||||||
|
|
||||||
// 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_NAME,
|
|
||||||
COLUMN_LAST_USED,
|
|
||||||
COLUMN_TIMES_USED
|
|
||||||
};
|
|
||||||
|
|
||||||
static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME;
|
|
||||||
|
|
||||||
static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
|
|
||||||
+ COLUMN_ID + " INTEGER PRIMARY KEY,"
|
|
||||||
+ COLUMN_NAME + " STRING,"
|
|
||||||
+ COLUMN_LAST_USED + " INTEGER,"
|
|
||||||
+ COLUMN_TIMES_USED + " INTEGER"
|
|
||||||
+ ");";
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,153 +0,0 @@
|
||||||
package fr.free.nrw.commons.upload.structure.depictions;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
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 org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
|
||||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
import static fr.free.nrw.commons.BuildConfig.DEPICTION_AUTHORITY;
|
|
||||||
import static fr.free.nrw.commons.upload.structure.depictions.DepictionDao.Table.ALL_FIELDS;
|
|
||||||
import static fr.free.nrw.commons.upload.structure.depictions.DepictionDao.Table.COLUMN_ID;
|
|
||||||
import static fr.free.nrw.commons.upload.structure.depictions.DepictionDao.Table.TABLE_NAME;
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("Registered")
|
|
||||||
public class DepictsContentProvider extends CommonsDaggerContentProvider {
|
|
||||||
|
|
||||||
private static final int DEPICTS = 1;
|
|
||||||
private static final int DEPICTS_ID = 2;
|
|
||||||
private static final String BASE_PATH = "depictions";
|
|
||||||
public static final Uri BASE_URI = Uri.parse("content://" + DEPICTION_AUTHORITY + "/" + BASE_PATH);
|
|
||||||
|
|
||||||
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
|
||||||
|
|
||||||
static {
|
|
||||||
uriMatcher.addURI(DEPICTION_AUTHORITY, BASE_PATH, DEPICTS);
|
|
||||||
uriMatcher.addURI(DEPICTION_AUTHORITY, BASE_PATH + "/#", DEPICTS_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
DBOpenHelper dbOpenHelper;
|
|
||||||
|
|
||||||
public static Uri uriForId(int id) {
|
|
||||||
return Uri.parse(BASE_URI.toString() + "/" + id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cursor query(@NotNull 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 DEPICTS:
|
|
||||||
cursor = queryBuilder.query(db, projection, selection, selectionArgs,
|
|
||||||
null, null, sortOrder);
|
|
||||||
break;
|
|
||||||
case DEPICTS_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(@NotNull Uri uri) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri insert(@NotNull Uri uri, ContentValues values) {
|
|
||||||
int uriType = uriMatcher.match(uri);
|
|
||||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
|
||||||
long id;
|
|
||||||
switch (uriType) {
|
|
||||||
case DEPICTS:
|
|
||||||
id = sqlDB.insert(TABLE_NAME, null, values);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unknown URI: " + uri);
|
|
||||||
}
|
|
||||||
getContext().getContentResolver().notifyChange(uri, null);
|
|
||||||
return Uri.parse(BASE_URI + "/" + id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int delete(@NotNull Uri uri, String selection, String[] selectionArgs) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int bulkInsert(@NotNull Uri uri, @NotNull ContentValues[] values) {
|
|
||||||
int uriType = uriMatcher.match(uri);
|
|
||||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
|
||||||
sqlDB.beginTransaction();
|
|
||||||
switch (uriType) {
|
|
||||||
case DEPICTS:
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int update(@NotNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
|
||||||
int uriType = uriMatcher.match(uri);
|
|
||||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
|
||||||
int rowsUpdated;
|
|
||||||
switch (uriType) {
|
|
||||||
case DEPICTS:
|
|
||||||
if (TextUtils.isEmpty(selection)) {
|
|
||||||
int id = Integer.valueOf(uri.getLastPathSegment());
|
|
||||||
rowsUpdated = sqlDB.update(TABLE_NAME,
|
|
||||||
values,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,7 +2,6 @@ package fr.free.nrw.commons.upload
|
||||||
|
|
||||||
//import com.nhaarman.mockito_kotlin.verify
|
//import com.nhaarman.mockito_kotlin.verify
|
||||||
import com.nhaarman.mockitokotlin2.whenever
|
import com.nhaarman.mockitokotlin2.whenever
|
||||||
import org.mockito.Mockito.verify
|
|
||||||
import fr.free.nrw.commons.category.CategoryItem
|
import fr.free.nrw.commons.category.CategoryItem
|
||||||
import fr.free.nrw.commons.explore.depictions.DepictsClient
|
import fr.free.nrw.commons.explore.depictions.DepictsClient
|
||||||
import fr.free.nrw.commons.repository.UploadRepository
|
import fr.free.nrw.commons.repository.UploadRepository
|
||||||
|
|
@ -16,7 +15,7 @@ import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.mockito.ArgumentMatchers
|
import org.mockito.ArgumentMatchers
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito
|
import org.mockito.Mockito.verify
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
class DepictsPresenterTest {
|
class DepictsPresenterTest {
|
||||||
|
|
@ -50,7 +49,7 @@ class DepictsPresenterTest {
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
testScheduler = TestScheduler()
|
testScheduler = TestScheduler()
|
||||||
depictedItem = DepictedItem("label", "desc", null, false, "entityId")
|
depictedItem = DepictedItem("label", "desc", "", false, "entityId")
|
||||||
depictedItems.add(depictedItem)
|
depictedItems.add(depictedItem)
|
||||||
testObservable = Observable.just(depictedItem)
|
testObservable = Observable.just(depictedItem)
|
||||||
depictsPresenter = DepictsPresenter(repository, testScheduler, testScheduler, depictsClient)
|
depictsPresenter = DepictsPresenter(repository, testScheduler, testScheduler, depictsClient)
|
||||||
|
|
@ -62,7 +61,7 @@ class DepictsPresenterTest {
|
||||||
fun searchEnglishDepictionsTest() {
|
fun searchEnglishDepictionsTest() {
|
||||||
whenever(repository?.sortBySimilarity(ArgumentMatchers.anyString())).thenReturn(Comparator<CategoryItem> { _, _ -> 1 })
|
whenever(repository?.sortBySimilarity(ArgumentMatchers.anyString())).thenReturn(Comparator<CategoryItem> { _, _ -> 1 })
|
||||||
whenever(repository?.selectedDepictions).thenReturn(depictedItems)
|
whenever(repository?.selectedDepictions).thenReturn(depictedItems)
|
||||||
whenever(repository?.searchAllEntities(ArgumentMatchers.anyString(), ArgumentMatchers.anyList())).thenReturn(Observable.empty())
|
whenever(repository?.searchAllEntities(ArgumentMatchers.anyString())).thenReturn(Observable.empty())
|
||||||
depictsPresenter?.searchForDepictions("test")
|
depictsPresenter?.searchForDepictions("test")
|
||||||
verify(view)?.showProgress(true)
|
verify(view)?.showProgress(true)
|
||||||
verify(view)?.showError(true)
|
verify(view)?.showError(true)
|
||||||
|
|
@ -75,7 +74,7 @@ class DepictsPresenterTest {
|
||||||
fun searchOtherLanguageDepictions() {
|
fun searchOtherLanguageDepictions() {
|
||||||
whenever(repository?.sortBySimilarity(ArgumentMatchers.anyString())).thenReturn(Comparator<CategoryItem> { _, _ -> 1 })
|
whenever(repository?.sortBySimilarity(ArgumentMatchers.anyString())).thenReturn(Comparator<CategoryItem> { _, _ -> 1 })
|
||||||
whenever(repository?.selectedDepictions).thenReturn(depictedItems)
|
whenever(repository?.selectedDepictions).thenReturn(depictedItems)
|
||||||
whenever(repository?.searchAllEntities(ArgumentMatchers.anyString(), ArgumentMatchers.anyList())).thenReturn(Observable.empty())
|
whenever(repository?.searchAllEntities(ArgumentMatchers.anyString())).thenReturn(Observable.empty())
|
||||||
depictsPresenter?.searchForDepictions("वी")
|
depictsPresenter?.searchForDepictions("वी")
|
||||||
verify(view)?.showProgress(true)
|
verify(view)?.showProgress(true)
|
||||||
verify(view)?.showError(true)
|
verify(view)?.showError(true)
|
||||||
|
|
@ -88,7 +87,7 @@ class DepictsPresenterTest {
|
||||||
fun searchForNonExistingDepictions() {
|
fun searchForNonExistingDepictions() {
|
||||||
whenever(repository?.sortBySimilarity(ArgumentMatchers.anyString())).thenReturn(Comparator<CategoryItem> { _, _ -> 1 })
|
whenever(repository?.sortBySimilarity(ArgumentMatchers.anyString())).thenReturn(Comparator<CategoryItem> { _, _ -> 1 })
|
||||||
whenever(repository?.selectedDepictions).thenReturn(depictedItems)
|
whenever(repository?.selectedDepictions).thenReturn(depictedItems)
|
||||||
whenever(repository?.searchAllEntities(ArgumentMatchers.anyString(), ArgumentMatchers.anyList())).thenReturn(Observable.empty())
|
whenever(repository?.searchAllEntities(ArgumentMatchers.anyString())).thenReturn(Observable.empty())
|
||||||
depictsPresenter?.searchForDepictions("******")
|
depictsPresenter?.searchForDepictions("******")
|
||||||
verify(view)?.showProgress(true)
|
verify(view)?.showProgress(true)
|
||||||
verify(view)?.setDepictsList(null)
|
verify(view)?.setDepictsList(null)
|
||||||
|
|
@ -101,7 +100,7 @@ class DepictsPresenterTest {
|
||||||
fun setSingleDepiction() {
|
fun setSingleDepiction() {
|
||||||
whenever(repository?.sortBySimilarity(ArgumentMatchers.anyString())).thenReturn(Comparator<CategoryItem> { _, _ -> 1 })
|
whenever(repository?.sortBySimilarity(ArgumentMatchers.anyString())).thenReturn(Comparator<CategoryItem> { _, _ -> 1 })
|
||||||
whenever(repository?.selectedDepictions).thenReturn(depictedItems)
|
whenever(repository?.selectedDepictions).thenReturn(depictedItems)
|
||||||
whenever(repository?.searchAllEntities(ArgumentMatchers.anyString(), ArgumentMatchers.anyList())).thenReturn(Observable.empty())
|
whenever(repository?.searchAllEntities(ArgumentMatchers.anyString())).thenReturn(Observable.empty())
|
||||||
depictsPresenter?.onDepictItemClicked(depictedItem)
|
depictsPresenter?.onDepictItemClicked(depictedItem)
|
||||||
depictsPresenter?.verifyDepictions()
|
depictsPresenter?.verifyDepictions()
|
||||||
verify(view)?.goToNextScreen()
|
verify(view)?.goToNextScreen()
|
||||||
|
|
@ -111,9 +110,9 @@ class DepictsPresenterTest {
|
||||||
fun setMultipleDepictions() {
|
fun setMultipleDepictions() {
|
||||||
whenever(repository?.sortBySimilarity(ArgumentMatchers.anyString())).thenReturn(Comparator<CategoryItem> { _, _ -> 1 })
|
whenever(repository?.sortBySimilarity(ArgumentMatchers.anyString())).thenReturn(Comparator<CategoryItem> { _, _ -> 1 })
|
||||||
whenever(repository?.selectedDepictions).thenReturn(depictedItems)
|
whenever(repository?.selectedDepictions).thenReturn(depictedItems)
|
||||||
whenever(repository?.searchAllEntities(ArgumentMatchers.anyString(), ArgumentMatchers.anyList())).thenReturn(Observable.empty())
|
whenever(repository?.searchAllEntities(ArgumentMatchers.anyString())).thenReturn(Observable.empty())
|
||||||
depictsPresenter?.onDepictItemClicked(depictedItem)
|
depictsPresenter?.onDepictItemClicked(depictedItem)
|
||||||
val depictedItem2 = DepictedItem("label2", "desc2", null, false, "entityid2")
|
val depictedItem2 = DepictedItem("label2", "desc2", "", false, "entityid2")
|
||||||
depictsPresenter?.onDepictItemClicked(depictedItem2)
|
depictsPresenter?.onDepictItemClicked(depictedItem2)
|
||||||
depictsPresenter?.verifyDepictions()
|
depictsPresenter?.verifyDepictions()
|
||||||
verify(view)?.goToNextScreen()
|
verify(view)?.goToNextScreen()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue