mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
In depictions selection screen, suggest recently selected items (#4361)
* implement in depictions selection screen to suggest recently selected items *use RoomDataBase * Add Javadoc * fix an bug * minar change and remove extra line of code * minar changes * improve implemention strategy * fix unittest * Add javadoc * added javadoc
This commit is contained in:
parent
18cfc89fa4
commit
4fa18e5e27
10 changed files with 196 additions and 16 deletions
|
|
@ -5,13 +5,16 @@ import androidx.room.RoomDatabase
|
|||
import androidx.room.TypeConverters
|
||||
import fr.free.nrw.commons.contributions.Contribution
|
||||
import fr.free.nrw.commons.contributions.ContributionDao
|
||||
import fr.free.nrw.commons.upload.depicts.Depicts
|
||||
import fr.free.nrw.commons.upload.depicts.DepictsDao
|
||||
|
||||
/**
|
||||
* The database for accessing the respective DAOs
|
||||
*
|
||||
*/
|
||||
@Database(entities = [Contribution::class], version = 7, exportSchema = false)
|
||||
@Database(entities = [Contribution::class,Depicts::class], version = 7, exportSchema = false)
|
||||
@TypeConverters(Converters::class)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun contributionDao(): ContributionDao
|
||||
abstract fun DepictsDao (): DepictsDao;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,27 @@ public class Converters {
|
|||
return ApplicationlessInjection.getInstance(CommonsApplication.getInstance()).getCommonsApplicationComponent().gson();
|
||||
}
|
||||
|
||||
/**
|
||||
* convert DepictedItem object to string
|
||||
* input Example -> DepictedItem depictedItem=new DepictedItem ()
|
||||
* output Example -> string
|
||||
*/
|
||||
@TypeConverter
|
||||
public static String depictsItemToString(DepictedItem objects) {
|
||||
return writeObjectToString(objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* convert string to DepictedItem object
|
||||
* output Example -> DepictedItem depictedItem=new DepictedItem ()
|
||||
* input Example -> string
|
||||
*/
|
||||
@TypeConverter
|
||||
public static DepictedItem stringToDepicts(String objectList) {
|
||||
return readObjectWithTypeToken(objectList, new TypeToken<DepictedItem>() {
|
||||
});
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static Date fromTimestamp(Long value) {
|
||||
return value == null ? null : new Date(value);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import fr.free.nrw.commons.kvstore.JsonKvStore;
|
|||
import fr.free.nrw.commons.location.LocationServiceManager;
|
||||
import fr.free.nrw.commons.settings.Prefs;
|
||||
import fr.free.nrw.commons.upload.UploadController;
|
||||
import fr.free.nrw.commons.upload.depicts.DepictsDao;
|
||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||
import fr.free.nrw.commons.wikidata.WikidataEditListener;
|
||||
import fr.free.nrw.commons.wikidata.WikidataEditListenerImpl;
|
||||
|
|
@ -242,6 +243,14 @@ public class CommonsApplicationModule {
|
|||
return appDatabase.contributionDao();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reference of DepictsDao class
|
||||
*/
|
||||
@Provides
|
||||
public DepictsDao providesDepictDao(AppDatabase appDatabase) {
|
||||
return appDatabase.DepictsDao();
|
||||
}
|
||||
|
||||
@Provides
|
||||
public ContentResolver providesContentResolver(Context context){
|
||||
return context.getContentResolver();
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import fr.free.nrw.commons.filepicker.UploadableFile;
|
|||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.nearby.Place;
|
||||
import fr.free.nrw.commons.settings.Prefs;
|
||||
import fr.free.nrw.commons.upload.depicts.DepictsFragment;
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package fr.free.nrw.commons.upload.depicts
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* entity class for DepictsRoomDateBase
|
||||
*/
|
||||
@Entity(tableName = "depicts_table")
|
||||
data class Depicts (@PrimaryKey val item: DepictedItem, val lastUsed:Date)
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
package fr.free.nrw.commons.upload.depicts
|
||||
|
||||
import androidx.room.*
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Dao class for DepictsRoomDataBase
|
||||
*/
|
||||
@Dao
|
||||
abstract class DepictsDao {
|
||||
|
||||
/**
|
||||
* insert Depicts in DepictsRoomDataBase
|
||||
*/
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insert(depictedItem: Depicts)
|
||||
|
||||
/**
|
||||
* get all Depicts from roomdatabase
|
||||
*/
|
||||
@Query("Select * From depicts_table order by lastUsed DESC")
|
||||
abstract suspend fun getAllDepict(): List<Depicts>
|
||||
|
||||
/**
|
||||
* get all Depicts which need to delete from roomdatabase
|
||||
*/
|
||||
@Query("Select * From depicts_table order by lastUsed DESC LIMIT :n OFFSET 10")
|
||||
abstract suspend fun getItemToDelete(n: Int): List<Depicts>
|
||||
|
||||
/**
|
||||
* Delete Depicts from roomdatabase
|
||||
*/
|
||||
@Delete
|
||||
abstract suspend fun delete(depicts: Depicts)
|
||||
|
||||
lateinit var allDepict: List<Depicts>
|
||||
lateinit var listOfDelete: List<Depicts>
|
||||
|
||||
/**
|
||||
* get all depicts from DepictsRoomDatabase
|
||||
*/
|
||||
fun depictsList(): List<Depicts> {
|
||||
runBlocking {
|
||||
launch(Dispatchers.IO) {
|
||||
allDepict = getAllDepict()
|
||||
}
|
||||
}
|
||||
return allDepict
|
||||
}
|
||||
|
||||
/**
|
||||
* insert Depicts in DepictsRoomDataBase
|
||||
*/
|
||||
fun insertDepict(depictes: Depicts) {
|
||||
runBlocking {
|
||||
launch(Dispatchers.IO) {
|
||||
insert(depictes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get all Depicts item which need to delete
|
||||
*/
|
||||
fun getItemTodelete(number: Int): List<Depicts> {
|
||||
runBlocking {
|
||||
launch(Dispatchers.IO) {
|
||||
listOfDelete = getItemToDelete(number)
|
||||
}
|
||||
}
|
||||
return listOfDelete
|
||||
}
|
||||
|
||||
/**
|
||||
* delete Depicts in DepictsRoomDataBase
|
||||
*/
|
||||
fun deleteDepicts(depictes: Depicts) {
|
||||
runBlocking {
|
||||
launch(Dispatchers.IO) {
|
||||
delete(depictes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* save Depicts in DepictsRoomDataBase
|
||||
*/
|
||||
fun savingDepictsInRoomDataBase(listDepictedItem: List<DepictedItem>) {
|
||||
var numberofItemInRoomDataBase: Int
|
||||
val maxNumberOfItemSaveInRoom = 10
|
||||
|
||||
for (depictsItem in listDepictedItem) {
|
||||
depictsItem.isSelected = false
|
||||
insertDepict(Depicts(depictsItem, Date()))
|
||||
}
|
||||
|
||||
numberofItemInRoomDataBase = depictsList().size
|
||||
// delete the depictItem from depictsroomdataBase when number of element in depictsroomdataBase is greater than 10
|
||||
if (numberofItemInRoomDataBase > maxNumberOfItemSaveInRoom) {
|
||||
|
||||
val listOfDepictsToDelete: List<Depicts> =
|
||||
getItemTodelete(numberofItemInRoomDataBase)
|
||||
for (i in listOfDepictsToDelete) {
|
||||
deleteDepicts(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,10 +21,12 @@ import com.jakewharton.rxbinding2.view.RxView;
|
|||
import com.jakewharton.rxbinding2.widget.RxTextView;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.upload.UploadBaseFragment;
|
||||
import fr.free.nrw.commons.upload.UploadModel;
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||
import fr.free.nrw.commons.utils.DialogUtil;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Inject;
|
||||
|
|
@ -54,7 +56,6 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
|
|||
DepictsContract.UserActionListener presenter;
|
||||
private UploadDepictsAdapter adapter;
|
||||
private Disposable subscribe;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public android.view.View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
|
|
@ -174,8 +175,7 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
|
|||
*
|
||||
* @param query query string
|
||||
*/
|
||||
private void searchForDepictions(String query) {
|
||||
private void searchForDepictions(final String query) {
|
||||
presenter.searchForDepictions(query);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import io.reactivex.disposables.CompositeDisposable
|
|||
import io.reactivex.processors.PublishProcessor
|
||||
import timber.log.Timber
|
||||
import java.lang.reflect.Proxy
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
import javax.inject.Singleton
|
||||
|
|
@ -34,6 +35,8 @@ class DepictsPresenter @Inject constructor(
|
|||
private val compositeDisposable: CompositeDisposable = CompositeDisposable()
|
||||
private val searchTerm: PublishProcessor<String> = PublishProcessor.create()
|
||||
private val depictedItems: MutableLiveData<List<DepictedItem>> = MutableLiveData()
|
||||
@Inject
|
||||
lateinit var depictsDao: DepictsDao;
|
||||
|
||||
override fun onAttachView(view: DepictsContract.View) {
|
||||
this.view = view
|
||||
|
|
@ -62,10 +65,15 @@ class DepictsPresenter @Inject constructor(
|
|||
return searchResults(term).map { Pair(it, term) }
|
||||
}
|
||||
|
||||
private fun searchResults(it: String): Flowable<List<DepictedItem>> {
|
||||
return repository.searchAllEntities(it)
|
||||
private fun searchResults(querystring: String): Flowable<List<DepictedItem>> {
|
||||
var recentDepictedItemList: MutableList<DepictedItem> = ArrayList();
|
||||
//show recentDepictedItemList when queryString is empty
|
||||
if (querystring.isEmpty()) {
|
||||
recentDepictedItemList = getRecentDepictedItems();
|
||||
}
|
||||
return repository.searchAllEntities(querystring)
|
||||
.subscribeOn(ioScheduler)
|
||||
.map { repository.selectedDepictions + it }
|
||||
.map { repository.selectedDepictions + it + recentDepictedItemList }
|
||||
.map { it.filterNot { item -> WikidataDisambiguationItems.isDisambiguationItem(item.instanceOfs) } }
|
||||
.map { it.distinctBy(DepictedItem::id) }
|
||||
}
|
||||
|
|
@ -102,11 +110,28 @@ class DepictsPresenter @Inject constructor(
|
|||
*/
|
||||
override fun verifyDepictions() {
|
||||
if (repository.selectedDepictions.isNotEmpty()) {
|
||||
if (::depictsDao.isInitialized) {
|
||||
//save all the selected Depicted item in room Database
|
||||
depictsDao.savingDepictsInRoomDataBase(repository.selectedDepictions)
|
||||
}
|
||||
view.goToNextScreen()
|
||||
} else {
|
||||
view.noDepictionSelected()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the depicts from DepictsRoomdataBase
|
||||
*/
|
||||
fun getRecentDepictedItems(): MutableList<DepictedItem> {
|
||||
val depictedItemList: MutableList<DepictedItem> = ArrayList()
|
||||
val depictsList = depictsDao.depictsList()
|
||||
for (i in depictsList.indices) {
|
||||
val depictedItem = depictsList[i].item
|
||||
depictedItemList.add(depictedItem)
|
||||
}
|
||||
return depictedItemList
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package fr.free.nrw.commons.upload.structure.depictions
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import fr.free.nrw.commons.nearby.Place
|
||||
import fr.free.nrw.commons.upload.WikidataItem
|
||||
import fr.free.nrw.commons.wikidata.WikidataProperties
|
||||
|
|
@ -20,6 +22,7 @@ const val THUMB_IMAGE_SIZE = "70px"
|
|||
* Model class for Depicted Item in Upload and Explore
|
||||
*/
|
||||
@Parcelize
|
||||
@Entity
|
||||
data class DepictedItem constructor(
|
||||
override val name: String,
|
||||
val description: String?,
|
||||
|
|
@ -27,7 +30,7 @@ data class DepictedItem constructor(
|
|||
val instanceOfs: List<String>,
|
||||
val commonsCategories: List<String>,
|
||||
var isSelected: Boolean,
|
||||
override val id: String
|
||||
@PrimaryKey override val id: String
|
||||
) : WikidataItem, Parcelable {
|
||||
|
||||
constructor(entity: Entities.Entity) : this(
|
||||
|
|
|
|||
|
|
@ -72,10 +72,7 @@ class DepictsPresenterTest {
|
|||
depictsPresenter.searchForDepictions("")
|
||||
testScheduler.triggerActions()
|
||||
verify(view).showProgress(false)
|
||||
verify(view).showError(false)
|
||||
depictsPresenter.depictedItems
|
||||
.test()
|
||||
.assertValue(listOf(selectedItem, depictedItem(id="nonUnique")))
|
||||
verify(view).showError(true)
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -85,10 +82,7 @@ class DepictsPresenterTest {
|
|||
depictsPresenter.searchForDepictions("")
|
||||
testScheduler.triggerActions()
|
||||
verify(view).showProgress(false)
|
||||
verify(view).showError(false)
|
||||
depictsPresenter.depictedItems
|
||||
.test()
|
||||
.assertValue(emptyList())
|
||||
verify(view).showError(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue