diff --git a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.kt b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.kt index 9ed19d686..b8a26a12f 100644 --- a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.kt +++ b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.kt @@ -253,7 +253,6 @@ class CommonsApplication : MultiDexApplication() { Timber.e(e) } BookmarkPicturesDao.Table.onDelete(db) - BookmarkLocationsDao.Table.onDelete(db) BookmarkItemsDao.Table.onDelete(db) } diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsContentProvider.kt b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsContentProvider.kt deleted file mode 100644 index e63325f2d..000000000 --- a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsContentProvider.kt +++ /dev/null @@ -1,126 +0,0 @@ -package fr.free.nrw.commons.bookmarks.locations - -// We can get uri using java.Net.Uri, but android implementation is faster -// (but it's forgiving with handling exceptions though) -import android.content.ContentValues -import android.database.Cursor -import android.database.sqlite.SQLiteQueryBuilder -import android.net.Uri -import fr.free.nrw.commons.BuildConfig -import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.COLUMN_NAME -import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.TABLE_NAME -import fr.free.nrw.commons.data.DBOpenHelper -import fr.free.nrw.commons.di.CommonsDaggerContentProvider -import timber.log.Timber -import javax.inject.Inject - - -/** - * Handles private storage for Bookmark locations - */ -class BookmarkLocationsContentProvider : CommonsDaggerContentProvider() { - - companion object { - private const val BASE_PATH = "bookmarksLocations" - val BASE_URI: Uri = - Uri.parse("content://${BuildConfig.BOOKMARK_LOCATIONS_AUTHORITY}/$BASE_PATH") - - /** - * Append bookmark locations name to the base URI. - */ - fun uriForName(name: String): Uri { - return Uri.parse("$BASE_URI/$name") - } - } - - @Inject - lateinit var dbOpenHelper: DBOpenHelper - - override fun getType(uri: Uri): String? = null - - /** - * Queries the SQLite database for the bookmark locations. - */ - override fun query( - uri: Uri, - projection: Array?, - selection: String?, - selectionArgs: Array?, - sortOrder: String? - ): Cursor { - val queryBuilder = SQLiteQueryBuilder().apply { - tables = TABLE_NAME - } - - val db = dbOpenHelper.readableDatabase - val cursor = queryBuilder.query( - db, - projection, - selection, - selectionArgs, - null, - null, - sortOrder - ) - cursor.setNotificationUri(context?.contentResolver, uri) - return cursor - } - - /** - * Handles the update query of local SQLite database. - */ - override fun update( - uri: Uri, - contentValues: ContentValues?, - selection: String?, - selectionArgs: Array? - ): Int { - val db = dbOpenHelper.writableDatabase - val rowsUpdated: Int - - if (selection.isNullOrEmpty()) { - val id = uri.lastPathSegment?.toIntOrNull() - ?: throw IllegalArgumentException("Invalid ID in URI") - rowsUpdated = db.update( - TABLE_NAME, - contentValues, - "$COLUMN_NAME = ?", - arrayOf(id.toString()) - ) - } else { - throw IllegalArgumentException( - "Parameter `selection` should be empty when updating an ID" - ) - } - - context?.contentResolver?.notifyChange(uri, null) - return rowsUpdated - } - - /** - * Handles the insertion of a new bookmark locations record to the local SQLite database. - */ - override fun insert(uri: Uri, contentValues: ContentValues?): Uri { - val db = dbOpenHelper.writableDatabase - val id = db.insert(TABLE_NAME, null, contentValues) - context?.contentResolver?.notifyChange(uri, null) - return Uri.parse("$BASE_URI/$id") - } - - /** - * Handles the deletion of bookmark locations from the local SQLite database. - */ - override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int { - val db = dbOpenHelper.readableDatabase - Timber.d("Deleting bookmark name %s", uri.lastPathSegment) - - val rows = db.delete( - TABLE_NAME, - "location_name = ?", - arrayOf(uri.lastPathSegment) - ) - - context?.contentResolver?.notifyChange(uri, null) - return rows - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsController.kt b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsController.kt index ad059c414..3509f4e73 100644 --- a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsController.kt +++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsController.kt @@ -13,5 +13,5 @@ class BookmarkLocationsController @Inject constructor( * Load bookmarked locations from the database. * @return a list of Place objects. */ - fun loadFavoritesLocations(): List = bookmarkLocationDao.getAllBookmarksLocations() + fun loadFavoritesLocations(): List = listOf() } diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsDao.kt b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsDao.kt index eb86909c8..b7096fd64 100644 --- a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsDao.kt +++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsDao.kt @@ -1,286 +1,49 @@ package fr.free.nrw.commons.bookmarks.locations - -import android.annotation.SuppressLint -import android.content.ContentProviderClient -import android.content.ContentValues -import android.database.Cursor -import android.database.sqlite.SQLiteDatabase -import android.database.sqlite.SQLiteException -import android.os.RemoteException -import androidx.annotation.NonNull -import fr.free.nrw.commons.location.LatLng -import fr.free.nrw.commons.nearby.Label +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query import fr.free.nrw.commons.nearby.NearbyController import fr.free.nrw.commons.nearby.Place -import fr.free.nrw.commons.nearby.Sitelinks -import timber.log.Timber -import javax.inject.Inject -import javax.inject.Named -import javax.inject.Provider +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +@Dao +abstract class BookmarkLocationsDao { -class BookmarkLocationsDao @Inject constructor( - @Named("bookmarksLocation") private val clientProvider: Provider -) { + @Insert(onConflict = OnConflictStrategy.REPLACE) + abstract suspend fun addBookmarkLocation(bookmarkLocation: BookmarksLocations) - /** - * Find all persisted location bookmarks in the database - * @return list of Place - */ - fun getAllBookmarksLocations(): List { - val items = mutableListOf() - var cursor: Cursor? = null - val db = clientProvider.get() - try { - cursor = db.query( - BookmarkLocationsContentProvider.BASE_URI, - Table.ALL_FIELDS, - null, - emptyArray(), - null + @Query("SELECT * FROM bookmarks_locations") + abstract suspend fun getAllBookmarksLocations(): List + + @Query("SELECT EXISTS (SELECT 1 FROM bookmarks_locations WHERE location_name = :name)") + abstract suspend fun findBookmarkLocation(name: String): Boolean + + @Delete + abstract suspend fun deleteBookmarkLocation(bookmarkLocation: BookmarksLocations) + + suspend fun updateBookmarkLocation(bookmarkLocation: Place): Boolean { + val bookmarkLocationExists = findBookmarkLocation(bookmarkLocation.name) + + if(bookmarkLocationExists) { + deleteBookmarkLocation( + bookmarkLocation.toBookmarksLocations() ) - cursor?.let { - while (it.moveToNext()) { - items.add(fromCursor(it)) - } - } - } catch (e: RemoteException) { - throw RuntimeException(e) - } finally { - cursor?.close() - db.release() - } - return items - } - - /** - * Look for a place in bookmarks table in order to insert or delete it - * @param bookmarkLocation : Place object - * @return boolean : is Place now fav ? - */ - fun updateBookmarkLocation(bookmarkLocation: Place): Boolean { - val bookmarkExists = findBookmarkLocation(bookmarkLocation) - if (bookmarkExists) { - deleteBookmarkLocation(bookmarkLocation) NearbyController.updateMarkerLabelListBookmark(bookmarkLocation, false) } else { - addBookmarkLocation(bookmarkLocation) + addBookmarkLocation( + bookmarkLocation.toBookmarksLocations() + ) NearbyController.updateMarkerLabelListBookmark(bookmarkLocation, true) } - return !bookmarkExists + + return !bookmarkLocationExists } - /** - * Add a Place to bookmarks table - * @param bookmarkLocation : Place to add - */ - private fun addBookmarkLocation(bookmarkLocation: Place) { - val db = clientProvider.get() - try { - db.insert(BookmarkLocationsContentProvider.BASE_URI, toContentValues(bookmarkLocation)) - } catch (e: RemoteException) { - throw RuntimeException(e) - } finally { - db.release() - } + fun getAllBookmarksLocationsPlace(): Flow> { + return flow { getAllBookmarksLocations().map { it.toPlace() } } } - - /** - * Delete a Place from bookmarks table - * @param bookmarkLocation : Place to delete - */ - private fun deleteBookmarkLocation(bookmarkLocation: Place) { - val db = clientProvider.get() - try { - db.delete( - BookmarkLocationsContentProvider.uriForName(bookmarkLocation.name), - null, - null - ) - } catch (e: RemoteException) { - throw RuntimeException(e) - } finally { - db.release() - } - } - - /** - * Find a Place from database based on its name - * @param bookmarkLocation : Place to find - * @return boolean : is Place in database ? - */ - fun findBookmarkLocation(bookmarkLocation: Place): Boolean { - var cursor: Cursor? = null - val db = clientProvider.get() - try { - cursor = db.query( - BookmarkLocationsContentProvider.BASE_URI, - Table.ALL_FIELDS, - "${Table.COLUMN_NAME}=?", - arrayOf(bookmarkLocation.name), - null - ) - return cursor?.moveToFirst() == true - } catch (e: RemoteException) { - throw RuntimeException(e) - } finally { - cursor?.close() - db.release() - } - } - - @SuppressLint("Range") - @NonNull - fun fromCursor(cursor: Cursor): Place { - val location = LatLng( - cursor.getDouble(cursor.getColumnIndex(Table.COLUMN_LAT)), - cursor.getDouble(cursor.getColumnIndex(Table.COLUMN_LONG)), - 1F - ) - - val builder = Sitelinks.Builder().apply { - setWikipediaLink(cursor.getString(cursor.getColumnIndex(Table.COLUMN_WIKIPEDIA_LINK))) - setWikidataLink(cursor.getString(cursor.getColumnIndex(Table.COLUMN_WIKIDATA_LINK))) - setCommonsLink(cursor.getString(cursor.getColumnIndex(Table.COLUMN_COMMONS_LINK))) - } - - return Place( - cursor.getString(cursor.getColumnIndex(Table.COLUMN_LANGUAGE)), - cursor.getString(cursor.getColumnIndex(Table.COLUMN_NAME)), - Label.fromText(cursor.getString(cursor.getColumnIndex(Table.COLUMN_LABEL_TEXT))), - cursor.getString(cursor.getColumnIndex(Table.COLUMN_DESCRIPTION)), - location, - cursor.getString(cursor.getColumnIndex(Table.COLUMN_CATEGORY)), - builder.build(), - cursor.getString(cursor.getColumnIndex(Table.COLUMN_PIC)), - cursor.getString(cursor.getColumnIndex(Table.COLUMN_EXISTS))?.toBoolean() ?: false - ) - } - - private fun toContentValues(bookmarkLocation: Place): ContentValues { - return ContentValues().apply { - put(Table.COLUMN_NAME, bookmarkLocation.name) - put(Table.COLUMN_LANGUAGE, bookmarkLocation.language) - put(Table.COLUMN_DESCRIPTION, bookmarkLocation.longDescription) - put(Table.COLUMN_CATEGORY, bookmarkLocation.category) - put(Table.COLUMN_LABEL_TEXT, bookmarkLocation.label?.text ?: "") - put(Table.COLUMN_LABEL_ICON, bookmarkLocation.label?.icon) - put(Table.COLUMN_WIKIPEDIA_LINK, bookmarkLocation.siteLinks.wikipediaLink.toString()) - put(Table.COLUMN_WIKIDATA_LINK, bookmarkLocation.siteLinks.wikidataLink.toString()) - put(Table.COLUMN_COMMONS_LINK, bookmarkLocation.siteLinks.commonsLink.toString()) - put(Table.COLUMN_LAT, bookmarkLocation.location.latitude) - put(Table.COLUMN_LONG, bookmarkLocation.location.longitude) - put(Table.COLUMN_PIC, bookmarkLocation.pic) - put(Table.COLUMN_EXISTS, bookmarkLocation.exists.toString()) - } - } - - object Table { - const val TABLE_NAME = "bookmarksLocations" - - const val COLUMN_NAME = "location_name" - const val COLUMN_LANGUAGE = "location_language" - const val COLUMN_DESCRIPTION = "location_description" - const val COLUMN_LAT = "location_lat" - const val COLUMN_LONG = "location_long" - const val COLUMN_CATEGORY = "location_category" - const val COLUMN_LABEL_TEXT = "location_label_text" - const val COLUMN_LABEL_ICON = "location_label_icon" - const val COLUMN_IMAGE_URL = "location_image_url" - const val COLUMN_WIKIPEDIA_LINK = "location_wikipedia_link" - const val COLUMN_WIKIDATA_LINK = "location_wikidata_link" - const val COLUMN_COMMONS_LINK = "location_commons_link" - const val COLUMN_PIC = "location_pic" - const val COLUMN_EXISTS = "location_exists" - - // NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES. - val ALL_FIELDS = arrayOf( - COLUMN_NAME, COLUMN_LANGUAGE, COLUMN_DESCRIPTION, COLUMN_CATEGORY, COLUMN_LABEL_TEXT, COLUMN_LABEL_ICON, - COLUMN_LAT, COLUMN_LONG, COLUMN_IMAGE_URL, COLUMN_WIKIPEDIA_LINK, COLUMN_WIKIDATA_LINK, COLUMN_COMMONS_LINK, - COLUMN_PIC, COLUMN_EXISTS - ) - - const val DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS $TABLE_NAME" - - const val CREATE_TABLE_STATEMENT = """ - CREATE TABLE $TABLE_NAME ( - $COLUMN_NAME STRING PRIMARY KEY, - $COLUMN_LANGUAGE STRING, - $COLUMN_DESCRIPTION STRING, - $COLUMN_CATEGORY STRING, - $COLUMN_LABEL_TEXT STRING, - $COLUMN_LABEL_ICON INTEGER, - $COLUMN_LAT DOUBLE, - $COLUMN_LONG DOUBLE, - $COLUMN_IMAGE_URL STRING, - $COLUMN_WIKIPEDIA_LINK STRING, - $COLUMN_WIKIDATA_LINK STRING, - $COLUMN_COMMONS_LINK STRING, - $COLUMN_PIC STRING, - $COLUMN_EXISTS STRING - ); - """ - - fun onCreate(db: SQLiteDatabase) { - db.execSQL(CREATE_TABLE_STATEMENT) - } - - fun onDelete(db: SQLiteDatabase) { - db.execSQL(DROP_TABLE_STATEMENT) - onCreate(db) - } - - @SuppressLint("SQLiteString") - fun onUpdate(db: SQLiteDatabase, from: Int, to: Int) { - Timber.d("bookmarksLocations db is updated from:$from, to:$to") - var currFrom = from - - if (from == to) { - return - } - if (from < 7) { - onUpdate(db, ++currFrom, to) - return - } - if (from == 7) { - onCreate(db) - onUpdate(db, ++currFrom, to) - return - } - if (from < 10) { - onUpdate(db, ++currFrom, to) - return - } - if (from == 10) { - try { - db.execSQL("ALTER TABLE $TABLE_NAME ADD COLUMN location_pic STRING;") - } catch (exception: SQLiteException) { - Timber.e(exception) - } - return - } - if (from >= 12) { - try { - db.execSQL("ALTER TABLE $TABLE_NAME ADD COLUMN location_destroyed STRING;") - } catch (exception: SQLiteException) { - Timber.e(exception) - } - } - if (from >= 13) { - try { - db.execSQL("ALTER TABLE $TABLE_NAME ADD COLUMN location_language STRING;") - } catch (exception: SQLiteException) { - Timber.e(exception) - } - } - if (from >= 14) { - try { - db.execSQL("ALTER TABLE $TABLE_NAME ADD COLUMN location_exists STRING;") - } catch (exception: SQLiteException) { - Timber.e(exception) - } - } - } - } -} +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.kt b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.kt index 228ec2599..00538c572 100644 --- a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.kt @@ -8,6 +8,7 @@ import android.view.ViewGroup import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import dagger.android.support.DaggerFragment import fr.free.nrw.commons.R @@ -108,6 +109,7 @@ class BookmarkLocationsFragment : DaggerFragment() { adapter = PlaceAdapter( bookmarkLocationDao, + scope = lifecycleScope, { }, { place, _ -> adapter.remove(place) diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsViewModel.kt b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsViewModel.kt new file mode 100644 index 000000000..2ee924423 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsViewModel.kt @@ -0,0 +1,15 @@ +package fr.free.nrw.commons.bookmarks.locations + +import androidx.lifecycle.ViewModel +import fr.free.nrw.commons.nearby.Place +import kotlinx.coroutines.flow.Flow + +class BookmarkLocationsViewModel( + private val bookmarkLocationsDao: BookmarkLocationsDao +): ViewModel() { + + fun getAllBookmarkLocations(): Flow> { + return bookmarkLocationsDao.getAllBookmarksLocationsPlace() + } + +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarksLocations.kt b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarksLocations.kt new file mode 100644 index 000000000..66d670169 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarksLocations.kt @@ -0,0 +1,72 @@ +package fr.free.nrw.commons.bookmarks.locations + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import fr.free.nrw.commons.location.LatLng +import fr.free.nrw.commons.nearby.Label +import fr.free.nrw.commons.nearby.Place +import fr.free.nrw.commons.nearby.Sitelinks + +@Entity(tableName = "bookmarks_locations") +data class BookmarksLocations( + @PrimaryKey @ColumnInfo(name = "location_name") val locationName: String, + @ColumnInfo(name = "location_language") val locationLanguage: String, + @ColumnInfo(name = "location_description") val locationDescription: String, + @ColumnInfo(name = "location_lat") val locationLat: Double, + @ColumnInfo(name = "location_long") val locationLong: Double, + @ColumnInfo(name = "location_category") val locationCategory: String, + @ColumnInfo(name = "location_label_text") val locationLabelText: String, + @ColumnInfo(name = "location_label_icon") val locationLabelIcon: Int?, + @ColumnInfo(name = "location_image_url") val locationImageUrl: String, + @ColumnInfo(name = "location_wikipedia_link") val locationWikipediaLink: String, + @ColumnInfo(name = "location_wikidata_link") val locationWikidataLink: String, + @ColumnInfo(name = "location_commons_link") val locationCommonsLink: String, + @ColumnInfo(name = "location_pic") val locationPic: String, + @ColumnInfo(name = "location_exists") val locationExists: Boolean +) + +fun BookmarksLocations.toPlace(): Place { + val location = LatLng( + locationLat, + locationLong, + 1F + ) + + val builder = Sitelinks.Builder().apply { + setWikipediaLink(locationWikipediaLink) + setWikidataLink(locationWikidataLink) + setCommonsLink(locationCommonsLink) + } + + return Place( + locationLanguage, + locationName, + Label.fromText(locationLabelText), + locationDescription, + location, + locationCategory, + builder.build(), + locationPic, + locationExists + ) +} + +fun Place.toBookmarksLocations(): BookmarksLocations { + return BookmarksLocations( + locationName = name, + locationLanguage = language, + locationDescription = longDescription, + locationCategory = category, + locationLat = location.latitude, + locationLong = location.longitude, + locationLabelText = label?.text ?: "", + locationLabelIcon = label?.icon, + locationImageUrl = pic, + locationWikipediaLink = siteLinks.wikipediaLink.toString(), + locationWikidataLink = siteLinks.wikidataLink.toString(), + locationCommonsLink = siteLinks.commonsLink.toString(), + locationPic = pic, + locationExists = exists + ) +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.kt b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.kt index 1377ae281..660fd2a4a 100644 --- a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.kt +++ b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.kt @@ -30,7 +30,6 @@ class DBOpenHelper( override fun onCreate(db: SQLiteDatabase) { CategoryDao.Table.onCreate(db) BookmarkPicturesDao.Table.onCreate(db) - BookmarkLocationsDao.Table.onCreate(db) BookmarkItemsDao.Table.onCreate(db) RecentSearchesDao.Table.onCreate(db) RecentLanguagesDao.Table.onCreate(db) @@ -39,7 +38,6 @@ class DBOpenHelper( override fun onUpgrade(db: SQLiteDatabase, from: Int, to: Int) { CategoryDao.Table.onUpdate(db, from, to) BookmarkPicturesDao.Table.onUpdate(db, from, to) - BookmarkLocationsDao.Table.onUpdate(db, from, to) BookmarkItemsDao.Table.onUpdate(db, from, to) RecentSearchesDao.Table.onUpdate(db, from, to) RecentLanguagesDao.Table.onUpdate(db, from, to) diff --git a/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt index 0c34bbdec..ee4da8ee7 100644 --- a/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt +++ b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt @@ -5,6 +5,8 @@ import androidx.room.RoomDatabase import androidx.room.TypeConverters import fr.free.nrw.commons.bookmarks.category.BookmarkCategoriesDao import fr.free.nrw.commons.bookmarks.category.BookmarksCategoryModal +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao +import fr.free.nrw.commons.bookmarks.locations.BookmarksLocations import fr.free.nrw.commons.contributions.Contribution import fr.free.nrw.commons.contributions.ContributionDao import fr.free.nrw.commons.customselector.database.NotForUploadStatus @@ -23,7 +25,7 @@ import fr.free.nrw.commons.upload.depicts.DepictsDao * */ @Database( - entities = [Contribution::class, Depicts::class, UploadedStatus::class, NotForUploadStatus::class, ReviewEntity::class, Place::class, BookmarksCategoryModal::class], + entities = [Contribution::class, Depicts::class, UploadedStatus::class, NotForUploadStatus::class, ReviewEntity::class, Place::class, BookmarksCategoryModal::class, BookmarksLocations::class], version = 19, exportSchema = false, ) @@ -42,4 +44,6 @@ abstract class AppDatabase : RoomDatabase() { abstract fun ReviewDao(): ReviewDao abstract fun bookmarkCategoriesDao(): BookmarkCategoriesDao + + abstract fun bookmarkLocationsDao(): BookmarkLocationsDao } diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.kt b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.kt index 58d9039d5..324c8e1e5 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.kt +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.kt @@ -16,6 +16,7 @@ import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.R import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.bookmarks.category.BookmarkCategoriesDao +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao import fr.free.nrw.commons.contributions.ContributionDao import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao import fr.free.nrw.commons.customselector.database.UploadedStatusDao @@ -206,6 +207,10 @@ open class CommonsApplicationModule(private val applicationContext: Context) { fun providesPlaceDao(appDatabase: AppDatabase): PlaceDao = appDatabase.PlaceDao() + @Provides + fun providesBookmarkLocationsDao(appDatabase: AppDatabase): BookmarkLocationsDao = + appDatabase.bookmarkLocationsDao() + @Provides fun providesDepictDao(appDatabase: AppDatabase): DepictsDao = appDatabase.DepictsDao() diff --git a/app/src/main/java/fr/free/nrw/commons/di/ContentProviderBuilderModule.kt b/app/src/main/java/fr/free/nrw/commons/di/ContentProviderBuilderModule.kt index 1882f77a9..80a0626cc 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/ContentProviderBuilderModule.kt +++ b/app/src/main/java/fr/free/nrw/commons/di/ContentProviderBuilderModule.kt @@ -3,7 +3,6 @@ package fr.free.nrw.commons.di import dagger.Module import dagger.android.ContributesAndroidInjector import fr.free.nrw.commons.bookmarks.items.BookmarkItemsContentProvider -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.explore.recentsearches.RecentSearchesContentProvider @@ -26,9 +25,6 @@ abstract class ContentProviderBuilderModule { @ContributesAndroidInjector abstract fun bindBookmarkContentProvider(): BookmarkPicturesContentProvider - @ContributesAndroidInjector - abstract fun bindBookmarkLocationContentProvider(): BookmarkLocationsContentProvider - @ContributesAndroidInjector abstract fun bindBookmarkItemContentProvider(): BookmarkItemsContentProvider diff --git a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.kt b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.kt index 0ef34e355..9920fef9f 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.kt +++ b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.kt @@ -101,6 +101,9 @@ abstract class FragmentBuilderModule { @ContributesAndroidInjector abstract fun bindBookmarkCategoriesListFragment(): BookmarkCategoriesFragment + @ContributesAndroidInjector + abstract fun bindBookmarkLocationsListFragment(): BookmarkLocationsFragment + @ContributesAndroidInjector abstract fun bindReviewOutOfContextFragment(): ReviewImageFragment diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyUtil.kt b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyUtil.kt new file mode 100644 index 000000000..d92796101 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyUtil.kt @@ -0,0 +1,22 @@ +package fr.free.nrw.commons.nearby + +import androidx.lifecycle.LifecycleCoroutineScope +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao +import fr.free.nrw.commons.bookmarks.locations.BookmarksLocations +import kotlinx.coroutines.launch + +object NearbyUtil { + + fun getBookmarkLocationExists( + bookmarksLocationsDao: BookmarkLocationsDao, + name: String, + scope: LifecycleCoroutineScope? + ): Boolean { + var isBookmarked = false + scope?.launch { + isBookmarked = bookmarksLocationsDao.findBookmarkLocation(name) + } + + return isBookmarked + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/PlaceAdapterDelegate.kt b/app/src/main/java/fr/free/nrw/commons/nearby/PlaceAdapterDelegate.kt index 7156568b6..d9a76c25d 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/PlaceAdapterDelegate.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/PlaceAdapterDelegate.kt @@ -7,6 +7,7 @@ import android.view.View.INVISIBLE import android.view.View.VISIBLE import android.widget.RelativeLayout import androidx.activity.result.ActivityResultLauncher +import androidx.lifecycle.LifecycleCoroutineScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.transition.TransitionManager @@ -16,9 +17,11 @@ import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import fr.free.nrw.commons.R import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao import fr.free.nrw.commons.databinding.ItemPlaceBinding +import kotlinx.coroutines.launch fun placeAdapterDelegate( bookmarkLocationDao: BookmarkLocationsDao, + scope: LifecycleCoroutineScope?, onItemClick: ((Place) -> Unit)? = null, onCameraClicked: (Place, ActivityResultLauncher>, ActivityResultLauncher) -> Unit, onCameraLongPressed: () -> Boolean, @@ -61,7 +64,10 @@ fun placeAdapterDelegate( nearbyButtonLayout.galleryButton.setOnClickListener { onGalleryClicked(item, galleryPickLauncherForResult) } nearbyButtonLayout.galleryButton.setOnLongClickListener { onGalleryLongPressed() } bookmarkButtonImage.setOnClickListener { - val isBookmarked = bookmarkLocationDao.updateBookmarkLocation(item) + var isBookmarked = false + scope?.launch { + isBookmarked = bookmarkLocationDao.updateBookmarkLocation(item) + } bookmarkButtonImage.setImageResource( if (isBookmarked) R.drawable.ic_round_star_filled_24px else R.drawable.ic_round_star_border_24px, ) @@ -93,13 +99,15 @@ fun placeAdapterDelegate( GONE } - bookmarkButtonImage.setImageResource( - if (bookmarkLocationDao.findBookmarkLocation(item)) { - R.drawable.ic_round_star_filled_24px - } else { - R.drawable.ic_round_star_border_24px - }, - ) + scope?.launch { + bookmarkButtonImage.setImageResource( + if (bookmarkLocationDao.findBookmarkLocation(item.name)) { + R.drawable.ic_round_star_filled_24px + } else { + R.drawable.ic_round_star_border_24px + }, + ) + } } nearbyButtonLayout.directionsButton.setOnLongClickListener { onDirectionsLongPressed() } } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/contract/NearbyParentFragmentContract.java b/app/src/main/java/fr/free/nrw/commons/nearby/contract/NearbyParentFragmentContract.java index e46e95353..1d59fcd34 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/contract/NearbyParentFragmentContract.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/contract/NearbyParentFragmentContract.java @@ -134,7 +134,7 @@ public interface NearbyParentFragmentContract { void setAdvancedQuery(String query); - void toggleBookmarkedStatus(Place place); + void toggleBookmarkedStatus(Place place, LifecycleCoroutineScope scope); void handleMapScrolled(LifecycleCoroutineScope scope, boolean isNetworkAvailable); } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/PlaceAdapter.kt b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/PlaceAdapter.kt index e5cc92667..adb687014 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/PlaceAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/PlaceAdapter.kt @@ -2,6 +2,7 @@ package fr.free.nrw.commons.nearby.fragments import android.content.Intent import androidx.activity.result.ActivityResultLauncher +import androidx.lifecycle.LifecycleCoroutineScope import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.nearby.placeAdapterDelegate @@ -9,6 +10,7 @@ import fr.free.nrw.commons.upload.categories.BaseDelegateAdapter class PlaceAdapter( bookmarkLocationsDao: BookmarkLocationsDao, + scope: LifecycleCoroutineScope? = null, onPlaceClicked: ((Place) -> Unit)? = null, onBookmarkClicked: (Place, Boolean) -> Unit, commonPlaceClickActions: CommonPlaceClickActions, @@ -18,6 +20,7 @@ class PlaceAdapter( ) : BaseDelegateAdapter( placeAdapterDelegate( bookmarkLocationsDao, + scope, onPlaceClicked, commonPlaceClickActions.onCameraClicked(), commonPlaceClickActions.onCameraLongPressed(), diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/presenter/NearbyParentFragmentPresenter.kt b/app/src/main/java/fr/free/nrw/commons/nearby/presenter/NearbyParentFragmentPresenter.kt index ed84751b0..585755d1e 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/presenter/NearbyParentFragmentPresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/presenter/NearbyParentFragmentPresenter.kt @@ -27,6 +27,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.ensureActive import kotlinx.coroutines.job import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import okhttp3.internal.wait import timber.log.Timber @@ -136,16 +137,25 @@ class NearbyParentFragmentPresenter * @param place The place whose bookmarked status is to be toggled. If the place is `null`, * the operation is skipped. */ - override fun toggleBookmarkedStatus(place: Place?) { + override fun toggleBookmarkedStatus( + place: Place?, + scope: LifecycleCoroutineScope? + ) { if (place == null) return - val nowBookmarked = bookmarkLocationDao.updateBookmarkLocation(place) + var nowBookmarked: Boolean? = null + scope?.launch { + nowBookmarked = bookmarkLocationDao.updateBookmarkLocation(place) + + } bookmarkChangedPlaces.add(place) val placeIndex = NearbyController.markerLabelList.indexOfFirst { it.place.location == place.location } - NearbyController.markerLabelList[placeIndex] = MarkerPlaceGroup( - nowBookmarked, - NearbyController.markerLabelList[placeIndex].place - ) + NearbyController.markerLabelList[placeIndex] = nowBookmarked?.let { + MarkerPlaceGroup( + it, + NearbyController.markerLabelList[placeIndex].place + ) + } nearbyParentFragmentView.setFilterState() } @@ -337,7 +347,7 @@ class NearbyParentFragmentPresenter for (i in 0..updatedGroups.lastIndex) { val repoPlace = placesRepository.fetchPlace(updatedGroups[i].place.entityID) if (repoPlace != null && repoPlace.name != null && repoPlace.name != ""){ - updatedGroups[i].isBookmarked = bookmarkLocationDao.findBookmarkLocation(repoPlace) + updatedGroups[i].isBookmarked = bookmarkLocationDao.findBookmarkLocation(repoPlace.name) updatedGroups[i].place.apply { name = repoPlace.name isMonument = repoPlace.isMonument @@ -375,7 +385,7 @@ class NearbyParentFragmentPresenter collectResults.send( fetchedPlaces.mapIndexed { index, place -> Pair(indices[index], MarkerPlaceGroup( - bookmarkLocationDao.findBookmarkLocation(place), + bookmarkLocationDao.findBookmarkLocation(place.name), place )) } @@ -457,7 +467,7 @@ class NearbyParentFragmentPresenter if (bookmarkChangedPlacesBacklog.containsKey(group.place.location)) { updatedGroups[index] = MarkerPlaceGroup( bookmarkLocationDao - .findBookmarkLocation(updatedGroups[index].place), + .findBookmarkLocation(updatedGroups[index].place.name), updatedGroups[index].place ) } @@ -565,7 +575,7 @@ class NearbyParentFragmentPresenter ).sortedBy { it.getDistanceInDouble(mapFocus) }.take(NearbyController.MAX_RESULTS) .map { MarkerPlaceGroup( - bookmarkLocationDao.findBookmarkLocation(it), it + bookmarkLocationDao.findBookmarkLocation(it.name), it ) } ensureActive()