mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
Refactor: Migrate to Room Database for Bookmark Locations
This commit migrates the bookmark locations functionality from a custom content provider to Room database.
Key changes:
* **Removal of `BookmarkLocationsContentProvider`:** This class, which previously handled data storage, has been removed.
* **Introduction of `BookmarksLocations`:** This data class now represents a bookmarked location, serving as the Room entity.
* **Creation of `BookmarkLocationsDao`:** This Room DAO handles database interactions for bookmark locations, including:
* Adding, deleting, and querying bookmarked locations.
* Checking if a location is already bookmarked.
* Updating the bookmark status of a location.
* Retrieving all bookmarked locations as `Place` objects.
* **`BookmarkLocationsViewModel`:** Added to manage the data layer for bookmark locations
* **`NearbyUtil`:** Created a Util class for Nearby to manage the bookmark locations.
* **Updates in `PlaceAdapter` and `PlaceAdapterDelegate`:** These classes have been modified to work with the new Room-based data layer.
* **Updates in `AppDatabase`:** The database now includes `BookmarksLocations` as an entity and exposes the `bookmarkLocationsDao`.
* **Updates in `FragmentBuilderModule` and `CommonsApplicationModule`**: for DI
* **Removal of `DBOpenHelper` upgrade for locations**: as it is no longer needed
* **Updates in `NearbyParentFragmentPresenter`**: refactored the logic to use the dao functions
* **Updates in `NearbyParentFragment`**: refactored the logic to use the util and dao functions
* **Update in `BookmarkLocationsController`**: removed as its no longer needed
* **Add `toPlace` and `toBookmarksLocations`**: extension functions to map between data class and entities
* **Update in `CommonsApplication`**: to remove old db table.
This commit is contained in:
parent
f32c59034d
commit
7894f4a026
17 changed files with 199 additions and 425 deletions
|
|
@ -253,7 +253,6 @@ class CommonsApplication : MultiDexApplication() {
|
|||
Timber.e(e)
|
||||
}
|
||||
BookmarkPicturesDao.Table.onDelete(db)
|
||||
BookmarkLocationsDao.Table.onDelete(db)
|
||||
BookmarkItemsDao.Table.onDelete(db)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<String>?,
|
||||
selection: String?,
|
||||
selectionArgs: Array<String>?,
|
||||
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<String>?
|
||||
): 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<String>?): 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
|
||||
}
|
||||
}
|
||||
|
|
@ -13,5 +13,5 @@ class BookmarkLocationsController @Inject constructor(
|
|||
* Load bookmarked locations from the database.
|
||||
* @return a list of Place objects.
|
||||
*/
|
||||
fun loadFavoritesLocations(): List<Place> = bookmarkLocationDao.getAllBookmarksLocations()
|
||||
fun loadFavoritesLocations(): List<Place> = listOf()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ContentProviderClient>
|
||||
) {
|
||||
@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<Place> {
|
||||
val items = mutableListOf<Place>()
|
||||
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<BookmarksLocations>
|
||||
|
||||
@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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
fun getAllBookmarksLocationsPlace(): Flow<List<Place>> {
|
||||
return flow { getAllBookmarksLocations().map { it.toPlace() } }
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<List<Place>> {
|
||||
return bookmarkLocationsDao.getAllBookmarksLocationsPlace()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -101,6 +101,9 @@ abstract class FragmentBuilderModule {
|
|||
@ContributesAndroidInjector
|
||||
abstract fun bindBookmarkCategoriesListFragment(): BookmarkCategoriesFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindBookmarkLocationsListFragment(): BookmarkLocationsFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindReviewOutOfContextFragment(): ReviewImageFragment
|
||||
|
||||
|
|
|
|||
22
app/src/main/java/fr/free/nrw/commons/nearby/NearbyUtil.kt
Normal file
22
app/src/main/java/fr/free/nrw/commons/nearby/NearbyUtil.kt
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Array<String>>, ActivityResultLauncher<Intent>) -> 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() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Place>(
|
||||
placeAdapterDelegate(
|
||||
bookmarkLocationsDao,
|
||||
scope,
|
||||
onPlaceClicked,
|
||||
commonPlaceClickActions.onCameraClicked(),
|
||||
commonPlaceClickActions.onCameraLongPressed(),
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue