Skip to content
Snippets Groups Projects
Commit 5091421c authored by Geoffrey Métais's avatar Geoffrey Métais
Browse files

Setup local browser favorites

parent ec197537
No related branches found
No related tags found
No related merge requests found
......@@ -20,6 +20,7 @@
package org.videolan.vlc.database
import android.arch.persistence.db.SupportSQLiteDatabase
import android.arch.persistence.room.Database
import android.arch.persistence.room.Room
import android.arch.persistence.room.RoomDatabase
......@@ -55,6 +56,11 @@ abstract class MediaDatabase: RoomDatabase() {
migration_17_18, migration_18_19, migration_19_20, migration_20_21,
migration_21_22, migration_22_23, migration_23_24, migration_24_25,
migration_25_26, migration_26_27)
.addCallback(object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
populateDB(context)
}
})
.build()
}
}
......
......@@ -22,7 +22,11 @@ package org.videolan.vlc.database
import android.arch.persistence.db.SupportSQLiteDatabase
import android.arch.persistence.room.migration.Migration
import org.videolan.vlc.util.Constants
import android.content.Context
import kotlinx.coroutines.experimental.launch
import org.videolan.vlc.repository.BrowserFavRepository
import org.videolan.vlc.util.AndroidDevices
import org.videolan.vlc.util.VLCIO
private const val DIR_TABLE_NAME = "directories_table"
private const val MEDIA_TABLE_NAME = "media_table"
......@@ -161,14 +165,24 @@ val migration_26_27 = object:Migration(26, 27) {
override fun migrate(database: SupportSQLiteDatabase) {
dropUnnecessaryTables(database)
val SLAVES_TABLE_NAME_TEMP = "${SLAVES_TABLE_NAME}_TEMP"
val slavesTableNameTemp = "${SLAVES_TABLE_NAME}_TEMP"
database.execSQL("UPDATE $SLAVES_TABLE_NAME SET slave_priority=2 WHERE slave_priority IS NULL;")
database.execSQL("CREATE TABLE IF NOT EXISTS $SLAVES_TABLE_NAME_TEMP ( slave_media_mrl TEXT PRIMARY KEY NOT NULL, slave_type INTEGER NOT NULL, slave_priority INTEGER NOT NULL, slave_uri TEXT NOT NULL);")
database.execSQL("INSERT INTO $SLAVES_TABLE_NAME_TEMP(slave_media_mrl, slave_type, slave_priority, slave_uri) SELECT slave_media_mrl, slave_type, slave_priority, slave_uri FROM $SLAVES_TABLE_NAME")
database.execSQL("CREATE TABLE IF NOT EXISTS $slavesTableNameTemp ( slave_media_mrl TEXT PRIMARY KEY NOT NULL, slave_type INTEGER NOT NULL, slave_priority INTEGER NOT NULL, slave_uri TEXT NOT NULL);")
database.execSQL("INSERT INTO $slavesTableNameTemp(slave_media_mrl, slave_type, slave_priority, slave_uri) SELECT slave_media_mrl, slave_type, slave_priority, slave_uri FROM $SLAVES_TABLE_NAME")
database.execSQL("DROP TABLE $SLAVES_TABLE_NAME")
database.execSQL("ALTER TABLE $SLAVES_TABLE_NAME_TEMP RENAME TO $SLAVES_TABLE_NAME")
database.execSQL("ALTER TABLE $slavesTableNameTemp RENAME TO $SLAVES_TABLE_NAME")
// Add a type column and set its value to 0 (till this version all favs were network favs)
database.execSQL("ALTER TABLE $FAV_TABLE_NAME ADD COLUMN type INTEGER NOT NULL DEFAULT 0;")
}
}
fun populateDB(context: Context) = launch(VLCIO) {
val favRepo = BrowserFavRepository(context)
val uris = listOf(AndroidDevices.MediaFolders.EXTERNAL_PUBLIC_MOVIES_DIRECTORY_URI,
AndroidDevices.MediaFolders.EXTERNAL_PUBLIC_MUSIC_DIRECTORY_URI,
AndroidDevices.MediaFolders.EXTERNAL_PUBLIC_PODCAST_DIRECTORY_URI,
AndroidDevices.MediaFolders.EXTERNAL_PUBLIC_DOWNLOAD_DIRECTORY_URI,
AndroidDevices.MediaFolders.WHATSAPP_VIDEOS_FILE_URI)
for (uri in uris) favRepo.addLocalFavItem(uri, uri.lastPathSegment)
}
\ No newline at end of file
......@@ -33,7 +33,9 @@ import org.videolan.medialibrary.media.MediaWrapper
import org.videolan.vlc.ExternalMonitor
import org.videolan.vlc.R
import org.videolan.vlc.VLCApplication
import org.videolan.vlc.database.models.BrowserFav
import org.videolan.vlc.gui.helpers.hf.getDocumentFiles
import org.videolan.vlc.repository.BrowserFavRepository
import org.videolan.vlc.util.*
import java.io.File
......@@ -41,14 +43,38 @@ open class FileBrowserProvider(dataset: LiveDataset<MediaLibraryItem>, url: Stri
private var storagePosition = -1
private var otgPosition = -1
private val favorites = if (url == null && !filePicker) BrowserFavRepository(VLCApplication.getAppContext()).localFavorites else null
private val favoritesObserver by lazy { Observer<List<BrowserFav>> {
val favs = convertFavorites(it)
val data = dataset.value.toMutableList()
if (data.size > 1) {
data.listIterator(1).run {
while (hasNext()) {
val item = next()
if (item.hasStateFlags(MediaLibraryItem.FLAG_FAVORITE) || item is DummyItem) remove()
}
}
}
if (favs.isNotEmpty()) {
val quickAccess = VLCApplication.getAppResources().getString(R.string.browser_quick_access)
data.add(DummyItem(quickAccess))
for (fav in favs) if (File(fav.uri.path).exists()) data.add(fav)
}
dataset.value = data
uiJob(false) { parseSubDirectories() }
} }
init {
if (url == null) ExternalMonitor.devices.observeForever(this)
if (url == null) {
ExternalMonitor.devices.observeForever(this)
favorites?.observeForever(favoritesObserver)
}
}
override fun browseRoot() {
val internalmemoryTitle = VLCApplication.getAppResources().getString(R.string.internal_memory)
val browserStorage = VLCApplication.getAppResources().getString(R.string.browser_storages)
val quickAccess = VLCApplication.getAppResources().getString(R.string.browser_quick_access)
val storages = AndroidDevices.getMediaDirectories()
val devices = mutableListOf<MediaLibraryItem>()
if (!filePicker) devices.add(DummyItem(browserStorage))
......@@ -73,37 +99,6 @@ open class FileBrowserProvider(dataset: LiveDataset<MediaLibraryItem>, url: Stri
otgPosition = devices.size
devices.add(otg)
}
// Set folders shortcuts
if (!filePicker) {
devices.add(DummyItem(quickAccess))
}
if (AndroidDevices.MediaFolders.EXTERNAL_PUBLIC_MOVIES_DIRECTORY_FILE.exists()) {
val movies = MediaWrapper(AndroidDevices.MediaFolders.EXTERNAL_PUBLIC_MOVIES_DIRECTORY_URI)
movies.type = MediaWrapper.TYPE_DIR
devices.add(movies)
}
if (!filePicker) {
if (AndroidDevices.MediaFolders.EXTERNAL_PUBLIC_MUSIC_DIRECTORY_FILE.exists()) {
val music = MediaWrapper(AndroidDevices.MediaFolders.EXTERNAL_PUBLIC_MUSIC_DIRECTORY_URI)
music.type = MediaWrapper.TYPE_DIR
devices.add(music)
}
if (AndroidDevices.MediaFolders.EXTERNAL_PUBLIC_PODCAST_DIRECTORY_FILE.exists()) {
val podcasts = MediaWrapper(AndroidDevices.MediaFolders.EXTERNAL_PUBLIC_PODCAST_DIRECTORY_URI)
podcasts.type = MediaWrapper.TYPE_DIR
devices.add(podcasts)
}
if (AndroidDevices.MediaFolders.EXTERNAL_PUBLIC_DOWNLOAD_DIRECTORY_FILE.exists()) {
val downloads = MediaWrapper(AndroidDevices.MediaFolders.EXTERNAL_PUBLIC_DOWNLOAD_DIRECTORY_URI)
downloads.type = MediaWrapper.TYPE_DIR
devices.add(downloads)
}
if (AndroidDevices.MediaFolders.WHATSAPP_VIDEOS_FILE.exists()) {
val whatsapp = MediaWrapper(AndroidDevices.MediaFolders.WHATSAPP_VIDEOS_FILE_URI)
whatsapp.type = MediaWrapper.TYPE_DIR
devices.add(whatsapp)
}
}
dataset.value = devices
}
......@@ -119,7 +114,10 @@ open class FileBrowserProvider(dataset: LiveDataset<MediaLibraryItem>, url: Stri
override fun refresh() = true
override fun release(): Job {
if (url == null) ExternalMonitor.devices.removeObserver(this)
if (url == null) {
ExternalMonitor.devices.removeObserver(this)
favorites?.removeObserver(favoritesObserver)
}
return super.release()
}
......@@ -138,5 +136,4 @@ open class FileBrowserProvider(dataset: LiveDataset<MediaLibraryItem>, url: Stri
dataset.add(otgPosition, otg)
}
}
}
\ No newline at end of file
......@@ -20,15 +20,11 @@
package org.videolan.vlc.repository
import android.arch.lifecycle.LiveData
import android.arch.lifecycle.MediatorLiveData
import android.content.Context
import android.net.Uri
import android.support.annotation.WorkerThread
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext
import org.videolan.medialibrary.media.MediaLibraryItem
import org.videolan.medialibrary.media.MediaWrapper
import org.videolan.vlc.ExternalMonitor
import org.videolan.vlc.database.BrowserFavDao
......@@ -37,6 +33,7 @@ import org.videolan.vlc.database.models.BrowserFav
import org.videolan.vlc.util.Constants.TYPE_LOCAL_FAV
import org.videolan.vlc.util.Constants.TYPE_NETWORK_FAV
import org.videolan.vlc.util.VLCIO
import org.videolan.vlc.util.convertFavorites
import org.videolan.vlc.util.uiJob
import java.util.*
......@@ -49,61 +46,34 @@ class BrowserFavRepository @JvmOverloads constructor(context: Context,
private val networkFavs by lazy { browserFavDao.getAllNetwrokFavs() }
fun addNetworkFavItem(uri: Uri, title: String, iconUrl: String?): Job {
return launch(VLCIO) {
(browserFavDao.insert(BrowserFav(uri, TYPE_NETWORK_FAV, title, iconUrl)))
}
val browserFavorites by lazy { browserFavDao.getAll() }
val localFavorites by lazy { browserFavDao.getAllLocalFavs() }
fun addNetworkFavItem(uri: Uri, title: String, iconUrl: String?) = launch(VLCIO) {
browserFavDao.insert(BrowserFav(uri, TYPE_NETWORK_FAV, title, iconUrl))
}
fun addLocalFavItem(uri: Uri, title: String, iconUrl: String?): Job {
return launch(VLCIO) {
browserFavDao.insert(BrowserFav(uri, TYPE_LOCAL_FAV, title, iconUrl))
}
fun addLocalFavItem(uri: Uri, title: String, iconUrl: String? = null) = launch(VLCIO) {
browserFavDao.insert(BrowserFav(uri, TYPE_LOCAL_FAV, title, iconUrl))
}
val networkFavorites by lazy {
MediatorLiveData<List<MediaWrapper>>().apply {
addSource(networkFavs) { value = createMediaWrapperObjects(it).filterNetworkFavs() }
addSource(networkFavs) { value = convertFavorites(it).filterNetworkFavs() }
addSource(ExternalMonitor.connected) {
uiJob {
val favList = getCurrentFavorites(networkFavs)
val favList = convertFavorites(networkFavs.value)
if (favList.isNotEmpty()) value = if (it == true) favList.filterNetworkFavs() else emptyList()
}
}
}
}
val browserFavorites by lazy {
browserFavDao.getAll()
}
val localFavorites by lazy {
browserFavDao.getAllLocalFavs()
}
@WorkerThread
suspend fun getCurrentFavorites(favsType: LiveData<List<BrowserFav>>) = withContext(VLCIO) {
favsType.value?.let {
createMediaWrapperObjects(it)
} ?: emptyList()}
@WorkerThread
fun browserFavExists(uri: Uri): Boolean = browserFavDao.get(uri).isNotEmpty()
private fun createMediaWrapperObjects(allBrowserFavs: List<BrowserFav>?): List<MediaWrapper> {
return allBrowserFavs?.map { (uri, _, title, iconUrl) ->
MediaWrapper(uri).apply {
setDisplayTitle(Uri.decode(title))
type = MediaWrapper.TYPE_DIR
iconUrl?.let { artworkURL = Uri.decode(it) }
setStateFlags(MediaLibraryItem.FLAG_FAVORITE)
}
} ?: emptyList()
}
fun deleteBrowserFav(uri: Uri) {
browserFavDao.delete(uri)
}
fun deleteBrowserFav(uri: Uri) = browserFavDao.delete(uri)
private fun List<MediaWrapper>.filterNetworkFavs() : List<MediaWrapper> {
return when {
......
/*****************************************************************************
* browserutils.kt
*****************************************************************************
* Copyright © 2018 VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
*****************************************************************************
* Copyright © 2018 VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
package org.videolan.vlc.util
import android.net.Uri
import org.videolan.medialibrary.media.MediaLibraryItem
import org.videolan.medialibrary.media.MediaWrapper
import org.videolan.vlc.database.models.BrowserFav
fun isSchemeSupported(scheme: String?) = when(scheme) {
"file", "smb", "ssh", "nfs" -> true
else -> false
}
\ No newline at end of file
}
fun convertFavorites(browserFavs: List<BrowserFav>?) = browserFavs?.map { (uri, _, title, iconUrl) ->
MediaWrapper(uri).apply {
setDisplayTitle(Uri.decode(title))
type = MediaWrapper.TYPE_DIR
iconUrl?.let { artworkURL = Uri.decode(it) }
setStateFlags(MediaLibraryItem.FLAG_FAVORITE)
}
} ?: emptyList()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment