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:
Prince kushwaha 2021-05-31 15:49:55 +05:30 committed by GitHub
parent 18cfc89fa4
commit 4fa18e5e27
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 196 additions and 16 deletions

View file

@ -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;
}

View file

@ -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);

View file

@ -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();

View file

@ -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;

View file

@ -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)

View file

@ -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)
}
}
}
}

View file

@ -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);
}
}

View file

@ -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
}
}
/**

View file

@ -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(

View file

@ -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